Applying this with mixin layer templates turned out to be a little
fiddly, because the base for the mixin layers cannot be templated. I
have built a working example, though.
It does require more code to do the composition than using the
fixpoint thing, since it needs the initial struct to be defined before
the composition, and then needs 'capping off' at the end with another
non-template struct.
Also, it looks like there's no real point to this when the base class
for the composition isn't templated. In the following example,
c_Base::c_Full_Node could have been defined outside of c_List, and it
is probably less confusing to do so.
I have achieved my goal, in that I can build data structures using
mixin layers but without using the fixpoint construct anyway, and I'm
kicking myself for not realising it's this simple.
The question remains, though - does the C++ standard require that it
is legal to have a struct/class that is declared in a base class, but
defined in its derived class?
The full example follows...
namespace mixins
{
// Required base class for starting the mixin
// composition - defines the empty base for the
// node structure that other mixin layers refine.
template<class B> struct c_List_Base : public B
{
typedef typename B::c_Full_Node c_Full_Node;
struct c_Node { };
};
template<class B> struct c_List_Fwd : public B
{
typedef typename B::c_Full_Node c_Full_Node;
struct c_Node : public B::c_Node
{
c_Full_Node *m_Next;
};
};
template<class B> struct c_List_Bwd : public B
{
typedef typename B::c_Full_Node c_Full_Node;
struct c_Node : public B::c_Node
{
c_Full_Node *m_Prev;
};
};
template<class B, class T> struct c_List_Data : public B
{
public:
typedef typename B::c_Full_Node c_Full_Node;
struct c_Node : public B::c_Node
{
T m_Data;
};
};
};
//////////
// Composition needs more code using this technique, though the
// principle is simple enough.
struct c_Base
{
// We need a new initial mixin layer for each composition,
// since we need a new c_Full_Node type declaration for
// composition or else the definitions would conflict.
struct c_Full_Node;
// May as well define the empty base for the node here as well
struct c_Node {};
};
// Step by step composition
struct c_List_000 : public mixins::c_List_Base< c_Base > {};
struct c_List_001 : public mixins::c_List_Fwd < c_List_000 > {};
struct c_List_002 : public mixins::c_List_Bwd < c_List_001 > {};
struct c_List_003 : public mixins::c_List_Data< c_List_002, int > {};
// Cap off the composition
struct c_List : public c_List_003
{
struct c_Base::c_Full_Node : public c_List_003::c_Node {};
typedef c_Base::c_Full_Node c_Node;
};
//////////
int main(int argc, char* argv[])
{
c_List::c_Full_Node l_Node1;
l_Node1.m_Next = 0;
l_Node1.m_Prev = 0;
l_Node1.m_Data = 0;
return EXIT_SUCCESS;
}
|