根据C ++模板类型调用不同的C函数

我的问题如下:我有一个C库,它包含每个函数的几个版本,根据它们使用的数据类型,例如:

void add(double *a, double *b, double *c); 

 void sadd(float *a, float *b, float *c); 

现在,有了一个外部C ++模板函数,我希望能够做到这样的事情:

 template void myfunc(/*params*/) { // Obtain a,b,c of type T* from params /* If T is double call add(a,b,c); else if T is float call sadd(a,b,c). */ } 

我知道它可以通过专门的模板函数来完成,例如:

 template void myfunc(/*params*/) { // Obtain a,b,c of type double* from params add(a,b,c); } 

等等,但它并不是一个真正的选择,因为引入模板化C ++函数的重点是减少代码重复,并且“//从params获取a,b,c类型”部分可能非常长。

这个问题有一个简单的解决方案吗?

谢谢

兹德涅克

定义重载的C ++转发器:

 inline void forward_add(double *a, double *b, double *c) { add( a, b, c ); } inline void forward_add(float *a, float *b, float *c) { sadd( a, b, c ); } template void myfunc(/*params*/) { // Obtain a,b,c of type T* from params forward_add( a, b, c ); } 

在某个地方你必须告诉编译器saddadd是相关的。

一种方法是traits类模板struct math;

 template<> struct math { static void add(double *a, double *b, double *c) { return ::add(a, b, c); } }; template<> struct math { static void add(float*a, float*b, float*c) { return ::sadd(a, b, c); } }; 

在哪里使用它像:

 template void myfunc(/*params*/) { // Obtain a,b,c of type T* from params math::add( a, b, c ); } 

这有利于将所有基于类型的重构放在一个位置。

另一种方法是创建具有doublefloat重载的独立C ++函数。 这样做的优点和缺点是允许您的代码分布在多个位置。

 void math_add( double* a, double* b, double* c ) { add(a,b,c); } void math_add( float* a, float* b, float* c ) { sadd(a,b,c); } 

现在,假设您的所有函数共享相同的名称模式 – foo表示doublesfoo表示float 。 在这种情况下,基于文本的代码生成可用于简化上述“写入过载”代码中的一些。

这里唯一的问题是function的签名可能会有所不同。 如果只有少数几个简单的宏可以工作:

 #define MAKE_FUNCS( f ) \ void CONCAT( math_, f ) ( double* a, double* b, double * c ) { \ f ( a, b, c ); \ } \ void CONCAT( math_, f ) ( float* a, float* b, float* c ) { \ CONCAT( f, s ) ( a, b, c ); \ } 

然后只是为你试图以这种方式克隆的库中的每个函数发送MAKE_FUNCS

缺点(许多)是它只支持一组固定的签名。 我们可以通过完美转发来解决这个问题,这是一种C ++ 11技术:

 #define MAKE_FUNCS( f ) \ template< typename... Args >\ auto f ( Args&&... args ) \ -> decltype(::f ( std::forward(args)... )) \ { \ ::f ( std::forward(args)... ); \ } \ template< typename... Args >\ auto f ( Args&&... args ) \ -> decltype(:: CONCAT( f, s ) ( std::forward(args)... )) \ { \ :: CONCAT( f, s ) ( std::forward(args)... ); \ } 

但这会遇到SFINAE和相同的签名问题。 您可以通过显式表达式SFINAE解决此问题:

 #include  #include  #include  #include  #define CONCAT2( a, b ) a##b #define CONCAT( a, b ) CONCAT2(a,b) // SFINAE helper boilerplate: template struct is_type:std::true_type {}; template struct secret_enum { enum class type {}; }; template using EnableIf = typename std::enable_if< b, typename secret_enum::type >::type; // Macro that takes a srcF name and a dstF name and an integer N and // forwards arguments matching dstF's signature. An integer N must be // passed in with a distinct value for each srcF of the same name: #define FORWARD_FUNC( srcF, dstF, N ) \ template< typename... Args, \ EnableIf< is_type< \ decltype( dstF ( std::forward(std::declval())... )) \ >::value , N >... > \ auto srcF ( Args&&... args ) \ -> decltype(dstF ( std::forward(args)... )) \ { \ dstF ( std::forward(args)... ); \ } #define MAKE_FUNCS( f ) \ FORWARD_FUNC( f, ::f, 0 ) \ FORWARD_FUNC( f, :: CONCAT( f, s ), 1 ) void add( double* a, double* b, double* c) {*a = *b+*c;} void adds( float* a, float* b, float* c) {*a = *b+*c;} namespace math { MAKE_FUNCS(add) } int main() { double a, b = 2, c = 3; float af, bf = 3, cf = 5; math::add( &a, &b, &c ); math::add( &af, &bf, &cf ); std::cout << a << "=" << b << "+" << c << "\n"; std::cout << af << "=" << bf << "+" << cf << "\n"; } 

但正如你所看到的,这已经变得相当迟钝了,目前还没有很多编译器能够处理这种级别的C ++ 11。 (我认为以上内容应该在gcc 4.8和intel的最新编译,但不是MSVC或clang 3.2)

现在,您只需在库中使用每个函数,并执行一个由一堆单行样板组成的头文件:

 namespace mymath { MAKE_FUNCS( add ) MAKE_FUNCS( sub ) MAKE_FUNCS( chicken ) } #undef MAKE_FUNCS 

然后你通过说mymath::add而不是addmymath::add来调用adds

这也可以通过其他forms的文本代码生成来完成。