#define定義されているマクロ名と関数名の衝突

解決


たから  2008-04-14 05:08:51  No: 68014

はじめまして。個人でアプリケーションを作成しているものです。
今回、自作したクラスでDispatchMessage()という関数を宣言・定義しました。
ところが、アプリケーションではwindows.hをインクルードしているため、WinAPIのメッセージ処理関数用のマクロ↓
#ifdef UNICODE
#define DispatchMessage  DispatchMessageW
#else
#define DispatchMessage  DispatchMessageA
#endif // !UNICODE
が適用され、自作した関数がDispatchMessageW または DispatchMessageAに置換されてしまいます。
このような場合、アプリケーションの共通ヘッダファイルなどで、
#undef DispatchMessage
と定義してマクロ置換を解決するべきなのか、もしくはこのようなWinAPIで定義されているような関数名は使わずに関数名を変えるべきなのか、どちらを選択すればよいのでしょうか?
個人的にDispatchMessage()という関数名は機能に則したものなので、できればそう命名したいのですが…。

環境は
OS: Win Xp sp2
開発環境:Visual C++ 2008 Express
         Platform SDK February 2003
です。よろしくお願いします。


maru  2008-04-14 06:35:59  No: 68015

ライブラリを使用している時にそのライブラリの定義を変更してはいけません。変更するにしても、その影響を理解して行う必要があります。

また、そのアプリケーションのソースコードを見る人があなただけならあまり問題がないでしょうが、他の人が見たとき、DispatchMessage()が呼び出されていれば多分勘違いするでしょう。

どうしてもその名前を使いたいのであれば自分専用の名前空間を使用するのがよいでしょう。


たから  2008-04-14 08:26:31  No: 68016

maruさん、ご回答ありがとうございます。
まだ疑問は残ります。私の説明が足りませんでした。

>ライブラリを使用している時にそのライブラリの定義を変更してはいけません。
もちろん定義は変更はしません。ネームスペースも使用しております。
今回は下記のようなコードが、

namespace Hoge {
class MyDispatcher {
public: 
    void DispatchMessage();
    // ....
};
} // namespace Hoge end

windows.hをインクルードしたために

namespace Hoge {
class MyDispatcher {
public: 
    void DispatchMessageW();
    // ....
};
} // namespace Hoge end

のようにコンパイル前に置換されるのはどうかな?と思った次第です。
名前空間を使えば関数名の競合を防げるのは存じております。
ですが#defineによるマクロは名前空間に関係なく適用されてしまいます。
今回は上記のような予期せぬマクロ変換が起きていてもコンパイルができ、実行ファイルの動作にも影響がないので、なかなか変換に気が付きませんでした。

windows.hからインクルードされるwinuser.hで宣言されている::DispatchMessageというAPIに限らず、様々なAPIが#defineマクロで実際に呼び出す関数名を変換をしているようです。(UNICODEか否かで)

このようなAPI群と同じ関数名を自分のプログラムで使いたい場合、マクロで変換されないよう#undefした場合にも問題が起こります。
下記のコードは極端な例だと思いますが、

#undef DispatchMessage

namespace Hoge {
class MyDispatcher {
public: 
    void DispatchMessage()
  {
    MSG msg;
    ::DispatchMessage(&msg) // WinAPIを呼び出す
  }
    // ....
};
} // namespace Hoge end

とすれば関数名はマクロ変換されずに済みますが、::DispatchMessageもマクロ変換されず呼び出せなくなってしまいます。(宣言があるのは::DispatchMessageA or DispatchMessageWなので)
かといって、

#undef DispatchMessage

namespace Hoge {
class MyDispatcher {
public: 
    void DispatchMessage()
  {
    MSG msg;
    #ifdef UNICODE
    #define DispatchMessage  DispatchMessageW
    #else
    #define DispatchMessage  DispatchMessageA
    #endif // !UNICODE
    ::DispatchMessage(&msg) // WinAPIを呼び出す
  }
    // ....
};
} // namespace Hoge end

のように局所的にマクロを挿入するのも理不尽かと思う次第です。
アプリケーションがUNICODEを使うものと断定して直接DispatchMessageWを呼び出すのようにする手もありますが…。

書いてて思ったのですが、これはもうwindows.h等で宣言されているAPI関数(defineで変化される)と同じ名前の関数を使うのは避けた方が良いな、と思いました。

長文失礼いたしました。
なにかご意見ありましたらよろしくお願いします。


Ban  2008-04-14 09:24:24  No: 68017

Win32と同じ名前は、避けるのが無難だし、多分それしかない…。

一応、がっつりWindows依存のアプリを書いているわけでないならば、
stdafxなどでむやみにwindows.hをインクルードするのをやめて、
特定ソース内や、ラッパー関数などに隠蔽する
(その自作ソースとその呼び出し部に対してwindows.hのdefineが聞かないようにする)
など考えられなくはないですが、まぁ、多分混乱の元?


επιστημη  URL  2008-04-14 10:33:52  No: 68018

そういうこともあろうかと、僕はメソッド/関数名を小文字から始めるです (^^;
# たまたま慣れたスタイルなもんで。衝突抑止は副次効果ッス


maru  2008-04-14 19:29:14  No: 68019

> そういうこともあろうかと、僕はメソッド/関数名を小文字から始めるです (^^;
御意。
うちのグループではこのルールを強制しております。


maru  2008-04-14 19:34:55  No: 68020

> 名前空間を使えば関数名の競合を防げるのは存じております。
> ですが#defineによるマクロは名前空間に関係なく適用されてしまいます。
そうか、マクロの展開はプリプロセスで行なわれるから名前空間を使っても駄目か。

やはり素直に名前を変更した方がよろしいでしょう。


たから  2008-04-14 20:14:15  No: 68021

皆さん、ご回答ありがとうございます。
無理なことはせず、やはり素直に名前を変更して解決しようと思います。

> そういうこともあろうかと、僕はメソッド/関数名を小文字から始めるです (^^;

関数名の小文字スタートの有用性が一つ発見できました。
今回、外部公開する関数は大文字スタートにしようと考えていたのですが、メリットがあまりないですね。
これからは関数名は小文字スタートを心がけたいと思います。


επιστημη  URL  2008-04-14 22:33:38  No: 68022

ちなみに、引数のあるマクロなら()で横取りを抑止できます。ご参考まで。

#include <iostream>

void ri_chi(int) {
  std::cout << "通らばリーチ!\n";
}

void ron(int) {
  std::cout << "ロォン♪\n";
}

#define ri_chi(N) ron(N)

int main() {
  ri_chi(0);   // マクロに捕まる
  (ri_chi)(0); // マクロは手出しできない
}


あー  2008-04-15 18:38:41  No: 68023

ADLでカスタマイズポイントを提供したい所だとその手は使えないけどね


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加