考虑以下代码,我在调用特定模板函数computecost
,具体取决于枚举值(类别)。在调用案例中,computecost
的参数是相同的。枚举值和C ++类型之间存在一对一的对应关系。由于computecost
的参数在所有调用中始终相同,因此可以更紧凑地编写以下代码,即。不重复每个类型/枚举值。
mxClassID category = mxGetClassID(prhs);
switch (category) {
case mxINT8_CLASS: computecost<signed char>(T,offT,Offset,CostMatrix); break;
case mxUINT8_CLASS: computecost<unsigned char>(T,offT,Offset,CostMatrix); break;
case mxINT16_CLASS: computecost<signed short>(T,offT,Offset,CostMatrix); break;
case mxUINT16_CLASS: computecost<unsigned short>(T,offT,Offset,CostMatrix); break;
case mxINT32_CLASS: computecost<signed int>(T,offT,Offset,CostMatrix); break;
case mxSINGLE_CLASS: computecost<float>(T,offT,Offset,CostMatrix); break;
case mxDOUBLE_CLASS: computecost<double>(T,offT,Offset,CostMatrix); break;
default: break;
}
答案 0 :(得分:4)
你可以有一个函数接受category
并返回一个合适的函数指针,然后用适当的参数调用它:
decltype(&computecost<int>) cost_computer(mxClassID const category) {
switch (category) {
case mxINT8_CLASS: return &computecost<signed char>;
...
}
}
cost_computer(mxGetClassID(prhs))(T, offT, Offset, CostMatrix);
或使用map
,正如马克建议的那样:
std::map<mxClassID, decltype(&computecost<int>)> compute_functions =
boost::assign::map_list_of
(mxINT8_CLASS, &computecost<signed char>)
// ... and so on
compute_functions[mxGetClassID(prhs)](T, offT, Offset, CostMatrix);
答案 1 :(得分:2)
首先,这对我来说有点时髦(类型代码总是吓到我一点)......感觉这应该是prhs对象中的某种虚函数:。
然后你的代码看起来像这样
prhs->computecost(T, offT, Offset, CostMatrix );
如果无法将computecost
转移到成员虚拟函数中,那么你将会遇到代码中某个地方的丑陋开关构造......但是如果你发现自己在重复做同样的事情和/或发现它使代码部分混乱,然后将其提升为辅助函数
void computecost( mxClassID category, /* all the other args go here */ )
{
switch (category) {
case mxINT8_CLASS: computecost<signed char>(T,offT,Offset,CostMatrix); break;
case mxUINT8_CLASS: computecost<unsigned char>(T,offT,Offset,CostMatrix); break;
case mxINT16_CLASS: computecost<signed short>(T,offT,Offset,CostMatrix); break;
case mxUINT16_CLASS: computecost<unsigned short>(T,offT,Offset,CostMatrix); break;
case mxINT32_CLASS: computecost<signed int>(T,offT,Offset,CostMatrix); break;
case mxSINGLE_CLASS: computecost<float>(T,offT,Offset,CostMatrix); break;
case mxDOUBLE_CLASS: computecost<double>(T,offT,Offset,CostMatrix); break;
default: break;
}
}
然后你的代码看起来像这样:
mxClassID category = mxGetClassID(prhs);
computecost(category, T,offT,Offset,CostMatrix );
答案 2 :(得分:1)
由于每个模板化函数都被编译器视为不同的函数,因此无法避免对每个模板执行不同的调用。您可以通过创建函数指针表来简化,因为每个函数都具有相同的签名。
答案 3 :(得分:1)
您有没有理由不为computecost
函数使用动态调度?
最简单的方法是创建一个继承层次结构,只使用动态调度。层次结构中将mxINT8_CLASS
作为类ID返回的每个类型都会将computecost
实现为对computecost<signed char>
的调用,对所有其他组合也类似。
如果有充分的理由不使用动态调度,您可以考虑以不同的方式实现自己的动态调度。最明显,最简单,可能更容易维护的是你已经拥有的。使用宏可以完成更复杂的操作,或者您可以尝试模板化版本,只是为了好玩......
宏解决方案(复杂性中的下一个)可以使用宏来定义关系,另一个用于定义每个case
,然后将它们组合起来:
#define FORALL_IDS( macro ) \
macro( mxINT8_CLASS, signed char ); \
macro( mxUINT8_CLASS, unsigned char ); \
// ...
#define CASE_DISPATCH_COMPUTECOST( value, type ) \
case value: computecost<type>( T, offT, Offset, CostMatrix ); break
联合:
switch ( category ) {
FORALL_IDS( CASE_DISPATCH_COMPUTECOST );
};
我在过去已经看过这个,并且不喜欢它,但是如果有很多地方你需要从类别映射到类型,这可能是一个简单易于编写难以维护的解决方案。另请注意,FORALL_IDS
宏可用于实现从枚举映射到类型的元编程特征,反之亦然:
template <classId id>
struct type_from_id;
#define TYPE_FROM_ID( id, T ) \
template <> struct type_from_id<id> { typedef T type; }
FORALL_IDS( TYPE_FROM_ID );
#undef TYPE_FROM_ID
template <typename T>
struct id_from_type;
#define ID_FROM_TYPE( id, T ) \
template <> struct id_from_type<T> { static const classId value = id; }
FORALL_IDS( ID_FROM_TYPE );
#undef ID_FROM_TYPE
请注意,这有很多缺点:宏本质上是不安全的,而这个宏更是如此,因为它们定义类型并且行为不像函数,所以很难找到合适的参数的括号数量,这使得它更容易出现文本替换中的所有错误......宏不了解上下文,因此您可能希望尝试通过在使用后立即取消它们来最小化范围。实现上面的特征是一个很好的方法:创建宏,使用它来生成模板化的非宏代码,取消宏。其余的代码可以使用模板而不是宏来映射一个到另一个。
实现动态分派的另一种方法是使用查找表而不是上面的switch
语句:
typedef T function_t( T1, T2, T3 ); // whatever matches the `computecost` signature
function_t *lookup[ categories ]; // categories is 1+highest value for the category enum
您可以手动初始化查找表,或者您可以使用上面的宏,代码的复杂性不会发生太大变化,只需从调用端移动到初始化查找表的位置。在呼叫者方面,你会这样做:
lookup[ mxGetClassID(prhs) ]( T, offT, Offset, CostMatrix );
而不是switch
语句,但不要被愚弄,成本没有被删除,只是推送到初始化(如果你需要映射多个函数,这可能是好的,你可以创建一个函数指针结构并一次执行所有的初始化,并且您有自己的手动定制vtable
,而不是vptr
,而是使用classId
字段进行索引。
这个模板化的版本可能是最麻烦的。我会尝试实现它的乐趣,但不是真的在生产代码中使用它。您可以尝试building the lookup table from a template [1] ,这很有趣,但可能比原始问题更复杂。
或者,您可以实现 type-list 类型的方法(A la Modern C++ Design),并在每个节点中调度到相应的函数。这可能不值得花费,并且将来会成为一个噩梦,所以远离生产代码。
总结一下:
只需使用语言动态调度,这是您的最佳选择。如果没有令人信服的理由,请平衡不同的选项和复杂性。根据您需要执行从classId到X的调度的位置(此处X为computecost
,但可能还有更多内容),请考虑使用手工定制的查找表来封装所有X
个操作到功能表 - 注意,此时,无论避免vtable
的动机可能已经消失:您手动,容易出错实施了同样的野兽!
[1] 由于从枚举到类型的映射,这种情况下的复杂性略高,但它不应该复杂得多。
答案 4 :(得分:0)
这就是宏的用途。
#define COMPUTECOST(X) computecost<X>(T, offT, Offset, CostMatrix)
case mxINT8_CLASS: COMPUTECOST(signed char); break;
case mxUINT8_CLASS: COMPUTECOST(unsigned char); break;
...etc...
为您省略一些重复输入,但您仍然需要单独调用每个。