有没有办法分解模板指向函数的指针?

时间:2015-01-27 18:23:50

标签: c++ templates function-pointers

目前我有一个类似的模板:

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

我正在使用它:

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

然而,我想知道是否可能只有这个:

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

如果我有这样的话,我就不能重载operator(),因为我无法将函数指针类型拆分为参数和返回类型(这样我就可以编写return_type operator()(args...) )。

有没有办法实现这个目标?

我使用VS2013 2013年11月CTP

3 个答案:

答案 0 :(得分:5)

你可以通过这样的专业化来实现:

// 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);
}

要像在你的例子中一样使用函数指针类型(虽然我更喜欢函数类型语法),写

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

附录:当我外出吃饭时还有一件事发生在我身上:operator()存在(轻微)问题,可能会或可能不会引起您的关注:您不会遇到转发问题使用通过值或左值引用传递的参数,因为它们只是在传入时传递(因为参数列表与函数指针和operator()完全相同),但是如果你计划使用rvalue-reference参数,这些参数不会隐式地对它们起作用。出于这个原因,

Entry<int(int&&)> foo;
foo(123);

无法编译。如果您打算将此函数用于带右值引用的函数,operator()可以像这样修复:

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

答案 1 :(得分:1)

可以使用@Wintermutes帖子中显示的部分专精 但是,如果没有它,您应该尝试的是:

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)...);
    }
};

模板参数必须是函数类型。但是,您可以使用函数类型的函数和指针,并在返回表达式中稍作修改:使用func*,而不是使用typename std::remove_pointer<func>::type*作为目标类型,即

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

答案 2 :(得分:0)

元编程方法。首先,一些指针特征试图保留调用约定信息:

template<class...>types {using type=types;};
enum class calling_convention {
  cdecl,
  clrcall,
  stdcall,
  fastcall,
  thiscall,
  vectorcall,
};
template<class Sig>
struct signature_properties;
template<class R, class...Args>
struct signature_properties {
  using return_type = R;
  using argument_types = types<Args...>;
};     
template<class FuncPtr>
struct function_properties;
#define CAT_(A,B) A##B
#define CAT(A,B) CAT_(A,B)

#define CALLING_CONVENTION_SUPPORT( CONVENTION ) \
  template<class R, class... Args> \
  struct function_properties< R(CAT(__, CONVENTION) *)(Args...) >: \
    signature_properties<R(Args...)> \
  { \
    using type = R(CAT(__, CONVENTION) *)(Args...) \
    static const calling_convention convention = calling_convention::CONVENTION; \
    static type from_pvoid(void const* pvoid) { \
      return static_cast<type>(pvoid); \
    } \
  }
CALLING_CONVENTION_SUPPORT(cdecl);
CALLING_CONVENTION_SUPPORT(clrcall);
CALLING_CONVENTION_SUPPORT(stdcall);
CALLING_CONVENTION_SUPPORT(fastcall);
CALLING_CONVENTION_SUPPORT(thiscall);
CALLING_CONVENTION_SUPPORT(vectorcall);
#undef CAT
#undef CAT_
#undef CALLING_CONVENTION_SUPPORT

Icky宏。严重的矫枉过正。未经测试。但是你明白了。

接下来,做一个帮助工作:

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;
};

帮助者的完美转发也很诱人。

最后,我们使用上面的特征类将工作从Entry退回到helper

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

除了我们在Entry中包含一个构造函数以获取void const*并将类型指针转发给helper

一个变化是我们从void*投射到我们的函数类型,我们知道它是函数类型,而不是我们调用的点。