Reputation: 23
Is it possible to make a composite template class factory without manually specifying all of the combinations? What I mean is if I have these classes:
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
I would like to create a function like this:
CompositeBase *CreateComposite(int c1, int c2, int c3);
so that
CreateComposite(4,3,7);
would create and return a
Composite<Component4,Component3,Component7>
The reason to do this is so that I can load data from a file and create the different composite objects. The file would have the three values for the components to create each composite object, then the other data required by it.
The problem is with 10 different components, there are 1000 different possible composite classes. To specify all of them would require a switch statement for the 1st component, 10 switch statements for the second component within that one, and 100 switch statements for the third component within those ten, and the function would be 1000+ lines long.
Is there any other way to write the CreateComposite function? Other than this:
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
}
}
Upvotes: 2
Views: 2091
Reputation: 18449
Why would you even want a generic Composite class with 3 members of completely arbitrary types? I can't imagine that you can write any useful code for such a class in that situation. The Composite itself can't contain any useful code and any code that uses a composite will need to know what types are in it (which you're currently not storing anywhere).
However, if the 3 types each implement some sort of known interface - eg. the first is always a numeric type, the second is a sequence type, etc - then you can just use one factory for each type and use polymorphism to return something with the correct interface, and you don't need the templates at all.
Upvotes: 0
Reputation: 12938
You can avoid O(n^3) complexity to O(n) by using cascade template methods with single switch-case in it:
#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;
}
Just for fun you can use boost proprocessor for O(1) complexity
#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;
}
In any case, I'd recommend to avoid these solutions if possible.
Upvotes: 2
Reputation: 308392
It is not possible to generate all the possible combinations of a template at runtime. The compiler needs to know which ones you will be using so that it can generate code for them.
Have you considered using composition rather than inheritance?
Edit: By composition I was thinking more along the lines of the following:
class Composite
{
private:
ComponentBase * component1;
ComponentBase * component2;
ComponentBase * component3;
};
class Component0 : public ComponentBase {}; //Also have Component1-9
If you can't use pointers to a base class for some reason, you're stuck having code to generate all the permutations of template classes. But rather than using nested case statements, you can use a table to create new instances, and macros to make the code easier.
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]();
}
I can't really say this is a better approach than the one you started with, but it's an alternate to consider.
Upvotes: 3
Reputation: 9408
Whilst i agree with others that dynamic polymorphism is probably the way to go, technically you can do what you're asking. Everyone else, avert your eyes now... :
#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;
}
That works for 5 types and it should be obvious how to it extend to 10 types.
I'm not sure i would ever want to actually use this though - for 10 composites the compiler would generate all 1000 instantiations of the Composite template.
Upvotes: 0
Reputation: 59831
Template arguments must be known at compile time. A possible solution would be:
CreateComposite<int, int, int>()
and you can specialize it for every possible case. Oh, well: that's rather a no go..
I'd suggest that you rather go with some oldfashioned dynamic polymorphism and a std::vector<ComponentBase>
.
Upvotes: 3
Reputation: 106589
Nope. Such would require reflection, which C++ does not have.
Why not just CreateComposite<T1, T2, T3>()
?
EDIT: Sounds like a case where the state/strategy pattern would be helpful.
Upvotes: 0