如何创建参数对象

时间:2014-04-24 10:22:34

标签: c++ c++11 boost

在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;
...
}

将此类型选择代码包装到某个通用函数中的最简单方法是什么?或者,当我需要更改fg的一个参数时,如何让我的生活更轻松?例如:

invoke(type, f, arg1, arg2, arg3, ..., arg9);
invoke(type, g, arg1, arg2, arg3, ..., arg9);

fg的参数类型可能不一样。

更新

我在如何解决这个问题上添加了我自己的答案。

2 个答案:

答案 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)

谢谢大家的答案!