Quitada ブログ HAX

Hatena Blog でも Quitada ブログ

Linux ベースの C++ アプリケーションを Windows に移植

今日はお客様訪問もなくて、じっくりまったり怪社で作業。

基本、Java ラーな quitada の未開の地であった C++ の世界ですが、ちょっと前に Platform Symphony DE 4.0 付属アプリケーションをベースに Linux 上で動作するアプリケーションとビルド環境を構築してみました。で、一段落ついたので、今日はそれを Windows に、Platform Symphony 4.1 DE(以下、Symphony)のアプリケーションとして移植してみました。

# Platform Symphony DE は無料セミナをやってるみたいですよ。

ということで、メモ書きを…

とりあえず項目列挙ベースで。

#というか、あまり C++ 的でないんだけど…

1.Visual C++ Express にさわってみる

とりあえず、ターゲットバージョンは Visual C++ 2008 Express Edition。無料だし、32 ビットのコンソールアプリケーション作るならこれで十分。えっと、Symphony はまだ Visual C++ 2008 はサポートしてませんが、動きます。

1-a) インストール

マイクロソフトこことかみればわかります。Visual C++ 2005 Express Edition は、インストール後に別途 Platform SDK をインストールする必要があったり(これがまた厄介)してめんどうでしたが、2008 は Platform SDK も同時にインストールされて楽ちんプーです。

1-b) ソリューションとプロジェクト

Visual Studio のマニュアルとかきちんとみてないので使った感覚なんですが、1 プロジェクトに対し 1 つの exe なりライブラリなりのソースコードやビルドの情報をもっていて、それらのプロジェクトをまとめたものがソリューションのようです。

なので、まずはソリューションファイルをクリックしてやります。VC++ 2008 なので以下のようなアイコン(バージョン的には VC++ 9.0 なので)ですね。

#以前のバージョンの VC++ のソリューションファイルをクリックすると、最新のソリューションファイルへ変換するウィザードが起動します。

で、VC++ Express を開いて、ソースコードをみた感じがこれ。

製品版とちょっと見た目違いますね。ちょっとした差別化でしょうか?で、ソリューションやプロジェクト情報をツリー構造で示すソリューションブラウザが、左ペインにあります(製品版は確か右のはず)。拡大したのが以下です。

この例だと、ツリーのルートがソリューション(sampleCPP_vc90)を表していて、それに 4 つのプロジェクト(AsyncClient とか、Common)がぶら下がってます。これでビルドすると、それぞれのプロジェクトごとに exe ができたりライブラリができたりするわけですね。ファイルシステム上は、ソリューションファイルのあるフォルダに、プロジェクト毎にプロジェクトの名前で(別名でも可能)フォルダができて、その配下にソースファイルだのプロジェクトファイル(*.vcproj)とか一式があります。

プロジェクトを追加したいとき(あらたに、ライブラリを作ったり exe を作ったりしたいとき)は、ソリューションブラウザのソリューション名のところを右クリックして、

追加 → 新しいプロジェクト

とたどっていけばウィザードが起動するのでそれに従えばいいですが、プロジェクトの設定(特に、include ファイルの設定とか、コンパイルしたものを出力先とか)をはじめから設定するのがめんどいですよね。この場合は、エクスプローラを開いて、既存のプロジェクトをフォルダごとコピーして、フォルダの名前を追加するプロジェクトの名前に変更、さらにプロジェクトファイルの名前とかも便宜上変更して、VC++ のソリューションブラウザのソリューション名のところを右クリックして、

追加 → 既存のプロジェクト

とたどっていくと、追加するプロジェクトファイル(*.vcproj)をきいてくるので、さきほどフォルダごとコピーしたものを指定します。

で、追加されるんですが、ソリューションブラウザ的には同名のプロジェクトが追加されるので、名前変更してやります(F2 おしてもいいし、右クリックでメニューから選択してよいし)。

あと、デフォルトだと、各プロジェクトが実際にどのフォルダにあるのかとかわかりませんね(製品版だと、右ペインにでてくるはずですが)。対処方法としては、プロパティウィンドウを開いて、ソリューションブラウザのプロジェクト名をクリックしてやるとどのフォルダにあるとかいった情報もふくめて表示されます。なお、プロパティウィンドウは、標準ツールバーの以下の赤丸がついているボタンを押すとでてきます。

デフォルトではフローティングウィンドウですが、ソリューションブラウザが表示されている左ペインのタブの部分にドラックアンドドロップしてやるとドッキングします。

