怎么做一个类型化的参数包类?

时间:2011-01-20 14:14:33

标签: c++ templates

我需要在一个类中打包固定数量的任意类型的值。然后我需要能够根据类型通过交换机传递每个参数。参数的类型是基本的C类型和指向东西的指针(特定和有限数量的“东西”),所以没有什么复杂的。

“参数”类需要很轻(在空间和处理中)。

这是我需要如何使用它的一个例子:

void MyFunc( const Parameters &Params )
{
// for loop
switch( Params(0).GetType() ) {
  case MY_INT_TYPE: int ValInt = Params(0).Get<int>(); ...
  case MY_PTR_TO_MY_STUFF1: MyStuff1 *ValS1 = Params(0).Get<MyStuff1*>(); ...
  ...
  }
}

Parameters MyParams(2);
MyParams.Set<int>(0, 123);
MyParams.Set<MyStuff1*>(1, &SomeClassInstance);
MyFunc( MyParams );
...
MyParams.Set<float>(0, 123.456);  // The same variable in the same scope
MyParams.Set<int*>(1, &Val);
MyFunc( MyParams );

当然,我可以手动专门针对所有类型并存储在一个联合中,这就是蛮力方法。我一直在想有一种更简单的方法可以做到这一点,但无法弄清楚。我可以使用类型特征来存储类型信息,但我仍然坚持使用值。并且铸造价值不是一种选择。

任何指针(比喻指针)?

4 个答案:

答案 0 :(得分:1)

关于上一次你的评论,我给你下一个答案:)

class ParamsProcessor
{
    // custom parameter reciever for type T1
    template<>
    public ParamsProcessor& in<T1>(const T1& o)
    { ... } 

    // custom parameter reciever for type T2
    template<>
    public ParamsProcessor& in<T2>(T2 t2)
    { ... } 

    // default parameter reciever
    template<class T>
    public ParamsProcessor& operator in(const T& o)
    {
       // some generic way if the project allows
       ...
       return *this
    } 

    void Process()
    { ... }
};


ParamsProcessor pp;
pp.in<T1>(0,123)
  .in<T2>(0,123.456)
  .in("asds");
  .Process();

如果每个重载都会自行处理,那么肯定会更好。 我真的希望我的答案能满足你的需求。

答案 1 :(得分:0)

如果不使用运行时多态,那么无论如何都会有一个开关。如果您将使用静态多态,则需要专门化或切换。

使用type2enum和enum2type映射的好方法:

type2enum:

// preparition
template<class T> type2enum();
#define TYPE2ENUM_SPEC(TYPE, ENUM) \
template<> type2enum<TYPE>() \
{ return ENUM; }

enum { T1enum, T2enum, T3enum }
TYPE2ENUM_SPEC(type1_t, T1enum);
TYPE2ENUM_SPEC(type2_t, T2enum);
TYPE2ENUM_SPEC(some_third_type_t, T3enum);

和向后enum2type:

// preparition
template<int Enum>
struct enum2type;
#define ENUM2TYPE_SPEC(ENUM, TYPE) \
template<> struct Enum2Type<ENUM> \
{ typedef TYPE type_t; }


// and generic macro
#define CREATE_TYPE_MAPPING(TYPE, INTEGER) \
#define TYPE2ENUM_SPEC(TYPE, INTEGER) \
#define ENUM2TYPE_SPEC(INTEGER, TYPE)

和代码示例:

我们有Type1,Type2,Type3和enum:

enum {T1, T2, T3};

CREATE_TYPE_MAPPING(Type1, T1);
CREATE_TYPE_MAPPING(Type2, T2);
CREATE_TYPE_MAPPING(Type3, T3);

用于将映射类型用于枚举:

int typeId = type2enum<Type1>();
输入

或常量枚举:

typedef Enum2Type<ConstEnumType>::type_t some_t;

并且对于运行时枚举类型,您可以使用以下内容:

template<class ObjT>
void DoWithTypeIdValue(int enumValue, const ObjT& obj)
{
    switch(enumValue)
    {
       case enumType1:
           obj.do<Enum2Type<enumType1>::type_t>();
           break;
       case enumType2:
           obj.do<Enum2Type<enumType2>::type_t>();
           break;
       case enumType3:
           obj.do<Enum2Type<enumType3>::type_t>();
           break;
       default:
           assert(!"Unknown type constant");
    }
}

答案 2 :(得分:0)

允许摆脱开关的另一种方法是使用enum和数组函数:

enum TheTypes { T1, T2, T3, TheTypesCount};

boost::function<void (const Parameters &Params)> Processors[] = {
    boost::bind(&T1::ProcessingMethod, t1Obj, _1),
    boost::bind(&T2::ProcessingStaticMethod, _1),
    boost::bind(RawFunction, _1)
};

这允许您用简单的构造替换侵入式缝合:

void MyFunc( const Parameters &Params )
{
    // for loop
    Processors[Params(0).GetType()](Params(0));
}

Congratulaions!

如果你是疯子而不是讨论下一步的方法。 如何创建静态装饰器(mixin)以自动执行以下操作:

  • 索引功能
  • 商店功能处理函子
  • 形成一系列特征

答案 3 :(得分:0)

理想的实现方式是使用由有效的参数类型集参数化的变体类型(例如boost::variant)。以下是如何使用变体类型来解决问题的示例:

typedef Variant<int, MyStuff1*> Parameter;

int main()
{
    MyStuff1 SomeClassInstance;

    Parameter MyParams[2] = { 123, &SomeClassInstance };

    int ValInt = MyParams[0].Get<int>();
    MyStuff1* ValS1 = MyParams[1].Get<MyStuff1*>();
}

如果您不能使用boost,那么这是一个如何实施变体类型的示例。

#include <type_traits>

template<typename T, typename U>
struct MaxSize
{
    static const size_t value = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U);
};

struct ValueBase
{
    virtual ~ValueBase()
    {
    }
};

template<typename T>
struct ValueGeneric : ValueBase
{
    T value;
    ValueGeneric(const T& value) : value(value)
    {
    }
};

template<typename T1, typename T2>
class Variant
{
    typename std::aligned_union<MaxSize<T1, T2>::value, T1, T2>::type storage;

    Variant(const Variant&); // not copyable
    Variant operator=(const Variant&); // not assignable
public:
    template<typename T>
    Variant(const T& value)
    {
        new(&storage) ValueGeneric<T>(value);
    }
    ~Variant()
    {
        static_cast<ValueBase*>(static_cast<void*>(&storage))->~ValueBase();
    }

    void Set(const T1& value)
    {
        SetImpl(value);
    }
    void Set(const T2& value)
    {
        SetImpl(value);
    }
    void Get(const T1& value)
    {
        this->~Variant();
        new (this) Variant(value);
    }
    template<typename T>
    bool IsA() const
    {
        return typeid(T) == typeid(*static_cast<const ValueBase*>(static_cast<const void*>(&storage)));
    }
    template<typename T>
    T& Get()
    {
        assert(IsA<T>());
        return *static_cast<T*>(static_cast<void*>(&storage));
    }
    template<typename T>
    const T& Get() const
    {
        assert(IsA<T>());
        return *static_cast<const T*>(static_cast<const void*>(&storage));
    }

private:
    template<typename T>
    void SetImpl(const T& value)
    {
        this->~Variant();
        new (this) Variant(value);
    }
};

此实现仅作为示例,仅支持两种类型 - 扩展以支持更多类型相对容易。您可能希望使用template-parameter-packs和move-constructors来实现最干净,最有效的实现。