Фабрика классов составных шаблонов C ++ - PullRequest
2 голосов
/ 11 ноября 2009

Можно ли создать фабрику составных шаблонов классов без указания вручную всех комбинаций? Я имею в виду, если у меня есть эти классы:

class CompositeBase {};

template< typename C1, typename C2, typename C3 >
class Composite : public CompositeBase
{
private:
    C1 component1;
    C2 component2;
    C3 component3;
};

class Component0 {}; //Also have Component1-9

Я хотел бы создать такую ​​функцию:

CompositeBase *CreateComposite(int c1, int c2, int c3);

так что

CreateComposite(4,3,7);

создаст и вернет

Composite<Component4,Component3,Component7>

Причина этого заключается в том, что я могу загружать данные из файла и создавать различные составные объекты. Файл будет иметь три значения для компонентов для создания каждого составного объекта, а затем другие необходимые ему данные.

Проблема в 10 различных компонентах, существует 1000 различных возможных составных классов. Чтобы указать все из них, потребуется оператор switch для 1-го компонента, 10 операторов switch для второго компонента в этом компоненте и 100 операторов switch для третьего компонента в пределах этих десяти, а функция будет иметь длину более 1000 строк.

Есть ли другой способ написания функции CreateComposite? Кроме этого:

CompositeBase *CreateComposite(int c1, int c2, int c3)
{
   switch(c1)
   {
   case 0:
       switch( c2 )
       {
       case 0:
           switch( c3 )
           {
              case 0: return new Composite<Component0,Component0,Component0>;
              case 1: return new Composite<Component0,Component0,Component1>;
              //etc
           }
           //etc
       }
       //etc
   }
}

Ответы [ 6 ]

3 голосов
/ 11 ноября 2009

Аргументы шаблона должны быть известны во время компиляции. Возможное решение будет: CreateComposite<int, int, int>(), и вы можете специализировать его для каждого возможного случая. О, хорошо: это скорее не ходи .. Я бы посоветовал вам пойти с некоторым старомодным динамическим полиморфизмом и std::vector<ComponentBase>.

3 голосов
/ 11 ноября 2009

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

Рассматривали ли вы использование композиции вместо наследования?

Редактировать: По составу я подумал о следующем:


class Composite
{
private:
    ComponentBase * component1;
    ComponentBase * component2;
    ComponentBase * component3;
};
class Component0 : public ComponentBase {}; //Also have Component1-9

Если по какой-то причине вы не можете использовать указатели на базовый класс, вам не хватает кода для генерации всех перестановок шаблонных классов. Но вместо использования вложенных операторов case вы можете использовать таблицу для создания новых экземпляров и макросы для упрощения кода.

typedef CompositeBase * NewComposite();
#define NEW_COMPOSITE(P1,P2,P3) CompositeBase * NewComposite##P1##P2##P3() { return new Composite<Component##P1,Component##P2,Component##P3>; }
NEW_COMPOSITE(0,0,0) NEW_COMPOSITE(0,0,1) NEW_COMPOSITE(0,0,2) NEW_COMPOSITE(0,0,3)
NEW_COMPOSITE(0,0,4) ...
...
CompositeBase *CreateComposite(int c1, int c2, int c3)
{
    static NewComposite * newCompositeTable[10][10][10] = {{{NewComposite000,NewComposite001,...},{NewComposite010,...}}};
    return newCompositeTable[c1][c2][c3]();
}

Я не могу сказать, что это лучший подход, чем тот, с которого вы начали, но это альтернатива.

2 голосов
/ 12 ноября 2009

Вы можете избежать сложности O (n ^ 3) для O (n), используя методы каскадного шаблона с одним переключателем в нем:

#include <iostream>
using namespace std;

class CompositeBase
{
public:
    virtual void print( std::ostream& o_out ) = 0;
};

template< typename C1, typename C2, typename C3 >
class Composite : public CompositeBase
{
public:
    void print( std::ostream& o_out )
    {
        o_out << typeid(*this).name();
    }
private:
    C1 component1;
    C2 component2;
    C3 component3;
};


class Component0 {};
class Component1 {};
class Component2 {};
class Component3 {};
class Component4 {};
class Component5 {};
class Component6 {};
class Component7 {};
class Component8 {};
class Component9 {};

template<typename C1,typename C2,typename C3>
CompositeBase *CreateComposite0()
{
    return new Composite<C1,C2,C3>();
}

