2015-01-27 19 views
6

Şu anda bir şablon şöyle var:Şablonlu işaretçiyi bir işleve ayırmanın bir yolu var mı?

template<typename func, typename ret, typename... args> class Entry{ 
public: 
    PVOID Address; 
    ret operator()(args...){ 
     return ((func) this->Address)(args...); 
    } 
}; 

Ve böyle kullanıyorum:

Entry<int(*)(int), int, int> func; 
// ^func  ^ret ^args 
func.Address = (PVOID) 0xDEADC0DE; 
func(123); // calls 0xDEADC0DE with '123' as argument 

Ancak, acaba eğer mümkünse sadece bu var:

Entry<int(*)(int)> func; 
// ^only specifying the function's prototype once instead of breaking it down 
func(123); 

Eğer böyle bir şeye sahip olsaydım, operator()'u aşırı yükleyemedim, çünkü işlev işaretçi türünü argümanlara ve dönüş türüne göre ayırmam mümkün değil. return_type operator()(args...) yazınız).

Bunu başarmanın bir yolu var mı? Ben VS2013 Kas kullanıyorum

2013 CTP

Böyle bir uzmanlaşma ile bunu yapabilir
+1

"void *" işlev göstergelerini depolamanın teknik bir sorunu olduğunu düşündüm? Kesinlikle herhangi bir platformda VS2013 hedefleri, itiraf ediyorum. – Yakk

+0

@Yakk, fonksiyon göstergelerinin boyutundan dolayı olabilir (normal bir işaretçiden çok daha büyük olabilir), ancak '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' 'fonksiyonunu yapmıyorum. Herhangi bir zarar vermemesi gereken – rev

cevap

5

: Sevdiğim rağmen (

// Entry has one template argument 
template<typename func> class Entry; 

// and if it's a function type, this specialization is used as best fit. 
template<typename ret, typename... args> class Entry<ret(args...)>{ 
public: 
    PVOID Address; 
    ret operator()(args... a){ 
    return ((ret(*)(args...)) this->Address)(a...); 
    } 
}; 

int main() { 
    Entry<int(int)> foo; 
    foo.Address = (PVOID) 0xdeadc0de; 
    func(123); 
} 

sizin örnekte olduğu gibi bir işlev işaretçisi türü ile kullanmak için fonksiyon tipi sözdizimi iyi), yazma

//           here ------v 
template<typename ret, typename... args> class Entry<ret(*)(args...)>{ 

Zeyilname: operator() tha ile (hafif) bir sorun vardır: Ben yemeğimi başlamıştı zaman bir şey daha bana geldi t sizi endişelendirebilir ya da olmayabilir: Aktarım sorunları, iletilen değerlerle veya lvalue referansı ile geçirilen parametrelerle çalışmazsınız çünkü bunlar aktarıldıklarında aktarıldıkları için (çünkü argüman listesi tam olarak aynıdır) işlev işaretçisi ve operator()), ancak rvalue referans parametrelerini kullanmayı planlıyorsanız, bunlar için örtük olarak çalışmaz. Bu nedenle,

derlenmiyor. Eğer rvalue başvuruları almaya fonksiyonları ile bu kullanmayı planlıyorsanız, operator() şöyle düzeltilebilir: @Wintermutes sonrası gösterilen

ret operator()(args... a){ 
    //     explicit forwarding ----v 
    return ((ret(*)(args...)) this->Address)(std::forward<args>(a)...); 
} 
+0

Bununla bir çağrı kuralı belirtebilir miyim? Entry foo; 've AND tipi sözdizimi gibi? Ya da bir işlev işaretçisi kullanmalıyım? – rev

+1

'Entry foo;' MSVC 2013 ile kullanabilirsiniz. '__stdcall' standardın bir parçası değildir, bu nedenle diğer derleyiciler muhtemelen desteklemeyecektir. – Wintermute

+0

Harika. Şimdi, sınıfıma şu şekilde bir kurucu eklemeyi denedim: 'Entry foo (0xC0DE); ', ancak' tamamlanmamış türde izin verilmez 'diyor. Yazım tipini kullanıyorum. Neyi kaçırıyorum? – rev

1

Kısmi uzmanlık olarak mümkündür.
Ancak, bu olmadan mümkün olmalıdır çalıştığın şey:

template <typename func> 
class Entry{ 
public: 
    PVOID Address; 

    template <typename... Args> 
    auto operator()(Args&&... args) 
    -> decltype(((func*) Address)(std::forward<Args>(args)...)) { 
     return ((func*) Address)(std::forward<Args>(args)...); 
    } 
}; 

şablon argümanı bir işlev türü olmalıdır. yani

template <typename... Args> 
auto operator()(Args&&... args) 
-> decltype(((typename std::remove_pointer<func>::type*) Address)(std::forward<Args>(args)...)) { 
    return ((typename std::remove_pointer<func>::type*) Address)(std::forward<Args>(args)...); 
} 

Demo, typename std::remove_pointer<func>::type* kullanmak yerine, dönüşümünün hedef türü olarak func* kullanılarak: Bununla birlikte, bu dönüş ifadesinde ufak bir değişiklikle türleri çalışması için, her iki fonksiyon ve işaretçi ile iş yapabilir.

0

Bir metaprogramlama yaklaşımı. İlk olarak, çağrı kuralı bilgilerini korumaya çalışan bazı işaretçi özellikleri:

Icky makroları. Ve ciddi overkill. Ve denenmemiş. Ama fikri anladın.

Sonraki, işi yapmak için bir yardımcı: yardımcısında

template<class FuncPtrType, class R, class Args> 
struct helper; 
template<class FuncPtrType, class R, class... Args> 
struct helper<FuncPtrType, R, types<Args...>> { 
    FuncPtrType ptr; 
    R operator()(Args...args)const { 
    return ptr(std::forward<Args>(args)...); 
    } 
    helper(FuncPtrType p):ptr(p) {}; 
    helper(helper const&)=default; 
    helper& operator=(helper const&)=default; 
}; 

Mükemmel yönlendirme da cazip olacaktır.

Son olarak, helper için Entry gelen çalışmayı çıkma yukarıdaki özellikleri sınıfını kullanın:

template<class FuncPtrType> 
struct Entry:helper< 
    FuncPtrType, 
    typename signature_properties<FuncPtrType>::return_type, 
    typename signature_properties<FuncPtrTpye>::arguments 
> { 
    using parent = helper< 
    FuncPtrType, 
    typename signature_properties<FuncPtrType>::return_type, 
    typename signature_properties<FuncPtrTpye>::arguments 
    >; 
    Entry(void const* pvoid):parent(static_cast<FuncPtrType>(pvoid)) {} 
}; 

biz void const* alıp helper yazdığınız işaretçi iletmek için Entry bir yapıcı dahil hariç.

Bir değişiklik, void*'dan işlev türümüze, çalıştığımız noktada değil, işlev türü olduğunu bildiğimiz en erken noktada yayınladığımızdır.

İlgili konular