2013-02-12 23 views
22

VC2012'de, benzersiz bir işaretçi ve bir deletici kullanarak bir kurucuda bir muteks oluşturmak istiyorum, böylece sadece CloseHandle'ı aramak için bir yok edici oluşturmam gerekmiyor.std :: unique_ptr, deleters ve Win32 API'sı

Bu işe olacağını sanırdım:

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

ama derleme üzerinde bir hata alıyorum:

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

I:

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

Ben böylece kurucusunu değiştirme daha sıra dışı olsun:

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

Şu an kaybettim. HANDLE, void için bir yazım hatasıdır *: Bilmem gereken bazı dönüşüm büyüsü var mı?

+0

Aşağıdaki soruna iyi bir yanıt var, ancak bu amaç için std :: unique_ptr's deleterini istismar etmekten ziyade, bir Mutex için kendi özel sahiplik sınıfımı hazırlamayı ciddi olarak düşünürüm. HANDLE'ın bir işaretçi olması, uygulama detayıdır. Bir indeks veya başka bir büyü değeri kadar kolay olabilirdi. Bunun için kendi RAII sarıcınızı yapın ve "gerçek" işaretçileri yönetmek için unique_ptr'den çıkın. –

+0

@Adrian: Senin noktanı görüyorum. Ayrıca, kurucu/yıkıcıda RAII Bekleme/Bırakma eşleştirmesini de içerebilir. Şerefe. – hatcat

cevap

40

Şimdilik özel silme işlemini unutun. std::unique_ptr<T> derken, unique_ptr yapıcısı bir T* almayı bekler, ancak CreateMutex bir HANDLE döndürür, bir HANDLE * değil.

std::unique_ptr<void, deleter> m_mutex; 

Bir void * için CreateMutex dönüş değer dağıtmak gerekir:

Bunu düzeltmek için 3 yol vardır.

Bunu yapmanın başka bir yolu, HANDLE 'un altta yatan türüne ulaşmak için std::remove_pointer kullanın.

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

Oysa bunu yapmanın başka bir yolu unique_ptr 'ın deleter pointer adında yuvalanmış türü içeriyorsa, o zaman unique_ptr onun yönetilen nesne işaretçisi yerine T* için bu tür kullanacak gerçeğini istismar etmektir. Windows API ile uğraşırken, sonra deleter olarak türünü için bir işlev geçirmek istiyorsanız

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()) {} 

Şimdi, aynı zamanda fonksiyon işaretçileri oluştururken çağrı kongre dikkat etmek gerekir.

Yani, CloseHandle bir işlev işaretçisi, her şeyi birleştiren bu

BOOL(WINAPI *)(HANDLE) 

gibi

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

bakmalıyız ben daha kolay bir lambda yerine

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

kullanmak bulmak Veya yorumlarda @hjmd tarafından önerildiği gibi, decltype kullanın e fonksiyon işaretçisinin türü.

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

+1 Fonksiyon işaretçisi yerine sadece decltype (& CloseHandle) – hmjd

+1

kullanın. Bir lambda ifadesi, bir değer değil, bir değer verir. Lambda örneğinin nasıl derleyeceğini anlamıyorum. – GManNickG

+0

@GManNickG Haklısınız, düzeltin. Teşekkürler! – Praetorian

1

sorun aslında (KOLU *) işlemek yazmak için işaretçi tutan unque_ptr tanımlamak, ancak sadece buna işaretçisi değil KOLU geçiren.

+0

Praetorian'un cevabı oldukça dolu ve doğru, IHMO. Yazarken aynı zamanda yazıyordum. –

28

Diğerleri, tüm HANDLE/HANDLE* sorununun nasıl çalıştığını belirttiler. Burada, std::unique_pointer'un ilginç özelliklerini kullanarak başa çıkmak için çok akıllıca bir yol var.

struct WndHandleDeleter 
{ 
    typedef HANDLE pointer; 

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

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

Bu, herhangi bir fantezi std::remove_pointer veya başka tür şeyler olmadan yerine HANDLE* ait HANDLE dönmek için unique_handle::get verir.

Bu, HANDLE bir işaretçi olduğu için çalışır ve bu nedenle NullablePointer'ı karşılar.

+0

Azıcık yırtılmışım: Praetorian'un cevabının son çizgisini bütün çözümün yerel olarak sevdim, ama çözümünüzü iyi bir yeniden kullanım parçası olarak beğeniyorum. Sana bir + 1 vereceğim! Teşekkürler. – hatcat

+0

+1. Ben de burada anonim bir yapı kullanabileceğini mi tahmin ediyorum? –

+0

@ MahmoudAl-Qudsi: Neden yapmak istediğini bilmiyorum, ama sanırım bir isim içermeyen bir yapı bildirebilirsin, ama değişken bir bildirimle. Sonra türünü almak için 'decltype' kullanın. Ama hiçbir şey kazanmıyorsunuz, çünkü her iki şekilde de isim alanına isim ekliyorsunuz. –