【问题标题】:Function pointer as template argument: Can the calling convention (cdecl/stdcall) be deducted?函数指针作为模板参数:调用约定(cdecl/stdcall)可以扣除吗?
【发布时间】:2020-08-31 10:33:44
【问题描述】:

我有这样的课

/**
  Some third party libraries with C interfaces have their own set of functions to allocate and free 
  heap memory buffers aligned to the specification of the library. This class helps to manage such
  buffers in a convenient RAII style

  @tparam Type The type of data to hold in the buffer
  @tparam SizeType The type used for sizes (in most cases either int or size_t)
  @tparam allocFunction A function to call that takes the number of bytes to allocate as argument and returns a pointer
                        of Type* to the allocated memory
  @tparam freeFunction A function to call that takes a void* pointer and frees the previously allocated memory
 */
template <typename Type, typename SizeType, Type* (*allocFunction)(SizeType), void (*freeFunction)(void*)>
class GenericScopedBuffer

这一直很好,直到我现在尝试更新我们仍然构建为 32 Windows 版本的旧产品之一。我们使用一些库,其分配/解除分配函数定义为__stdcall 类型并将它们传递给此类模板,例如

GenericScopedBuffer<char, int, stdcallAllocFunction, stdcallFreeFunction>

导致像C2440: 'specialization': cannot convert from 'void (__stdcall *)(void *)' to 'void (__cdecl *)(void *)'这样的编译器错误

显而易见的原因是 x86 编译器假定像这样声明的函数指针作为指向 __cdecl 函数的指针。现在,由于这个类模板实际上应该与函数指针一起使用任何类型的函数,我想知道是否有办法从传入的函数中扣除调用约定类型。但是,__stdcall 或 __cdecl 不是真正的类型,而更像是提示编译器,对吧?

那么,以跨平台和通用方式启用此功能的好方法是什么?

【问题讨论】:

  • 假设您的默认约定是cdecl,当您编写函数模板时,它会自动添加__cdecl__cdecl__stdcall 不仅仅是提示,而是构建和调用函数的方式,这就是为什么你不能在 cdecl 函数和 stdcall 函数之间转换而不会出现一些堆栈错误。相反,您可以使用简单的typename 并使用std::is_function 进行检查。
  • 是的,我知道__cdecl__stdcall 之间的根本区别。但是,“您可以使用简单的类型名并使用 std::is_function 进行检查”是什么意思。您能提供一些简短的演示代码供我详细说明吗?

标签: templates c++14 calling-convention


【解决方案1】:

void (__stdcall *)(void *)void (__cdecl *)(void *) 就 MSVC 而言是两种不同的类型。这些限定符是一种指定语言链接的预标准方式。回想一下,extern "C" 函数不同于 extern "C++" 函数,后者又不同于 extern "FORTRAN" 函数,尽管有些编译器允许您将它们混在一起。

有几种方法可以解决这个问题。

您可以针对 __cdecl 函数专门化您的模板。

你可以告诉编译器你不在乎类型是什么:

template <auto allocFunction, auto freeFunction> class GenericScopedBuffer 

如果您无法访问现代版本的 C++,您可以按函数类型进行参数化:

template <typename AllocFuncT, AllocFuncT allocFunction, 
          typename FreeFuncT, FreeFuncT freeFunction> class GenericScopedBuffer

但是当然你必须手动指定类型。您可以为此使用decltype,可能还结合了一个愚蠢的宏,例如

#define TV(x) decltype(x), (x)

【讨论】:

  • 谢谢。不幸的是,我们使用 C++14 并且自动模板参数是 C++17 的一个特性——我更新了原始问题的标签,否则这将是我选择的解决方案。 “愚蠢的宏”解决方案似乎是一种可行的方法,尽管它取决于一个愚蠢的宏:D——我更愿意避免
猜你喜欢
  • 1970-01-01
  • 2013-07-11
  • 1970-01-01
  • 2023-03-20
  • 2015-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多