Как сделать типизированный параметр пакета класса? - PullRequest
1 голос
/ 20 января 2011

Мне нужно упаковать фиксированное количество значений произвольных типов в классе. Затем я должен быть в состоянии передать каждый параметр через переключатель в соответствии с их типом. Типы параметров - это базовые типы 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 ]

1 голос
/ 27 августа 2011

Что касается последнего вашего комментария, я даю вам следующий ответ:)

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();

Конечно лучше, если каждая перегрузка будет обрабатываться сама.Я очень надеюсь, что мои ответы покроют ваши потребности.

0 голосов
/ 02 октября 2013

В идеальной реализации используется вариантный тип (например, 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 для достижения наиболее чистой и эффективной реализации.

0 голосов
/ 20 августа 2011

Другой способ, позволяющий избавиться от переключателя, - это использовать 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));
}

Поздравляем!

Если вы маньяк, то обсудим следующий подход.Как насчет создания статического декоратора (миксин) для автоматического выполнения следующих действий:

  • функции индексирования
  • функтор обработки объектов хранилища
  • , формирующий массив объектов
0 голосов
/ 30 июля 2011

В любом случае будет сразу один переключатель, если вы не будете использовать полиморфизм во время выполнения. Если вы будете использовать статический полиморфизм, вам понадобится либо специализация, либо переключатель.

Хороший подход к использованию отображения 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);

, чем использовать его для отображения типа enum:

int typeId = type2enum<Type1>();

или постоянное перечисление для типа:

typedef Enum2Type<ConstEnumType>::type_t some_t;

и для выполнения enum во время выполнения вы используете что-то вроде:

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");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...