SoftwareTips and Traps

PeekMessage()

発端

Win32 の PeekMessage() 関数で罠にはまりました。

コード


LRESULT C__::WndProc( UINT msg, ... )
{
   switch( msg )
   {
   case WM_DORESET:
   {
      DoReset( );
 
      //-- 滞積しているメッセージを除去する。
      MSG msg;
      while( ::PeekMessage( &msg, m_hWnd, WM_DORESET, WM_DORESET, PM_REMOVE ) )
      {
         ;
      }
   } break;
   }
   return CSuperClass::WndProc( ... );
}

こんな感じで、プライベートウィンドウメッセージ WM_DORESET に応答して、DoReset() を実行する機構を作りました。

WM_DORESET メッセージは都合上、様々なタイミングで何度もポストされます。 しかし、一度 DoReset() を実行すると、改めてポストされるまでの滞積しているメッセージは不要になる仕様でした。 そこで、PeekMessage() を使って不要に滞積したメッセージをキューから除去しようと思ったわけですが、問題が起こりました。

Q: その問題とはなんでしょう。

A: アプリケーションが終了しなくなった。

原因は、わかってみれば簡単なのですが、PeekMessage() が WM_QUIT をキューから除去してしまうからです。 メッセージの取得範囲を明示的に指定しても、WM_QUIT は必ず取得されるという仕様をよく見ていなかったために陥ったトラブルでした。

滞積したメッセージを除去している間に WM_QUIT を取得した場合は、後で PostQuitMessage() を再度コールするという方法で一旦は解決したのですが…。 よくよく考えてみたら、プライベートメッセージを投げる前に既にそのメッセージがキューにあるかをチェックするだけで済みます。 オチとしては情け無いものですが。

template w dll

発端

クラスライブラリを DLLで提供する時、クラステンプレートを公開する事があります。 そのようなケースでなんとも嫌な感じのエラーに遭遇しました。 情報は古いのですが忘備録も兼ねてレポートします。

コード

まず DLL プロジェクトで、スタティック変数を持つクラステンプレートを宣言します。


template<typename T> class TSample1
{
public:
   static T* s_pInstance;
};
 
template<typename T> T* TSample1<T>::s_pInstance = NULL;

また DLL プロジェクト内で TSample1 を使用するコードを書きます。


class __declspec(dllex/import) CSample1 : public TSample1<CSample1>
{
public:
   CSample1( );
   {
      s_pInstance = this;
   }
};

コンストラクタのインプリメントは以下の通り。


CSample1::CSample1( )
{
   s_pInstance = this;
}

この DLL を他の EXE プロジェクトから使用します。

問題が発生する EXE プロジェクトのサンプルは…


WinMain( ... )
{
   CSample1 sample1;
   assert( sample1::s_pInstance != NULL );
   //-- ↑ここでアサート。
	...
};

こんな感じです。

デバッガでトレースしていくと、DLL 内のコードから EXE 内のコードへ移った時に、&(CSample1::s_pInstance) の値が変わります。 DLL で見ている CSample1::s_pInstance と EXE でのそれは別物なんですね。

以下の構文でクラステンプレートを明示的にエクスポートしてもダメみたいでした。


extern template __declspec(dllex/import) TSample1<CSample1>;

static 変数と class template と DLL を併用するワークスペースでは、この問題に陥りやすいので注意が必要です。

Singleton パターン

前置き

アプリケーションの中で、あるクラスのインスタンスが決して2つ以上作成されないようにしたい事がよくあります。 デザインパターンではこういうクラスを、Singleton パターンと呼ぶみたいです。

例1

Singleton の定義は、あるクラスのインスタンスが最大 1つしか存在しない事を保証する事です。 この要求を満たす最も簡単なのは、コンストラクタが 2回目に呼び出されたときにアサートする方法です。


