在另一个模板函数

时间:2016-12-12 06:18:21

标签: c++ visual-studio template-meta-programming

我正在制作一个类模板来编码函数指针。该函数可以具有任何结果类型和参数的数量/类型。这就是我所拥有的:

LPVOID EncodePtr(LPVOID ptr) {
  // Encode...
  return ptr;
}

LPVOID DecodePtr(LPVOID ptr) {
  // Decode...
  return ptr;
}

template<class T>
class encoded_ptr {
public:

  typedef encoded_ptr<T> _Myt;

  encoded_ptr() {
    ptr_ = (T*)EncodePtr(nullptr);
  }

  // Irresponsible?
  template<class _OtherType>
  encoded_ptr(_OtherType ptr) {
    ptr_ = (T*)DecodePtr((LPVOID)ptr);
  }

  ~encoded_ptr() {
    ptr_ = (T*)EncodePtr(nullptr);
  }

  // Makes it possible to call the function directly
  template<class... _Args>
  typename std::result_of<T*(_Args...)>::type operator()(_Args... _Ax) {
    T* fn = get();

    return fn(_Ax...);
  }

  T* get() const {
    return (T*)DecodePtr((LPVOID)ptr_);
  }

  bool is_set() {
    return (get() != nullptr);
  }

private:
  T* ptr_;
};

它按预期工作。例如:

encoded_ptr<decltype(MessageBoxA)> MsgBox;
MsgBox = &MessageBoxA; // Could also initialize in the constructor

// (HWND)0 is justified by the actual problem in the question
MsgBox((HWND)0, "Test message!", "Test", 0);

第一个问题是声明括号运算符()的方式不允许Visual Studio的IntelliSense发挥其魔力并给出关于函数参数的提示:

template<class... _Args>
typename std::result_of<T*(_Args...)>::type operator()(_Args... _Ax) {
  T* fn = get();

  return fn(_Ax...);
}

我没有使用(_Args... _Ax),而是想解压实际的函数参数,以便IntelliSense可以正确地给出提示。

目前的行为是:

Wrong

预期的行为是:

enter image description here

第二个问题是以这种方式调用函数,编译器不进行基本强制转换,迫使我将NULL强制转换为(void*)NULL0转换为(HWND)0等。当使用具有大量参数的函数时,这很烦人。

也许在实施中存在一些错误,但我不是模板专家。另外,我不知道问题的标题是否合适。

我感谢任何帮助。

修改

到目前为止我已尝试过(@ OlegBogdanov的建议):

template<class T, class... Args>
class encoded_ptr;

template<class T, class... Args>
class encoded_ptr<T(Args...)> {
public:

  typedef encoded_ptr<T> _Myt;

  using Fptr = T(*)(Args...);
  encoded_ptr(Fptr ptr) {
    ptr_ = (Fptr)EncodePtr((LPVOID)ptr);
  }

  // Makes it possible to call the function directly
  typename T operator()(Args... _Ax) {
    Fptr fn = get();

    return fn(std::forward<Args>(_Ax)...);
  }

  Fptr get() const {
    return (T*)DecodePtr((LPVOID)ptr_);
  }

  bool is_set() {
    return (get() != nullptr);
  }

private:
  Fptr ptr_;
};

结果:无法实例化/使用构造函数:Incomplete type is not allowed

修改

这是正确的方向,问题是调用惯例。

更改:

class encoded_ptr<T(Args...)>class encoded_ptr<T(__stdcall)(Args...)>

using Fptr = T(*)(Args...)using Fptr = T(__stdcall*)(Args...)

我正在尝试检测调用约定而不是硬编码。

1 个答案:

答案 0 :(得分:1)

我认为你的期望

template<class... _Args>
typename std::result_of<T*(_Args...)>::type operator()(_Args... _Ax) {
  T* fn = get();

  return fn(_Ax...);
}

错了。它完全忽略了你的目标函数参数列表(你使用了糟糕的芒类型擦除)和传递(我想使用单词&#39;转发&#39;但这将是不准确的)无论调用者给出什么。因此0在

MsgBox(0, "Test message!", "Test", 0);

推断为int,您必须将其强制转换为HWND,否则编译器无法猜测它。

你真正在做的是重新发明std::function或其上面的包装器。

如果您真的认为std::function不足以满足您的需求,您将不得不复制其部分实施,即您至少需要

    template<class R, class... Args>
    class encoded_ptr; // leaving this undefined

    template<class R, class... Args>
    class encoded_ptr<R(Args...)> {

    using Fptr = R(*)(Args...);
    encoded_ptr(Fptr ptr) {
        ptr_ = (T*)DecodePtr((LPVOID)ptr);
    }
    ...

捕获类型

中的参数列表

并且调用运算符将​​重用它而不是随机键入的传递参数:

// this must not be here -> template<class... _Args>
R operator()(Args... _Ax) {
  T* fn = get()

  return fn(std::forward<Args>(_Ax)...);
}

编辑:

您无法再存储T*T只是一种返回类型,按Fptr存储