none
#defineのマクロの挙動が良く分からない?? RRS feed

  • 質問

  • すいません、初歩的な質問だと思うのですが、プリプロセッサの挙動についての質問です。

    ソースの一部を、#define hogehogeの宣言があるかどうかでコンパイルする/しないを切り分けるようなものを作っています。
    (使用頻度が少ない割に色んなライブラリをリンクしなければいけない部分について、#defineで宣言しなければコンパイルしないようにしたいです)

    ・testApplication.h

    #pragma once
    #define hogehoge
    #include "fugafuga.h"
    
    ...

    ・fugafuga.h

    #pragma once

    namespace fuga { class Fuga { Fuga(); }; } // namespace fuga


    ・fugafuga.cpp

    #include "fugafuga.h"
    
    namespace fuga{
     Fuga::Fuga() {
    #ifdef hogehoge
      // hogehogeを宣言してないとここがアクティブにならないはず
    #endif
      }
    } // namespace fuga

    というような感じで書いているのですが、このfugafuga.cppの#ifdef hogehogeがアクティブになってくれません。
    そして、fugafuga.hの中に#ifdef hogehogeを入れると、アクティブになったりならなかったりと挙動が安定しません。

    こういうようにtestApplication.hで何らか宣言させることによって、fugafuga.cppのコンパイル内容を切り替えたい場合って、どういうように実現したら良いんでしょうか?

    2014年3月30日 9:21

回答

  • fugafuga.cppにおいて、#includeされているのはfugafuga.hだけです。

    そしてfugafuga.hにはとくにhogehogeなるマクロは定義されていません。

    ですので、fugafuga.cppにおいてはhogehogeなるマクロは定義されていない状態になっています。

    testApplication.hをfugafuga.cppで#includeすれば、fugafuga.cppにおいてhogehogeマクロが定義されることになります。

    • 回答としてマーク 星 睦美 2014年4月1日 4:55
    2014年3月30日 10:16
  • コンパイル時の切り替えが嬉しいなーと思います。

    大半の人が作成する「音声を使わないプロジェクト」では不要なライブラリへリンクせずとも使うことができ、一部の人が作成する「音声を使うプロジェクト」では、そちらへのリンクを貼ってもらうような想定です。

    この2者が矛盾しています。前者はコンパイル時に切り替えと言われていますが後者はリンク時に切り替えを意識しています。

    コンパイル時に切り替えの場合、利用者が(切り替えに必要な部分の)ソースコードを得、利用者がその部分をコンパイルすることになります。対して、リンク時に切り替えであれば使用する関数名を切り替えるだけで利用者は関数本体のソースコードを使用しません。

    質問者さんのいう「ライブラリ」とはどのようなものを想定していますか? また利用者へのソースコードの配布を意識していますか?
    ライブラリには静的ライブラリ(.LIB)と動的ライブラリ(.DLLと.LIB)の2種類があります。静的ライブラリであれば、使用された関数だけ組み込まれるため、「音声を使う関数」「音声を使わない関数」両方を.LIBに含めておけばリンク時に切り替えてもらう必要はなくなります。
    動的ライブラリも似たような状況ではありますが、「音声を使う関数」「音声を使わない関数」を別DLLに分け関数名を同一のものにすることもできます(ややこしく危険ですからやはり関数名を分けることをお勧めしますが。)

    加えて、#pragma commentを用いることで

    #ifdef HOGEHOGE #pragma comment( lib, "LIBRARY-A" ) #define LibraryFunction LibraryFunctionA #else #define LibraryFunction LibraryFunctionB #pragma comment( lib, "LIBRARY-B" ) #endif


    適切なライブラリへのリンク切り替えも含めてしまい、利用者の手間を減らすことも可能です。CRTやMFCもそのような仕組みを利用していますので、ヘッダーファイルを検索してみると参考になります。

    • 回答としてマーク 星 睦美 2014年4月1日 4:54
    2014年3月31日 2:03
  • ヘッダーファイルに#defineする方法でもよいですが、
    もちっと大人のやり方としては、「構成マネージャ」で、
    両ビルド毎の「構成」を作成するという方法もあります。
    この場合、デバッグビルドとリリースビルドの様に
    構成毎に異なるビルド結果が得られます。
    #defineに相当する部分は、構成別にプロパティDLGから
    「プリプロセッサの定義」で定義します(直接コードをしません)。
    コード上では#ifdefで切り替えることになります(今まで通り)。
    ご検討してみてはどうでしょう。
    2014年3月31日 2:05

すべての返信

  • fugafuga.cppにおいて、#includeされているのはfugafuga.hだけです。

    そしてfugafuga.hにはとくにhogehogeなるマクロは定義されていません。

    ですので、fugafuga.cppにおいてはhogehogeなるマクロは定義されていない状態になっています。

    testApplication.hをfugafuga.cppで#includeすれば、fugafuga.cppにおいてhogehogeマクロが定義されることになります。

    • 回答としてマーク 星 睦美 2014年4月1日 4:55
    2014年3月30日 10:16
  • さっそくのご返信ありがとうございます!
    ということは、ライブラリ内でコンパイル時に切り分けたいマクロはユーザが作成するファイル内で宣言させることはできないんですね。

    上記のfugafuga.hとfugafuga.cppはライブラリとして提供しようとしていて、testApplicationはユーザに作成して貰う、というイメージなのですが、上記のようにユーザの用途によって内部のコンパイルを切り分ける、ということを実現したい場合、ライブラリ内のfugafuga.hをハックして貰って#define hogehogeを宣言して貰う(コメントアウトさせる等)という形の実装が一番妥当でしょうか。

    ・fugafuga.h

    #pragma once
    
    // ○○の機能を使用したい場合は以下をコメントアウトしてください
    //#define hogehoge
    
    namespace fuga {
      class Huga {
        Fuga();
      };
    } // namespace fuga

    2014年3月30日 11:28
  • 機能を切り替えるときの方法として

    • コンパイル時切り替え(質問されているような#defineで関数の中身を切り替える)
    • リンク時切り替え(WindowsがMessageBoxA()とMessageBoxW()のように#defineで関数全体を切り替える)
    • 実行時切り替え(関数の引数を指定してもらい if文で切り替える)

    の3種類があります。質問者さんはどう切り替えたいのでしょうか? どうもよくわからないまま迷走しているように見受けられます。

    2014年3月30日 11:45
  • ありがとうございます。:)

    コンパイル時の切り替えが嬉しいなーと思います。

    具体的に言えば、カメラデバイス用のライブラリを作っていて、その中の音声処理を行う部分なのですが、余り使う人が居ない上に、処理の中で使う機能の一部が再頒布可能ではないライブラリにリンクする必要があるので、ユーザに「音声を使わないプロジェクト」、「音声を使うプロジェクト」で切り分けて使って欲しい、というイメージです。

    大半の人が作成する「音声を使わないプロジェクト」では不要なライブラリへリンクせずとも使うことができ、一部の人が作成する「音声を使うプロジェクト」では、そちらへのリンクを貼ってもらうような想定です。

    切り替えるケースは、「そこの機能を使わない場合」と「使う場合」で、使わない場合は該当部分の機能をコンパイルせず、#define hogehogeを宣言せずに使おうとしたらエラーで「#define hogehogeを宣言して○○.libをリンクする必要がありますよ」と出そうとしています。

    2014年3月31日 0:54
  • コンパイル時の切り替えが嬉しいなーと思います。

    大半の人が作成する「音声を使わないプロジェクト」では不要なライブラリへリンクせずとも使うことができ、一部の人が作成する「音声を使うプロジェクト」では、そちらへのリンクを貼ってもらうような想定です。

    この2者が矛盾しています。前者はコンパイル時に切り替えと言われていますが後者はリンク時に切り替えを意識しています。

    コンパイル時に切り替えの場合、利用者が(切り替えに必要な部分の)ソースコードを得、利用者がその部分をコンパイルすることになります。対して、リンク時に切り替えであれば使用する関数名を切り替えるだけで利用者は関数本体のソースコードを使用しません。

    質問者さんのいう「ライブラリ」とはどのようなものを想定していますか? また利用者へのソースコードの配布を意識していますか?
    ライブラリには静的ライブラリ(.LIB)と動的ライブラリ(.DLLと.LIB)の2種類があります。静的ライブラリであれば、使用された関数だけ組み込まれるため、「音声を使う関数」「音声を使わない関数」両方を.LIBに含めておけばリンク時に切り替えてもらう必要はなくなります。
    動的ライブラリも似たような状況ではありますが、「音声を使う関数」「音声を使わない関数」を別DLLに分け関数名を同一のものにすることもできます(ややこしく危険ですからやはり関数名を分けることをお勧めしますが。)

    加えて、#pragma commentを用いることで

    #ifdef HOGEHOGE #pragma comment( lib, "LIBRARY-A" ) #define LibraryFunction LibraryFunctionA #else #define LibraryFunction LibraryFunctionB #pragma comment( lib, "LIBRARY-B" ) #endif


    適切なライブラリへのリンク切り替えも含めてしまい、利用者の手間を減らすことも可能です。CRTやMFCもそのような仕組みを利用していますので、ヘッダーファイルを検索してみると参考になります。

    • 回答としてマーク 星 睦美 2014年4月1日 4:54
    2014年3月31日 2:03
  • ヘッダーファイルに#defineする方法でもよいですが、
    もちっと大人のやり方としては、「構成マネージャ」で、
    両ビルド毎の「構成」を作成するという方法もあります。
    この場合、デバッグビルドとリリースビルドの様に
    構成毎に異なるビルド結果が得られます。
    #defineに相当する部分は、構成別にプロパティDLGから
    「プリプロセッサの定義」で定義します(直接コードをしません)。
    コード上では#ifdefで切り替えることになります(今まで通り)。
    ご検討してみてはどうでしょう。
    2014年3月31日 2:05
  • >> 佐祐理さん

    ありがとうございますー。:)

    ライブラリとして提供という表現に若干語弊がありましたね、すいません。
    libやdllのようにコンパイル済のものを公開するのではなく、ソースごと公開してユーザにアプリケーションのソースと一緒にコンパイルして貰うような公開の仕方を想定しています。

    フォルダ構成が
    - lib
     + fugafuga.h
     + fugafuga.cpp

    - src
     + testApplication.h
     + testApplication.cpp
     + main.cpp

    のような感じです。
    なので、ユーザに触って貰う想定のsrcフォルダ内のソースで#defineしてもらえれば、アプリ毎に「これは音声使うプロジェクト」、「これは使わないプロジェクト」と分けて使えるなーと思っていました。
    fugafuga.hに#defineする必要があると、色んなプロジェクトを作って「これは音声使うプロジェクト」、「これは使わないプロジェクト」と切り替えるときにいちいち毎回fugafuga.hの#defineを定義したりコメントアウトしたりしなきゃいけないので、うーんと悩んでいました。

    ありがとうございます~!CRTとかMFC、見てみて勉強してみます。:)


    >>仲澤@失業者さん

    ありがとうございます。
    なるほどー!構成マネージャでプロプロセッサ定義ができるんですね。
    そこにhogehogeを入れちゃえばifdefで切り替えられるということですね。
    それはピッタリな気がします。:)


    皆様、ありがとうございました!

    2014年3月31日 8:40