 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Chris Guest
|
Posted: Wed Jun 15, 2005 10:23 am Post subject: Recursive templates and inheritance on g++, xlC, and icpc |
|
|
Hi,
In the Loki library, macros are used as a shorthand for creating
Typelists [1]. The most recent issue of C/C++ Users Journal offers a
"template chain" alternative to the Loki shorthand [2]. In implementing
this solution, I came across some portability problems between g++ and
xlC. Consider this code fragment:
-- begin listing --
template <class T>
struct Foo
{
template <class S>
struct Bar : public Foo<S>
{
};
};
int main()
{
Foo<double>::Bar<int> x;
Foo<int>::Bar<double>::Bar<cha r*> y;
return 0;
}
-- end listing --
g++ and icpc compiles this bit of code without complaint. xlC, on the
other hand, has trouble with resolving the names and returns the error:
"test.cpp", line 14.39: 1540-0300 (S) The "private" member "struct
Foo<int>::Bar<char *>" cannot be accessed.
This leads me to the first question (since I'm not as well versed in
the standard as I would like): which compiler is to be believed? And a
second question: how can I write a portable version of that listing? I
took a stab at the second question and came up with:
-- begin listing --
template <class T>
struct Foo
{
typedef Foo<T> This;
template <class S>
struct Bar : public Foo<S>
{
};
};
int main()
{
Foo<double>::This::Bar<int> x;
Foo<double>::Bar<int> x_clone;
// A quick test to see if the types make sense
x = x_clone;
// Foo<int>::Bar<double>::Bar<cha r*> y; // I won't compile on xlC!
Foo<int>::This::Bar<double>::T his::Bar<char*> y_clone;
// y = y_clone; // I won't compile on xlC!
return 0;
}
-- end listing --
At first glance, this seems to do the trick, but if we uncomment the "I
won't compile" lines, and try to compile them with g++ we get:
test.cpp: In function `int main()':
test.cpp:23: no match for `Foo<int>::Bar<char*>& =
Foo<double>::Bar<char*>&'
operator
test.cpp:9: candidates are: Foo<int>::Bar<char*>&
Foo<int>::Bar<char*>::operator =(const Foo<int>::Bar<char*>&)
icpc gives a similar error. The types aren't matching up! The typedef
is looking at the top level for y, but at the inherited level for
y_clone. So what's a solution to this? Moving the typedef from the
parent class to the child class gives us:
-- begin listing --
template <class T>
struct Foo
{
template <class S>
struct Bar : public Foo<S>
{
typedef Foo<T> This;
};
};
int main()
{
Foo<double>::Bar<int> x;
Foo<double>::Bar<int> x_clone;
x = x_clone;
Foo<int>::Bar<double>::Bar<cha r*> y; // I won't compile on xlC
Foo<int>::Bar<double>::This::B ar<char*> y_clone;
y = y_clone; // I won't compile on xlC
return 0;
}
-- end listing --
We get the desired behavior with icpc and g++ (that is, the type
signatures match up), and xlC builds the program. Now, since building
is not a sign of correctness, I will state that the desired
funcionality (as described in the article) is obtained, at the expense
of the original goal of brevity. My inclination is to believe that the
xlC implementation of templates is broken. Anyone have thoughts or
comments?
Thanks,
Chris
[1] Modern C++ Design, Andrei Alexandrescu, Addison Wesley, 2002.
[2] "Using Chains to Free Library Code," C/C++ Users Journal, July
2005, p 26-29.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Graeme Prentice Guest
|
Posted: Sat Jun 18, 2005 9:22 am Post subject: Re: Recursive templates and inheritance on g++, xlC, and icp |
|
|
On 15 Jun 2005 06:23:46 -0400, Chris wrote:
| Quote: | Hi,
In the Loki library, macros are used as a shorthand for creating
Typelists [1]. The most recent issue of C/C++ Users Journal offers a
"template chain" alternative to the Loki shorthand [2]. In implementing
this solution, I came across some portability problems between g++ and
xlC. Consider this code fragment:
-- begin listing --
template <class T
struct Foo
{
template
struct Bar : public Foo
{
};
};
int main()
{
Foo
Foo<int>::Bar<double>::Bar<cha r*> y;
return 0;
}
-- end listing --
g++ and icpc compiles this bit of code without complaint. xlC, on the
other hand, has trouble with resolving the names and returns the error:
"test.cpp", line 14.39: 1540-0300 (S) The "private" member "struct
Foo<int>::Bar<char *>" cannot be accessed.
|
Did you try adding the public: specifier to the Foo class definition - I
know the Bar member is already public but it might help your xIC
compiler.
| Quote: |
This leads me to the first question (since I'm not as well versed in
the standard as I would like): which compiler is to be believed?
|
Try your code at http://www.comeaucomputing.com/tryitout/
or buy the compiler ($50). If the Comeau compiler accepts or rejects
your code, it's a good indication of whether it's strictly ANSI
compliant. Obviously xIC is wrong here.
| Quote: | And a
second question: how can I write a portable version of that listing? I
took a stab at the second question and came up with:
-- begin listing --
template <class T
struct Foo
{
typedef Foo
template <class S
struct Bar : public Foo
{
};
};
int main()
{
Foo
Foo<double>::Bar<int> x_clone;
// A quick test to see if the types make sense
x = x_clone;
// Foo<int>::Bar<double>::Bar<cha r*> y; // I won't compile on xlC!
Foo<int>::This::Bar<double>::T his::Bar<char*> y_clone;
// y = y_clone; // I won't compile on xlC!
return 0;
}
-- end listing --
At first glance, this seems to do the trick, but if we uncomment the "I
won't compile" lines, and try to compile them with g++ we get:
test.cpp: In function `int main()':
test.cpp:23: no match for `Foo<int>::Bar<char*>& =
Foo<double>::Bar<char*>&'
operator
test.cpp:9: candidates are: Foo<int>::Bar<char*>&
Foo<int>::Bar<char*>::operator =(const Foo<int>::Bar<char*>&)
icpc gives a similar error. The types aren't matching up! The typedef
is looking at the top level for y, but at the inherited level for
y_clone.
|
Comeau and VC7.1 accept this code and the code looks fine to me. This is
a g++ bug.
| Quote: | So what's a solution to this? Moving the typedef from the
parent class to the child class gives us:
-- begin listing --
template <class T
struct Foo
{
template
struct Bar : public Foo
{
typedef Foo
};
};
int main()
{
Foo<double>::Bar<int> x;
Foo<double>::Bar<int> x_clone;
x = x_clone;
Foo<int>::Bar<double>::Bar<cha r*> y; // I won't compile on xlC
Foo<int>::Bar<double>::This::B ar<char*> y_clone;
y = y_clone; // I won't compile on xlC
return 0;
}
-- end listing --
We get the desired behavior with icpc and g++ (that is, the type
signatures match up), and xlC builds the program.
|
Comeau and VC7.1 don't accept the code and nor should they because in
y_clone, This refers to Foo<int> so y_clone has type
Foo<int>::Bar<char*> which is not the same as the type of y
(where did the extraneous spaces in your code come from?)
| Quote: | Now, since building
is not a sign of correctness, I will state that the desired
funcionality (as described in the article) is obtained, at the expense
of the original goal of brevity. My inclination is to believe that the
xlC implementation of templates is broken. Anyone have thoughts or
comments?
|
It's tricky and clever code so it's not surprising some compilers have
problems with it.
Here's another way to generate a chain of types - not quite as easy to
write as the foo::bar technique but not unusable either.
The C++ standard specifies a guideline of a recommended minimum for
"Recursively nested template instantiations" of 17 and a recommended
minimum for "Direct and indirect base classes" of 16384. I'm unsure
whether the code below or above involves recursive template
instantiation or not - probably the code below does and the code above
doesn't.
struct end{};
template <class T,class U>
struct Foo
{
};
int main()
{
Foo< Foo< Foo,double > y1;
Foo< Foo< Foo< Foo< Foo< Foo< Foo< Foo
int>,double>,int>,double>,int>,double>,int> y2;
}
Graeme
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Graeme Prentice Guest
|
Posted: Sun Jun 19, 2005 11:50 am Post subject: Re: Recursive templates and inheritance on g++, xlC, and icp |
|
|
On 18 Jun 2005 05:22:52 -0400, Graeme Prentice wrote:
| Quote: |
Here's another way to generate a chain of types - not quite as easy to
write as the foo::bar technique but not unusable either.
struct end{};
template <class T,class U
struct Foo
{
};
int main()
{
Foo< Foo< Foo,double > y1;
Foo< Foo< Foo< Foo< Foo< Foo< Foo< Foo
int>,double>,int>,double>,int>,double>,int> y2;
}
|
Oops, that's not quite right - but anyway, that's the Loki/ Andreii
typelist scheme ... - what was I thinking! I was just trying to find
another way that the xIC compiler might like.
Graeme
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Chris Hoge Guest
|
Posted: Tue Jun 21, 2005 4:30 pm Post subject: Re: Recursive templates and inheritance on g++, xlC, and icp |
|
|
Hi Graeme,
Thanks for the Comeau link. I've seen advertisements for it in the
regular magazines, but the tryitout link is pretty neat. I managed to
incorporate the typelist chain into the system I'm developing, and it
compiles on g++, xlC (the ibm/absoft compiler) and icpc (the intel
compiler). It works fairly well outside of a class scope. For example:
-- begin listing --
struct A : public MossBase< double, 10 > {};
struct B : public MossBase< SomeClass, 20 > {};
struct C : public MossBase< int, 1 > {};
typedef GenClass
<
typename TypeListGen::Y::
Add< A >::Y::
Add< B >::Y::
Add< C >::Y::
TypeList
-- end listing --
describes a structure with an array of 10 doubles, an array of 10
SomeClasses, and a single int. I like how it avoids the lisp-like
nesting of template braces. Following the convention in Andrei's book
member access is fairly easy.
The reason I'm going to this trouble is because I want a framework that
allows me to serialize a class without the explicit serialization
programming. This solution is being deployed in an MPI environment (MPI
is a parallel computing API). MPI provides a complete, but inconvenient
way to serilalize objects. I've snipped quite a bit of code that does
the work, but you can take on faith that my library, with the "Data"
class defined above performs all of the necessary serialization
operations for MPI with a member function, Data::commit(). I'm still a
bit unsure about the solution, especially considering that if you want
to make the Data object a member of a class, it becomes much messier to
write. For example:
-- begin listing --
template <class T>
class TestClass
{
struct A : public MossBase< double, 10 > {};
struct B : public MossBase< T, 20 > {};
struct C : public MossBase< int, 1 > {};
typedef GenClass
<
typename TypeListGen::Y::
template Add< typename TestClass::Y::
template Add< typename TestClass::Y::
template Add< typename TestClass::Y::
TypeList,
Holder
};
-- end listing --
My apologies for the extra whitespace. Cut-and-paste from my favorite
editor left something to be desired.
-Chris
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|