C ++复合模板类工厂

时间:2009-11-11 17:57:29

标签: c++ templates

是否可以在不手动指定所有组合的情况下制作复合模板类工厂?我的意思是如果我有这些课程:

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语句,第一个组件中的第二个组件的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 个答案:

答案 0 :(得分:3)

无法在运行时生成模板的所有可能组合。编译器需要知道您将使用哪些,以便它可以为它们生成代码。

您是否考虑过使用composition而不是继承?

编辑:通过撰写,我更多地考虑以下几点:


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

我不能说这是一个比你开始时更好的方法,但它是一个可以考虑的替代方法。

答案 1 :(得分:3)

模板参数必须在编译时知道。一个可能的解决方案是: CreateComposite<int, int, int>()您可以针对每种可能的情况对其进行专门化。哦,好吧:那是相当不行的.. 我建议你选择一些老式的动态多态和一个std::vector<ComponentBase>

答案 2 :(得分:2)

通过使用带有单个switch-case的级联模板方法,可以避免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;
}

为了好玩,您可以将boost proprocessor用于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;
}

无论如何,我建议尽可能避免使用这些解决方案。

答案 3 :(得分:0)

不。这需要反思,C ++没有。

为什么不只是CreateComposite<T1, T2, T3>()

编辑:听起来像状态/策略模式会有所帮助。

答案 4 :(得分:0)

虽然我同意其他人认为动态多态可能是要走的路,但从技术上讲,你可以做你所要求的。其他人,现在避开你的眼睛......:

#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个实例。

答案 5 :(得分:0)

为什么你甚至想要一个具有3个完全任意类型成员的泛型Composite类?我无法想象你可以在那种情况下为这样的类编写任何有用的代码。 Composite本身不能包含任何有用的代码,任何使用复合的代码都需要知道其中的类型(您当前没有存储在哪里)。

然而,如果3种类型各自实现某种已知的接口 - 例如。第一个是数字类型,第二个是序列类型等 - 然后你可以为每个类型使用一个工厂并使用多态来返回具有正确接口的东西,而你根本不需要模板。 / p>