class CSample1
{
public:
   CSample( )
   {
      static bool bFirst = true;

      assert( bFirst );

      if( bFirst )
      {
         bFirst = false;
      }

      ...
   }
};

次のようなマクロを定義しておくと便利かもしれません。


#if defined _DEBUG
#define SIMPLE_SINGLETON( )  {\
                                static bool bFirst = true;\
                                assert( bFirst );\
                                if( bFirst ){ bFirst = false; }\
                             }
#else
#define SIMPLE_SINGLETON( )
#endif

…あんまり便利じゃないかもしれませんけどね。

例2

確かに例1の方法は、Singleton パターンの基本的な要件を満たしています。 しかし、クラスのインスタンスををグローバル変数の様に、任意の場所から任意のタイミングで使用でき、かつそれらは同じインスタンスである事が要求される事もまた多くあります。 このような場合は、クラスがスタティックな取得関数を提供する事で解決できます。


class CSample2
{
public:
   //-- 静的な取得関数。
   static CSample2& GetInstance( )
   {
      static CSample2 instance;
      return instance;
   }

private:
   //-- デフォルトのコンストラクタはプライベート。
   //   GetInstance() 以外の方法で構築させない。
   CSample2( )
   {
      ...
   }
};

このパターンでは、CSample2 のインスタンスは要求された段階で構築されます。 要求されなければ構築されないという点でも合理的といえます。

例3

例2 の方法に強いてケチをつけるとすれば、必要な時にインスタンスを破棄できないという点でしょうか。 CSample2 の場合、インスタンスが破棄されるのは main( ) や WinMain( ) 関数から戻った後です。 時には明示的にインスタンスを破棄する必要があるかもしれません。 この場合、ちょっと面倒くさい事になります。


class CSample3
{
private:
   static CSample3* s_pInstance;

public:
   //-- 静的な取得関数。<
   static CSample3* GetInstance( )
   {
      if( s_pInstance == NULL )
      {
         s_pInstance = new CSample3( );
      }
      return s_pInstance;
   }
   static void DeleteInstance( );
   {
      delete s_pInstance;
      s_pInstance = NULL;
   }

private:
   //-- デフォルトのコンストラクタはプライベート。
   //   GetInstance() 以外の方法で構築させない。
   CSample3( )
   {
      ...
   }
};

ソースファイルでは、

CSample3* CSample3::s_pInstance = NULL;

と記述して、スタティックなインスタンスポインタを初期化しておきます。

例4

こんな感じで、CSample3 はグローバル変数的に使用するクラスとしてはほぼ問題ない形になりました。 ただ、これらのメンバ関数を毎回追加するのは面倒です。 クラステンプレートとして準備できないか考えてみましょう。


template<typename T> class TSample4
{
private:
   static T* s_pInstance;
 
public:
   //-- 静的な取得関数。
   static T* GetInstance( )
   {
      if( s_pInstance == NULL )
      {
         s_pInstance = new T( );
      }
      return s_pInstance;
   }
   static void DeleteInstance( );
   {
      delete s_pInstance;
   }
 
protected:
   TSample4( )
   {
      //-- NULL でない偽のアドレスを T と TSingleton<T> のポインタにキャストし、その差を取得します。
      //-- 多重継承を使用しない限り、iOffset は 0 です。
      static int iOffset
                  = ( reinterpret_cast<int>( reinterpret_cast<T*>(1) )
                  -   reinterpret_cast<int>( reinterpret_cast<TSingleton*>(1) ) );
 
      //-- this から T のアドレスを計算して s_pInstance を初期化します。
      s_pInstance = reinterpret_cast<T*>( reinterpret_cast<int>(this) + iOffset );
   }
   ~TSample4( )
   {
      s_pInstance = NULL;
   }
};

クラスを宣言する時は、次のようにします。


class CSample4B : public CBaseClass, public TSample<CSample4>
{
};

この例では、2つの多重継承の右側に TSample4 が指定されています。