template<typename C1,typename C2>
CompositeBase *CreateComposite1(int c3)
{
    switch(c3)
    {
    case 0: return CreateComposite0<C1,C2,Component0>();
    case 1: return CreateComposite0<C1,C2,Component1>();
    case 2: return CreateComposite0<C1,C2,Component2>();
    case 3: return CreateComposite0<C1,C2,Component3>();
    case 4: return CreateComposite0<C1,C2,Component4>();
    case 5: return CreateComposite0<C1,C2,Component5>();
    case 6: return CreateComposite0<C1,C2,Component6>();
    case 7: return CreateComposite0<C1,C2,Component7>();
    case 8: return CreateComposite0<C1,C2,Component8>();
    case 9: return CreateComposite0<C1,C2,Component9>();
    default: return 0;
    }
}

template<typename C1>
CompositeBase *CreateComposite2(int c2, int c3)
{
    switch(c2)
    {
    case 0: return CreateComposite1<C1,Component0>(c3);
    case 1: return CreateComposite1<C1,Component1>(c3);
    case 2: return CreateComposite1<C1,Component2>(c3);
    case 3: return CreateComposite1<C1,Component3>(c3);
    case 4: return CreateComposite1<C1,Component4>(c3);
    case 5: return CreateComposite1<C1,Component5>(c3);
    case 6: return CreateComposite1<C1,Component6>(c3);
    case 7: return CreateComposite1<C1,Component7>(c3);
    case 8: return CreateComposite1<C1,Component8>(c3);
    case 9: return CreateComposite1<C1,Component9>(c3);
    default: return 0;
    }
}

CompositeBase *CreateComposite(int c1,int c2, int c3)
{
    switch(c1)
    {
    case 0: return CreateComposite2<Component0>(c2,c3);
    case 1: return CreateComposite2<Component1>(c2,c3);
    case 2: return CreateComposite2<Component2>(c2,c3);
    case 3: return CreateComposite2<Component3>(c2,c3);
    case 4: return CreateComposite2<Component4>(c2,c3);
    case 5: return CreateComposite2<Component5>(c2,c3);
    case 6: return CreateComposite2<Component6>(c2,c3);
    case 7: return CreateComposite2<Component7>(c2,c3);
    case 8: return CreateComposite2<Component8>(c2,c3);
    case 9: return CreateComposite2<Component9>(c2,c3);
    default: return 0;
    }
}

int main()
{
    CompositeBase* base1 = CreateComposite(4,5,6);
    CompositeBase* base2 = CreateComposite(8,2,0);
    base1->print(cout);
    cout << endl;
    base2->print(cout);
    return 0;
}

Просто для удовольствия вы можете использовать буст-пропроцессор для сложности O (1)

#include <iostream>
#include <boost/preprocessor/repetition.hpp>
using namespace std;

class CompositeBase
{
public:
    virtual void print( std::ostream& o_out ) = 0;
};

template< typename C1, typename C2, typename C3 >
class Composite : public CompositeBase
{
public:
    void print( std::ostream& o_out )
    {
        o_out << typeid(*this).name();
    }
private:
    C1 component1;
    C2 component2;
    C3 component3;
};

#define DIM 10

#define COMPONENT_DECLARATION(z,n,unused) class BOOST_PP_CAT(Component,n) {};
BOOST_PP_REPEAT(DIM,COMPONENT_DECLARATION, ~)
#undef COMPONENT_DECLARATION

template<typename C1,typename C2,typename C3>
CompositeBase *CreateComposite0()
{
    return new Composite<C1,C2,C3>();
}

template<typename C1,typename C2>
CompositeBase *CreateComposite1(int c3)
{
#define COMPOSITE(z,n,unused) case n: return CreateComposite0<C1,C2,BOOST_PP_CAT(Component,n)>();
    switch(c3)
    {
BOOST_PP_REPEAT(DIM,COMPOSITE,~)
    default: return 0;
    }
#undef COMPOSITE
}

template<typename C1>
CompositeBase *CreateComposite2(int c2, int c3)
{
#define COMPOSITE(z,n,unused) case n: return CreateComposite1<C1,BOOST_PP_CAT(Component,n)>(c3);
    switch(c2)
    {
BOOST_PP_REPEAT(DIM,COMPOSITE,~)
    default: return 0;
    }
#undef COMPOSITE
}