1-c) 構成マネージャ - リリース版とデバッグ

デフォルトだと、そのままビルドするとデバッグ版ができあがります。これはデバッグするための余計な情報が入っているので、サイズも大きいし、VC のランタイムがインストールされていない環境では動きません。リリース版をビルドしたい場合は、標準ツールバーの以下の赤丸の部分を Release に変更してからビルドします。

1-d) 構成プロパティの編集

1-b) のように、既存プロジェクトをコピっただけだと、コンパイル時のオプションとか、exe のファイル名とかコピー元のプロジェクトと同じなので、適宜変更する必要があります。こちらは、ソリューションブラウザの対象プロジェクト名を右クリックして、プロパティを選択、プロパティウィンドウを開いて、構成プロパティを編集します。プロパティウィンドウはこんな感じ。

まず構成プロパティ編集にあたっての注意は、どの「構成」を変更するか。つまり、1-c) で言及した、リリース版とかデバッグ版とか、どれを編集対象にするかということ。これは、プロパティウィンドウの上にある「構成」で選択します。以下のようなイメージ。

編集箇所は、左ペインの「構成プロパティ」からツリーを開いてでてくる各項目をクリックすると、右ペインにでてきます。主な変更箇所は以下ですかね。

  • C/C++ → 全般
  • リンカ → 全般
  • リンカ → 入力
    • 追加の依存ファイル

2.C 言語における LinuxWindows の違い

2-a) sleep

指定した時間、プログラム中で sleep したい場合。しかも、ミリ秒(あるいはそれよりももっと細かく)。

Windows は、Java と同じ感覚で以下の感じで実現できますね。

#include
  :
Sleep(int [ミリ秒]);

Linux だと、以下のパターンだと秒単位でしか sleep できないんですよね。

#include
  :
sleep(unsigned int [秒]);

なので、Linux の場合は usleep を使います。そうすると、マイクロ秒単位の sleep ができるんですね。

#include
  :
usleep(useconds_t [マイクロ秒]);
/* やってみた感じ、useconds_t 型のかわりに
  unsigned int で OK */

でも、usleep 関数は、1 秒を超える sleep ができないんですよね。なので、変数で指定して sleep 時間を指定して、1 秒を超えることが想定される場合は、とりあえず quitada 的には Linux で以下のようにコーディングしてみました。

#include
#include
  :
// slptime: sleep する時間(秒)
double slptime = 10.123;

// slpfloor: sleep する時間(秒)の整数部分
double slpfloor = floor(slptime);
if (slptime > 1) {
  sleep((int) slpfloor);
  slptime = slptime - slpfloor;
}
if (slptime > 0) {
  usleep((unsigned int) (slptime * 1000000));
}

なんか、もっと簡単な方法ないですかねぇ> Linux

2-b) gethostname

プログラムを実行したローカルマシンのマシン名を知りたいと思いました。Linux だと、割と簡単ですね。こんな感じ。

#include
#include
  :
// ch_name: マシン名をセットする文字列
char ch_name[256];
if(gethostname(ch_name, (size_t)sizeof(ch_name)) == -1) {
  strcpy(ch_name,"NA");
}

Windows はどうやら、わざわざ Winsock(Windows Sockets API)を使ってネットワークプログラミングっぽく実装してやらなくてはいけないようです。こんなイメージ。

#include
#include
  :
// ch_name: マシン名をセットする文字列
char ch_name[256];
WSADATA data;
if(WSAStartup(MAKEWORD(1, 1), &data)){
  // ソケットを初期化できなかったら
  strcpy_s(ch_name,"WF");
  /* strcpy を使ったらコンパイル時に VC に「非推奨」
    って怒られるから strcpy_s 使用 */
} else {
  if(gethostname(ch_name, (int)sizeof(ch_name)) == -1) {
    strcpy_s(ch_name,"NA");
  }
}
WSACleanup();

なんか、もっと簡単な方法ないですかね> Windows

3.C++ のコンストラクタで値をセット

コンストラクタで、インスタンス変数に値をセットする場合、Java の場合はこんな感じですよね。

class Hoge {
  private int id;
  private double value;

  Hoge (int i, double d) {
    id = i;
    value = d;
  }

C++ でも似たような書き方でできるようですが、以下のような記述もできるようで。

class Hoge {
private:
  int id;
  double value;
};

Hoge::Hoge (int i, double d) {
  : id(i), value(d) {
}

へ〜。