我的问题如下: 我有一个C库,它包含每个函数的几个版本,根据它们使用的数据类型,例如:
void add(double *a, double *b, double *c);
和
void sadd(float *a, float *b, float *c);
现在,拥有一个外部C ++模板函数,我希望能够做到这样的事情:
template<class T>
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<double>(/*params*/)
{
// Obtain a,b,c of type double* from params
add(a,b,c);
}
等等,但它实际上并不是一个选项,因为引入模板化C ++函数的重点是减少代码重复和&#34; //从params&#获取类型T *的a,b,c 34;部分可能很长。
这个问题有一个简单的解决方案吗?
由于
兹德涅克
答案 0 :(得分:9)
定义重载的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<class T>
void myfunc(/*params*/)
{
// Obtain a,b,c of type T* from params
forward_add( a, b, c );
}
答案 1 :(得分:1)
在某处您必须告诉编译器sadd
和add
是相关的。
一种方法是特质类 模板 struct math;
template<>
struct math<double> {
static void add(double *a, double *b, double *c) {
return ::add(a, b, c);
}
};
template<>
struct math<float> {
static void add(float*a, float*b, float*c) {
return ::sadd(a, b, c);
}
};
你在哪里使用它:
template<class T>
void myfunc(/*params*/)
{
// Obtain a,b,c of type T* from params
math<T>::add( a, b, c );
}
这有利于将所有基于类型的重构放在一个位置。
另一种方法是创建具有double
和float
重载的独立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
为double
,sfoo
为float
。在这种情况下,基于文本的代码生成可用于简化上述“写入重载”代码。
这里唯一的问题是功能的签名可能会有所不同。如果只有少数几个简单的宏可以工作:
#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>(args)... )) \
{ \
::f ( std::forward<Args>(args)... ); \
} \
template< typename... Args >\
auto f ( Args&&... args ) \
-> decltype(:: CONCAT( f, s ) ( std::forward<Args>(args)... )) \
{ \
:: CONCAT( f, s ) ( std::forward<Args>(args)... ); \
}
但这会遇到SFINAE和相同的签名问题。您可以通过显式表达式SFINAE来修复此问题:
#include <utility>
#include <type_traits>
#include <cstddef>
#include <iostream>
#define CONCAT2( a, b ) a##b
#define CONCAT( a, b ) CONCAT2(a,b)
// SFINAE helper boilerplate:
template<typename T> struct is_type:std::true_type {};
template<std::size_t n> struct secret_enum { enum class type {}; };
template<bool b, std::size_t n>
using EnableIf = typename std::enable_if< b, typename secret_enum<n>::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<Args>(std::declval<Args>())... )) \
>::value , N >... > \
auto srcF ( Args&&... args ) \
-> decltype(dstF ( std::forward<Args>(args)... )) \
{ \
dstF ( std::forward<Args>(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
而不是add
或adds
来致电。
这也可以通过其他形式的文本代码生成来完成。