CompositeBase *CreateComposite(int c1,int c2, int c3)
{
#define COMPOSITE(z,n,unused) case n: return CreateComposite2<BOOST_PP_CAT(Component,n)>(c2,c3);
    switch(c1)
    {
BOOST_PP_REPEAT(DIM,COMPOSITE,~)
    default: return 0;
    }
#undef COMPOSITE
}

#undef DIM

int main()
{
    CompositeBase* base1 = CreateComposite(4,5,6);
    CompositeBase* base2 = CreateComposite(8,2,0);
    base1->print(cout);
    cout << endl;
    base2->print(cout);
    return 0;
}

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

0 голосов
/ 12 ноября 2009

Зачем вам вообще нужен универсальный класс Composite с 3 членами совершенно произвольных типов? Я не могу представить, что вы можете написать какой-нибудь полезный код для такого класса в этой ситуации. Сам Composite не может содержать никакого полезного кода, и любой код, который использует композит, должен знать, какие типы в нем (которые вы в настоящее время нигде не храните).

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

0 голосов
/ 11 ноября 2009

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

#include <stdexcept>
#include <iostream>

struct CompositeBase
{
    virtual ~CompositeBase () = 0 {}
    virtual std::ostream& output (std::ostream& os) const = 0;
};

template<typename CA, typename CB, typename CC>
struct Composite : public CompositeBase
{
    std::ostream& output (std::ostream& os) const
    {
        return os << componentA.id () << "," << componentB.id () << "," << componentC.id ();
    }

    CA componentA;
    CB componentB;
    CC componentC;
};

struct Component0 {int id () const {return 0;}};
struct Component1 {int id () const {return 1;}};
struct Component2 {int id () const {return 2;}};
struct Component3 {int id () const {return 3;}};
struct Component4 {int id () const {return 4;}};
// ...

template<int N> struct IntToType {};
template<> struct IntToType<0> {typedef Component0 Type;};
template<> struct IntToType<1> {typedef Component1 Type;};
template<> struct IntToType<2> {typedef Component2 Type;};
template<> struct IntToType<3> {typedef Component3 Type;};
template<> struct IntToType<4> {typedef Component4 Type;};
// ...

// Change 4 to match number of composites.
template<int N1 = 4, int N2 = 4, int N3 = 4>
struct CompositeFactory
{
    static CompositeBase* create (int c1, int c2, int c3)
    {
        if (c1 == N1)
        {
            if (c2 == N2)
            {
                if (c3 == N3)
                    return new Composite<IntToType<N1>::Type, IntToType<N2>::Type, IntToType<N3>::Type>;
                else
                    return CompositeFactory<N1, N2, N3-1>::create (c1, c2, c3);
            }
            else
                return CompositeFactory<N1, N2-1, N3>::create (c1, c2, c3);
        }
        else
            return CompositeFactory<N1-1, N2, N3>::create (c1, c2, c3);
    }
};

template<int N2, int N3>
struct CompositeFactory<-1, N2, N3>
{
    static CompositeBase* create (int c1, int c2, int c3)
    {
        throw std::runtime_error ("Could not create Composite");
    }
};

template<int N1, int N3>
struct CompositeFactory<N1, -1, N3>
{
    static CompositeBase* create (int c1, int c2, int c3)
    {
        throw std::runtime_error ("Could not create Composite");
    }
};

template<int N1, int N2>
struct CompositeFactory<N1, N2, -1>
{
    static CompositeBase* create (int c1, int c2, int c3)
    {
        throw std::runtime_error ("Could not create Composite");
    }
};

CompositeBase* createComposite (int c1, int c2, int c3)
{
    return CompositeFactory<>::create (c1, c2, c3);
}

int main (int argc, char* argv[])
{
    CompositeBase* comp = createComposite (4,1,2);

    comp->output (std::cout);

    return 0;
}

Это работает для 5 типов, и должно быть очевидно, как его расширить до 10 типов.

Я не уверен, что когда-нибудь захочу использовать это на самом деле - для 10 композитов компилятор сгенерирует все 1000 экземпляров шаблона Composite.

0 голосов
/ 11 ноября 2009

Неа. Такое потребовало бы отражения, которого нет в C ++.

Почему не просто CreateComposite<T1, T2, T3>()?

РЕДАКТИРОВАТЬ: звучит как случай, когда шаблон состояния / стратегии будет полезным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...