From: pfultz2 on
On Feb 2, 3:13 pm, "Johannes Schaub (litb)" <schaub-johan...(a)web.de>
wrote:
> pfultz2 wrote:
> > I seem to have a problem with CRTP and typedefs, here is a sample
> > code:
>
> > template<class B>
> > class Sequence
> > {
> > public:
> > typedef typename B::ElementType ElementType;
> > }
>
> > template<class T>
> > class Container
> > {
> > public:
> > typedef T ElementType;
> > }
>
> > Containter<char> test;
>
> > but the compiler says that Container<char>::ElementType doesnt exist?
> > is it possible to refer to subtypes using CRTP? or perhaps i have a
> > error some other place in my code?
> > is there a way to forward declare the type and the subtypes like this:
>
> > template<class T>
> > class Container;
>
> > //Then forward declare subtype here
> > template<class T>
> > class Containter<T>::ElementType;
>
> > Or maybe there is another way, thanks
>
> I'm assuming you actually have an inheritance relationship, and forgot to
> write that in your testcase. Well, at the point "Sequence<Container>" is
> instantiated (which happens immediately when the base class name is seen),
> Container::ElementType has not yet been declared, and so the instantiation
> of the typedef declaration in Sequence will fail to find it.
>
> You can put that as a template argument. What about
>
> template<typename T, typename Typedefs>
> class Sequence {
> typedef typename Typedefs::ElementType ElementType;
> // ...
>
> };

{ edits: quoted banner removed. please remove extraneous material before
posting. -mod }

Well that would work for the first type but if its an inner class,
what work around would i use?
for example:
template<class B, class Iterator>
class Sequence
{
public:
typename B::ElementType Calculate()
{
return 1;
}

typename B::Iterator Get()
{
return typename B::Iterator();
}

};

template<class T>
class Container : public Sequence<Container<T> >
{
public:
typedef T ElementType;

class Iterator
{

};
};

/*
*
*/
int main(int argc, char** argv)
{
Container<int> test;
printf("%i", test.Calculate());
return (EXIT_SUCCESS);
}

I could acces ElementType, but how do i access Iterator?


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Ulrich Eckhardt on
pfultz2 wrote:
> template<class B>
> class Sequence
> {
> public:
> typedef typename B::Iterator Iterator;
> typedef typename B::ElementType ElementType;
> }

Again, this declaration is missing a semicolon, so this can not be the code
you tried to compile...

> template<class T>
> class Container : public Sequence<Container<T>>
> {
> public:
> typedef T ElementType;
> class Iterator
> {
> };
> }

....as does this one.

> Now i get an error saying that B::Iterator and B::ElementType doesnt
> exist.

This doesn't help. Provide the *real* code, not some substitute that more or
less resembles that code. I'm left to guessing what you really did now...


> I need to use it in the return types in several places like this:
> ElementType First()
> {
> return this->Derived().GetIterator().First();
> }

Where is that? I guess it's a memberfunction of Sequence<>, right? If so, I
believe you can just write

typename B::ElementType First() { [...] }

The simple reason is that while these are parsed to some extent, they are
not actually instantiated as early. That is what allows an inline member
function to refer to a variable declared later in the class definition.

> Is this even possible? Or is there a better way to do this?

Several alternatives exist:
* Provide a template parameter for the ElementType.
* (Maybe) the workaround above.
* Use a common traits-class that defines the ElementType both for
Sequence<> and Container<>.
* Define the ElementType in the baseclass instead of the derived class.

> The only other way i can think of is to use macros, but this is not
> very nice, and is harder to mantain.

How would that way look like?

Uli

--
Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: pfultz2 on

>
> This doesn't help. Provide the *real* code, not some substitute that more or
> less resembles that code. I'm left to guessing what you really did now...

Well i have reposted the code, trying to use an inner class also, here
it is again:
template<class B>
class Sequence
{
public:
typename B::ElementType Calculate()
{
return 1;
}

typename B::Iterator Get()
{
return typename B::Iterator();
}

};

template<class T>
class Container : public Sequence<Container<T> >
{
public:
typedef T ElementType;

class Iterator
{

};

};

/*
*
*/
int main(int argc, char** argv)
{
Container<int> test;
printf("%i", test.Calculate());
return (EXIT_SUCCESS);

}

> Several alternatives exist:
> * Provide a template parameter for the ElementType.
> * (Maybe) the workaround above.
> * Use a common traits-class that defines the ElementType both for
> Sequence<> and Container<>.
> * Define the ElementType in the baseclass instead of the derived class.

This would work for ElementType but not for Iterator, since it is a
subclass. But maybe i can use some kind of common traits class for
that also, and place the iterator in there. Only this makes the design
a little more messy, perhaps not.
>
> > The only other way i can think of is to use macros, but this is not
> > very nice, and is harder to mantain.
>
> How would that way look like?

Well i would define all the methods from sequence in a macro like this

#define SEQUENCE(B) \
B::ElementType Calculate()\
{\
return 1;\
}\
\
B::Iterator Get()\
{\
return typename B::Iterator();\
}\

and then in the container class i would add the macro like this:

template<class T>
class Container
{
public:
typedef T ElementType;

class Iterator
{

};

SEQUENCE(Container<T>)

};

This will work, i just dont like using macros like this, first because
it invades the global namespace, and secondly it can be messy to write
the code for Sequence, because everything has to be accessed at the
global namespace, so if some classes are used three or more levels
deep in a namespace, it makes it much more difficult to read. Thirdly,
you can't override the routines in the derived class either, which it
would be nice if they could be overriden in some cases for more
effecient routines in some cases. I thought CRTP would be great option
for this, and i dont know if i can overcome its limitiations in this
case.



--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]