Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > runtime выбор типа


Автор: GoldFinch 20.9.2009, 14:48
есть набор структур с данными, для каждой структуры есть код который ее обрабатывает, для этого кода есть общий "контекст" (данные) в котором он работает
каждая структура имеет свой id, допустим целое число

по сети, или из файла, приходит буфер который содержит данные какой-то структуры, и id этой структуры
надо написать код который по полученному id определит какая структура лежит в буфере и выполнит код ее обработки.
(каждая структура имеет конструктор который загружает ее из буфера)

допустим, что "контекст" - это класс, код обработки - метод этого класса, перегруженный для каждой структуры.
(все можно сделать наоборот, например код обработки- метод структуры, и ему передается контекст)
Код

struct Foo {
  static const id_t id = 1;
  Foo(buf_t& buf) {/*код загрузки из буфера buf*/}
  /*данные*/
  .........
};
struct Bar {
  static const id_t id = 2;
  Bar(buf_t& buf) {/*код загрузки из буфера buf*/}
  /*данные*/
  .........
};
struct Context
{   
  /*данные*/
  ...........

  Process( Foo & data ) {/*код обработки*/}
  Process( Bar & data ) {/*код обработки*/}
  ............
};


я пока что придумал примерно три решения

1) switch
Код

void Dispatch(Context& ctx, id_t id, buf_t& buf)
{
  switch(id)
  {
  default: assert(!"unknown id");
  Foo::id : ctx.Process( Foo(buf) );
  Bar::id : ctx.Process( Bar(buf) );
  }
}


2) саморегистрирующиеся фабрики
Код

template<class Context>
struct Register
{
    template<class Processee>
    static bool Reg()
    {
        Map()[Processee::id] = &StartProcess<Processee>;
        return true;
    }

    typedef void(*StartProcess_t)(Context& ctx, buf_t& buf);
    
    template<class Processee>
    static void StartProcess(Context& ctx, buf_t& buf)
    {
        ctx.Process( Processee(buf) );
    }

    typedef std::map<id_t,StartProcess_t> map_t;
    static map_t& Map()
    {
        static map_t map_;
        return map_;
    }
};
template<class Context>
void Dispatch(Context& ctx, id_t id, buf_t& buf)
{
    Register<Context>::map_t map_=Register<Context>::Map();
    assert( map_.find(id) != map_.end() && "unknown id" );
    (map_[id])(ctx,buf);
}

#define REGISTER(name)  \
    struct Context;     \
    struct name;        \
    namespace trash{ bool BOOST_PP_CAT(reg,__LINE__) = Register<Context>::Reg<name>(); } \
    struct name         \
    /*******************/

REGISTER(Foo)
{
    static const int id = 1;
    Foo(buf_t){/*...*/}    
    /*...*/
};

REGISTER(Bar)
{
    static const int id = 2;
    Bar(buf_t){/*...*/}
    /*...*/
};

в этом варианте добавлять новые структуры удобно, все сосредоточенно в одном месте

3) списки типов
Код

template<typename AccepteesList>
void Dispatch(Context& ctx, id_t id, buf_t& buf)
{
  typedef boost::mpl::front<PacketsListTail>::type current_t;
  if( current_t::id == id )
    return ctx.Process( current_t(buf) );

  Dispatch<boost::mpl::pop_front<AccepteesList>::type>(ctx,id,buf);
}
template<>
void Dispatch<boost::mpl::list<>::type>(Context&, id_t, buf_t&)
{
  assert(!"unknown id");
}

..................
/* запуск Dispatch */
Dispatch<boost::mpl::list<Foo,Bar>::type>(ctx,id,buf);


Какие еще могут быть варианты?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)