While prototyping the Metagene transformations, we encountered several difficulties with the some C++ specific (and often weird) issues. This page is the Hall of shame of the C++ horrors we met.
Nested res
The problem
in ocaml, the expression
let f a b = a + b in f 50 1 |
is equivalent to
let f = function a -> function b -> a + b in f 50 1 |
Therefore, we want to translate such expressions with the following construct:
template<unsigned a>
struct f
{
template<unsigned b>
struct res
{
enum {res = a + b};
};
}; |
The bad news is that this code doesn't compile.
Comeau gives the following error:
'declaration of a member with the same name as its class'.
A solution
Here is an equivalent c++ code that compiles.
template<unsigned a>
struct f
{
struct __ {
template<unsigned b>
struct res
{
struct __ {
enum {res = a + b};
};
};
};
}; |
The use of this solution means that for a n-ary funcion, we are going to generate 2 * n nested structs. Moreover using this construction makes function application two times heavier.
This can be made simplier with the use of macros:
#define mtg_open {struct __{
#define mtg_close };};
#define mtg_apply(F, P) F< P >::__::res |
Our code becomes:
template<unsigned a>
struct f
mtg_open
template<unsigned b>
struct res
mtg_open
enum {res = a + b};
mtg_close
mtg_close |
This function can be applied the following way:
enum {res = mtg_apply(mtg_apply(plus, 50), 1)}; |
Our current translation process use implicit boxes (see
History: generation paradigm). This asserts us that we will not have two nested structs which the same name.
Use of the typename keyword
Just a flavor of the typename keyword problems
# define mtg_open(T, N, Sn) template<T N> struct Sn { struct __ {
struct let {
mtg_open(typename, a, plus)
mtg_open(typename, b, res)
typedef [1] mtg_apply(mtg_apply(mtg::plus, a), b) res;
mtg_close
mtg_close
mtg_open(typename, a, dbl)
typedef [2] mtg_apply(mtg_apply(mtg::times, mtg::Int <2>), a) res;
mtg_close
typedef [3] mtg_apply(mtg_apply(plus, mtg_apply(dbl, mtg::Int <5>)),
mtg_apply(mtg_apply(plus,
mtg_apply(dbl, mtg::Int <10>)),
mtg::Int <26>))
res;
}; |
Do we need to put the 'typename' keyword in the places [1], [2] and [3]?
Here are some c++ compilers results:
- E stands for empty
- T stands for 'typename'
- x stands for don't care (can be empty or 'typename')
g++ 3.2
| [1] | [2] | [3] | compiler result |
| E | E | E | doesn't compile (hard-to-understand message) |
| T | E | E | a bit better but doesn't compile |
| E | T | E | works (with a warning saying that there should be a typename in [1]) |
| T | T | E | g++ segv bouh |
| x | x | T | "error: using `typename' outside of template" |
como
That's better, but it should be great that the generated code compiles with g++.
In order to make the problem easier we decided that each basic operation (application of one argument, declaration of an int...) would correspond to one 'typedef'.
Here is an example:
let res = plus (times 2 4) (times 5 1) |
is translated as
typedef plus temp7;
typedef times temp11;
typedef mtg::Int <2> temp12;
typedef temp11::value< temp12 >::res temp9;
typedef mtg::Int <4> temp10;
typedef temp9::value< temp10 >::res temp8;
typedef temp7::value< temp8 >::res temp1;
typedef times temp5;
typedef mtg::Int <5> temp6;
typedef temp5::value< temp6 >::res temp3;
typedef mtg::Int <1> temp4;
typedef temp3::value< temp4 >::res temp2;
typedef temp1::value< temp2 >::res res; |
Which is very verbose, but allows us to isolate the cases where we need to put the 'typename' keyword.
Specialisation in a structure
The problem
The following code:
let fmatch =
function 1 -> 2
| _ -> 1
in fmatch 5;;
|
is translated in the following way:
mtg_open0(let)
mtg_open(typename, _T, fmatch)
typedef mtg::Int <1> res;
mtg_close
template<>
mtg_open0(fmatch< mtg::Int< 1 > >)
typedef mtg::Int <2> res;
mtg_close
typedef mtg_apply(fmatch, mtg::Int <5>) res;
mtg_close
typedef let::__::res res; |
When trying to compile this, como says:
'error: explicit specialization is not allowed in the current scope'
C++ doesn't allow template specialization in structures. This is a
very bad news which completly breaks our way translating pattern matchings...
Late night comment about this issue
According to the C++ standard, partial specialisations (
template < ... >) are allowed as class members, wether explicit specializations (
template <>) are not.
This is why the following code does not compile.
struct foo
{
template < class T >
class bar;
template <>
class bar < int >
{};
};
int main ()
{
foo :: bar < int > b;
} |
However, there is a tricky solution :
class bogus
{};
struct foo
{
template < class T, class Bogus = bogus >
class bar;
template < class Bogus >
class bar < int, Bogus >
{};
};
int main ()
{
foo :: bar < int > b;
} |
This code is equivalent to the previous snippet and compiles properly. This solution is now used everywhere in the current implementation of Metagene.
--
FrancisMaes? - 19 Nov 2003
- Metagene, Metagene main page.
- Introduction, An introduction to the Metagene project.
- History: preliminary examples, Some examples showing the equivalence between C++ meta-programming and functionnal programming.
- History: generation paradigm, The generation paradigm and its history.
- Metagene translation process, From functionnal expressions to class templates.
- Related work, Some related work.
- Download Metagene, Download the latest release of Metagene.
- Paper and Slides, A paper introduicing the Metagene project with corresponding slides.
- Possible extensions, Possible extensions to Metagene.
- Horror Show, The museum of C++ horrors.
to top