在Python中,我们可以创建一个“arguments对象”来调用任何接受这种参数序列的函数:
args = (42, True, "Hello")
f1(*args)
f2(*args)
我在C ++中遇到的问题是我有一个泛型函数f
,它有几个参数和16个不同的特化,我不得不通过读取一个值来明确告诉要调用哪个版本。 enum
:
switch (type) {
case xtype::BOOL:
f<bool>(arg1, arg2, arg3, ..., arg9);
break;
case xtype::INT8:
f<int8_t>(arg1, arg2, arg3, ..., arg9);
break;
case xtype::UINT8:
f<uint8_t>(arg1, arg2, arg3, ..., arg9);
break;
case xtype::INT16:
f<int16_t>(arg1, arg2, arg3, ..., arg9);
break;
...
case xtype::FLOAT:
f<float>(arg1, arg2, arg3, ..., arg9);
break;
}
我最近获得了另一个函数g
,我还需要按照相同的模式调用它:
switch (type) {
case xtype::BOOL:
g<bool>(arg1, arg2, arg3, ..., arg9);
break;
...
}
将此类型选择代码包装到某个通用函数中的最简单方法是什么?或者,当我需要更改f
或g
的一个参数时,如何让我的生活更轻松?例如:
invoke(type, f, arg1, arg2, arg3, ..., arg9);
invoke(type, g, arg1, arg2, arg3, ..., arg9);
f
和g
的参数类型可能不一样。
我在如何解决这个问题上添加了我自己的答案。
答案 0 :(得分:1)
使用可变参数模板,您可以构建类型列表并相应地分派
#include <iostream>
#include <stdexcept>
class TypeIdentifier
{
public:
typedef unsigned Integer;
enum Value
{
Bool = 1,
Int8,
UInt8,
Int32,
UInt32,
Int64,
UInt64,
Float,
Double,
String,
Unknown = 0
};
template <Value ...Ids> struct ListType {};
typedef ListType<
Bool,
Int8,
UInt8,
Int32,
UInt32,
Int64,
UInt64,
Float,
Double,
String,
// Always the last value:
Unknown
>
List;
public:
Integer id;
TypeIdentifier(Integer value = Unknown)
: id(value)
{}
template<typename Functor, typename ... T>
typename Functor::result_type dispatch(const Functor&, T&& ...);
};
// dispatch
// =============================================================================
namespace TypeIdentifierDispatch {
template <typename Functor, TypeIdentifier::Value I, TypeIdentifier::Value ... Ids> struct Evaluate;
template <typename Functor>
struct Evaluate<Functor, TypeIdentifier::Unknown> {
template <typename ... T>
static typename Functor::result_type
apply(TypeIdentifier::Integer id, const Functor&, T&& ... arguments) {
throw std::runtime_error("Unknown Type");
}
};
template <typename Functor, TypeIdentifier::Value I, TypeIdentifier::Value ... Ids>
struct Evaluate {
template <typename ... T>
static typename Functor::result_type
apply(TypeIdentifier::Integer id, const Functor& functor, T&& ... arguments) {
if(id == I) return functor(std::forward<T>(arguments) ...);
else return Evaluate<Functor, Ids...>::apply(id, functor, std::forward<T>(arguments) ...);
}
};
template <typename Functor, TypeIdentifier::Value ... Ids, typename ... T>
inline typename Functor::result_type
evaluate(
TypeIdentifier::Integer id,
const Functor& functor,
TypeIdentifier::ListType<Ids...>,
T&& ... arguments)
{
return Evaluate<Functor, Ids...>::apply(id, functor, std::forward<T>(arguments) ...);
}
} // namespace TypeIdentifierDispatch
template<typename Functor, typename ... T>
typename Functor::result_type TypeIdentifier::dispatch(const Functor& functor, T&& ... arguments) {
return TypeIdentifierDispatch::evaluate(
id,
functor,
TypeIdentifier::List(),
std::forward<T>(arguments) ...);
}
struct Add
{
typedef double result_type;
double operator () (double a, double b) const { return a + b; }
};
int main() {
TypeIdentifier id(TypeIdentifier::Int32);
std::cout << id.dispatch(Add(), 1.0, 1.5) << std::endl;
}
注意:在上面的代码中,涉及一个仿函数。您可以使用函数签名或std :: function。 kennytm写了一些有用的功能特征(见https://github.com/kennytm/utils/blob/master/traits.hpp)
答案 1 :(得分:0)
我最终使用普通的旧宏来解决我的代码重复问题:
#define XYZ_DISPATCH_BY_XTYPE(F, T, ...) \
switch(T) \
{ \
case xtype::BOOL: \
F<bool>(__VA_ARGS__); \
break; \
case xtype::FLOAT: \
F<float>(__VA_ARGS__); \
break; \
....
添加新功能相对容易:
XYZ_DISPATCH_BY_XTYPE(f1, type, a0, a1, a2, ..., a9)
XYZ_DISPATCH_BY_XTYPE(g1, type, q0, q1, q2, ..., q9)
谢谢大家的答案!