Reputation: 33655
I have this puzzle which I am trying to solve, and fundamentally it boils down to the following example:
template <typename CT>
struct A
{
typedef typename CT::VALUE_T FOO; // FOO is dependent on CT
};
template <typename CT>
struct B
{
typedef typename CT::BAR BAR;
BAR foo() {}
};
template <typename DT>
struct C : B<C<DT> >
{
typedef DT VALUE_T;
typedef typename A<C>::FOO BAR;
};
int main () {
C<int> c;
}
I can try to explain the above (I've tried about three times and deleted the text!), but basically the requirements are:
C
must inherit from B
typed with C
(exploiting CRTP), i.e. B<C<>>
C
is the only one that can instantiate A
(i.e. A
has to be typed with C
)A
is the only one that can define FOO
(FOO
is dependent on the type CT
, the relationship is more complicated than that presented)The problem (as you can see with the above code) is that the BAR
type is available only within C
and this is incomplete when B
is instantiated, hence B
does not see the BAR
type of the template argument CT
(C<int>
). Unfortunately within B
, the type BAR
is used as arguments to functions, and return types (i.e. not just limited to function scope - as a result I cannot simply move the typedef to function scope).
Is there a way around this? I cannot break the above stated relationships (unless as a last resort). Presumably with c++11, I could use auto
and get around the need to have the BAR
typedef in B
, however this is currently not yet an option.
EDIT: following on from @bitmasks' comment, some more information.
A
and B
is used in quite a few binaries in different situations, the only unique situation in this case is that C
derives from B
, in the other instances, C
owns an instance of something derived from B
.A
and B
), as long as they can be defaulted to values which doesn't require changing existing uses of A
and B
. The same set of types must be available either as template parameter that is defaulted or some other mechanism.I'm using templates here simply because I require the tight-coupling and I needed the flexibility to use the code in different situations.
Descriptions of components:
A
is best described as a container, and FOO
really is an iterator, what it contains is defined by a typedef of the template parameterB
is best described as a base class which contains a set of functions which are called by some components owned by an instance of C
. In the previous cases, those components were passed a reference to things derived from B
(and those things are also owned by C
), in this instance, I'm providing a reference to C
itself.The main complication arises from accessing the container A
, previously the relationship between B
and C
is that C
has an instance of B
, but now C
is an instance of B
- this change in semantics breaks the way types are injected into the classes.
Upvotes: 6
Views: 383
Reputation: 34674
I think you can combat the cyclic typedef
requirement with default template parameters. The following works as intended (as far as I understand your question) and leaves you (almost) all the liberties of your original code:
template <typename CT, typename CTVALUE_T = typename CT::VALUE_T>
struct A
{
//typedef typename CT::VALUE_T FOO; // FOO is dependent on CT
typedef CTVALUE_T FOO; // FOO is dependent on CT
};
template <typename CT, typename CTBAR = typename CT::BAR>
struct B
{
//typedef typename CT::BAR BAR;
typedef CTBAR BAR;
BAR foo() {return 0;}
};
template <typename DT> struct VALUE_T__from__DT {
typedef DT VALUE_T;
};
template <typename DT, template <class T> class _C_ > struct BAR__from__DT {
typedef typename A<_C_<DT> , typename VALUE_T__from__DT<DT>::VALUE_T >::FOO BAR;
};
template <typename DT>
struct C : B<C<DT>, typename BAR__from__DT<DT, C >::BAR >
{
//typedef DT VALUE_T;
//typedef typename A<C>::FOO BAR;
};
int main () {
C<int> c;
int x = c.foo();
(void)x;
return 0;
}
The names of the helper template types are horrible, perhaps you can find better names.
The core trick is that the definition of the problematic typedef
s is put in a separate meta-container (i.e. a container for traits) such that you can still do any voodoo there.
I commented the unnecessary typedefs out (but left them there so you have a better chance to figure out what I'm doing there).
Does that fit your need?
Upvotes: 4