2013-02-12 16 views
22

VC2012では、コンストラクタ内に一意のポインタとディテクタを使用してミューテックスを作成したいので、CloseHandleを呼び出すだけでデストラクタを作成する必要はありません。std :: unique_ptr、deletersおよびWin32 API

私はこれが働くだろうと思っているだろう:

struct foo 
{ 
    std::unique_ptr<HANDLE, BOOL(*)(HANDLE)> m_mutex; 
    foo() : m_mutex(CreateMutex(NULL, FALSE, NULL), CloseHandle) {} 
} 

が、私はエラーを取得するコンパイル上:

error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(void *,int 
(__cdecl *const &)(HANDLE)) throw()' : cannot convert parameter 1 from 
'HANDLE' to 'void *' 

私はこのようにコンストラクタを変更する場合:

foo() : m_mutex((void*)CreateMutex(NULL, FALSE, 
    (name + " buffer mutex").c_str()), CloseHandle) {} 

私はさらに珍しい:

error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(void *, 
int (__cdecl *const &)(HANDLE)) throw()' : cannot convert 
parameter 1 from 'void *' to 'void *' 

私は今失業しています。 HANDLEはvoid *のtypedefです:私が知る必要のある変換マジックはありますか?

+0

あなたの質問には良い答えがありますが、私は真剣に、この目的のためにstd :: unique_ptrのデリターを悪用するのではなく、自分のカスタムオーナークラスをMutexのために使うことを検討します。 HANDLEがポインタであるという事実は実装の詳細です。インデックスやその他の魔法の価値があったのと同じように簡単にできます。独自のRAⅡラッパーを作成し、unique_ptrを残して "実際の"ポインタを管理してください。 –

+0

@Adrian:あなたの意見が分かります。また、コンストラクタ/デストラクタにRAII Wait/Releaseペアリングを含めることもできます。乾杯。 – hatcat

答えて

40

今のところカスタムディレクターについては忘れてしまいます。 std::unique_ptr<T>と指定すると、は、T*を受け取りますが、CreateMutexは、HANDLE *ではなく、HANDLEを返します。

この問題を解決するには3通りの方法があります。

std::unique_ptr<void, deleter> m_mutex; 

あなたはvoid *CreateMutexの戻り値をキャストする必要がありますが。

もう1つの方法はstd::remove_pointerを使用してHANDLEの基になるタイプにすることです。これを行うには

std::unique_ptr<std::remove_pointer<HANDLE>::type, deleter> m_mutex; 

さらにもう1つの方法は、その後、unique_ptrは、その管理オブジェクトポインタの代わりに、T*ため、そのタイプを使用しますが、もしunique_ptrのデリータはpointerという名前のネストされたタイプが含まれているという事実を利用することです。

struct mutex_deleter { 
    void operator()(HANDLE h) 
    { 
    ::CloseHandle(h); 
    } 
    typedef HANDLE pointer; 
}; 
std::unique_ptr<HANDLE, mutex_deleter> m_mutex; 
foo() : m_mutex(::CreateMutex(NULL, FALSE, NULL), mutex_deleter()) {} 

あなたはデリータようなタイプの関数へのポインタを渡したい場合は、WindowsのAPIを扱うときに今、あなたはまた、関数ポインタを作成するときに呼び出し規約に注意を払う必要があります。

ので、CloseHandleへの関数ポインタはそれのすべてを組み合わせると、この

BOOL(WINAPI *)(HANDLE) 

std::unique_ptr<std::remove_pointer<HANDLE>::type, 
       BOOL(WINAPI *)(HANDLE)> m_mutex(::CreateMutex(NULL, FALSE, NULL), 
               &::CloseHandle); 

のように見える必要があり、私はそれが簡単に代わり

std::unique_ptr<std::remove_pointer<HANDLE>::type, 
       void(*)(HANDLE)> m_mutex; 
foo() : m_mutex(::CreateMutex(NULL, FALSE, NULL), 
       [](HANDLE h) { ::CloseHandle(h); }) {} 

をラムダを使用することを見つけますまた、@hjmdのコメントのように、decltypeを使ってe関数ポインタの型。

std::unique_ptr<std::remove_pointer<HANDLE>::type, 
       decltype(&::CloseHandle)> m_mutex(::CreateMutex(NULL, FALSE, NULL), 
                &::CloseHandle); 
+5

+1関数ポインタの代わりにdecltype(&CloseHandle)を使用します – hmjd

+1

ラムダ式は型ではなく値を生成します。私はラムダの例がどのようにコンパイルされるのか分かりません。 – GManNickG

+0

@GManNickGあなたは正しいです、それを修正しました。ありがとう! – Praetorian

1

問題は、あなたが実際に(HANDLE *)を扱う型へのポインタを保持しているunque_ptrを定義ですが、あなたはそれへのポインタ、ハンドルではなく通過します。

+0

Praetorianの回答はかなり完全であり、IHMOです。私が書いていたのと同じ時間に書かれています。 –

28

HANDLE/HANDLE*の全号がどのように作用するかを指摘している人もいます。興味深い機能を使用して、それに対処するより洗練された方法があります。std::unique_pointer

struct WndHandleDeleter 
{ 
    typedef HANDLE pointer; 

    void operator()(HANDLE h) {::CloseHandle(h);} 
}; 

typedef std::unique_ptr<HANDLE, WndHandleDeleter> unique_handle; 

これはunique_handle::getは、任意の空想std::remove_pointerまたは他のそのようなことなしに、HANDLE代わりのHANDLE*を返すことができます。

これは、HANDLEがポインタであり、したがってNullablePointerを満たすために機能します。

+0

私は少し裂けています:私はPraetorianの答えの最終行が全体的な解決策としてローカルにあるのが好きですが、私はあなたの解決策が好きです。私はあなたに+1を差し上げます!ありがとう。 – hatcat

+0

+1。ここでも匿名の構造体を使うことができると思いますか? –

+0

@ MahmoudAl-Qudsi:あなたが望む理由はわかりませんが、名前のない構造体を宣言できると思いますが、変数の宣言があります。次に、 'decltype'を使って型を取得します。しかし、あなたは名前空間に名前を挿入しているので、何も得ていません。 –

関連する問題