
"David Abrahams" <dave@boost-consulting.com> wrote
That's the basic idea behind all typeof() emulations that I've ever seen. They all require some type registration to associate a compile-time integral constant with a type. The innovation that Steve brought, IIUC, was that with the registration of only class types, the rest of the transformations possible in the type system could be encoded by the library automatically. For example, if you register class Foo, then the library can generate encodings for Foo*, Foo[2], Foo**, int (Foo), Foo(*)(Foo,Foo), etc., so you don't have to register those types. The problem is that those numbers can get very large...
Right. What I am even more interested in, is the ability to generate encodings of complicated templates. Say I registered int, std::vector, and mpl::vector2, and the library should be able to work with std::vector<int>, mpl::vector2<std::vector<int>, mpl::vector2<int, int> >, etc. It looks like this can be solved pretty easily. I imagine that Boost.Lambda, for example might also benefit from the ability to encode/decode templates (and maybe Spirit, but I am not sure).
My guess is that manipulating array types in that way has a lower cost at compile-time than using a vector_c, though using mpl probably yields much nicer library code and vector_c is more portable.
I don't suspect my solution is very compile-time efficient. But I don't have enough knowledge of compilers internals to estimate this. I believe the real advantage is in is simplicity. Anyway, here it is... Let's recall that to achieve our goal we have to: 1) Pass our expression to a function template, like: template<class T> SOMETHING foo(const T&); 2) inside foo() encode our type into a compile-time list of integers; 3) somehow manage to pass this list through the function boundaries; 4) decode the type. The most chalanging is 3. Let's define the following template: template<class N> struct sizer { char_[N::value]; }; The whole purpose of this is to create classes with a given sizeof (to be completely accurate we have to then divide this by sizeof(char)). Next template is what it is all about: template<class IntList> struct multi_int { typedef IntList int_list; template<class T> sizer<typename mpl::apply_if< typename mpl::less<T, typename mpl::size<int_list>::type>, mpl::at<IntList, T>, mpl::identity<mpl::int_<1> > >::type> get(const T&); }; So, if we have an object of this class, x, then sizeof(x.get(mpl::int_<n>())) defines the nth element of the list, and out-of-range values produce 1. The list can be reproduced like, say: #define BOOST_RTL_TYPEITEM(Expr, N)\ mpl::int_<sizeof(Expr.get(mpl::int_<N>()))> mpl::vector<\ BOOST_RTL_TYPEITEM(x, 0) , BOOST_RTL_TYPEITEM(x, 1) ... , BOOST_RTL_TYPEITEM(x, N)
Where it can be generated by the preprocessor for a fairly large N. The reproduced list is larger than the original one, and is 1-padded. The encoding scheme I use does not require to know where the list ends, so this doesn't matter. This is basically it. The return type of the function foo above (sorry for such name) is going to be multi_int<encode<T>::type>, where T is the original type, and encode template works accordingly to some encoding scheme. The encoding scheme to choose is a separate issue. In my example I encode templates by adding the encodings of its parameters to the code of template itself. So, if int is 1, std::vector is 2, and mpl::vector2 is 3, the above example: mpl::vector2<std::vector<int>, mpl::vector2<int, int> > becomes 3 2 1 3 1 1 Also note that all templates with one parameter behave similarly, so only one registration macro is required. Another macro is required to register templates with two parameters, etc.
For what it's worth, I'm pretty sure I know how to solve the problem that most typeof() implementations strip top-level references. See boost/iterator/is_lvalue_iterator.hpp for a demonstration.
Cool. I imagine pointers are pretty easy, but references surely represent a big problem... I will follow up with the working sketch... Regards, Arkadiy

The example is at: http://groups.yahoo.com/group/boost/files/typeof.zip I am using an MS-specific __COUNTER__ preprocessor macro. I guess under other compilers macros REGISTER_TYPE_IMPL, REGISTER_TEMPLATE_1_IMPL, etc., can be used directly. I admit that the code needs to be cleanned up in terms of MPL usage, as well as factoring out some code from registration macros, and better use of pre-processor looping features. I apologize for this. For now I am just trying to figure out whether there is an interest, and whether or not I am re-inventing the wheel :) Regards, Arkadiy

Arkadiy Vertleyb wrote:
The example is at:
http://groups.yahoo.com/group/boost/files/typeof.zip
I am using an MS-specific __COUNTER__ preprocessor macro. I guess under other compilers macros REGISTER_TYPE_IMPL, REGISTER_TEMPLATE_1_IMPL, etc., can be used directly.
I admit that the code needs to be cleanned up in terms of MPL usage, as well as factoring out some code from registration macros, and better use of pre-processor looping features. I apologize for this. For now I am just trying to figure out whether there is an interest, and whether or not I am re-inventing the wheel :)
I'm interested. Spirit desperately needs a typeof mechanism, or better yet, an auto mechanism. It will make life so much happier :-) However, it definitely has to be cross platform. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Hi Joel, "Joel de Guzman" <joel@boost-consulting.com> wrote
I'm interested. Spirit desperately needs a typeof mechanism, or better yet, an auto mechanism. It will make life so much happier :-)
What is "auto mechanism"? In RTL we use template argument deduction mechanism to make our relational expressions look decent. However, when the user needs to allocate a non-temporary object, she needs to specify the type explicitely, and this is where it becomes ugly. As you mentioned in the "typeof" section of Spirit documentation, what we really want (and what we need typeof for) is something like: ALLOCATE(expr, union(cross_product(t1, t2), selection(t3, _1[c1()]==15))); // use expr The type of our relational expression is an instantiation of a template, where most nodes are library-defined. However, the leaves are column names, and those are classes implicitely defined by the user. That's why I want to avoid explicit registration... There are two problems I can see here. One is related to getting a unique integer (what I am using __COUNTER__ for). I am trying to figure out the portable way of doing this (Paul just showed me something I think can work). Second -- registration is a specialization, and so has to be done out of global namespace (to get into the required namespace). This is also inconvenient. But, all this only required to handle user-created types. Is this an issue for Spirit?
However, it definitely has to be cross platform.
Sure :) Regards, Arkadiy

On Thu, 22 Apr 2004 07:32:03 -0400 "Arkadiy Vertleyb" <vertleyb@hotmail.com> wrote:
There are two problems I can see here. One is related to getting a unique integer (what I am using __COUNTER__ for). I am trying to figure out the portable way of doing this (Paul just showed me something I think can work). Second -- registration is a specialization, and so has to be done out of global namespace (to get into the required namespace). This is also inconvenient.
For what do you need the unique integer (i.e., what does it buy you)? Will it be used as an integral constant template parameter, or as a key during runtime, or what?

"Jody Hagins" <jody-boost-011304@atdesk.com> wrote
There are two problems I can see here. One is related to getting a unique integer (what I am using __COUNTER__ for). I am trying to figure out the portable way of doing this (Paul just showed me something I think can work). Second -- registration is a specialization, and so has to be done out of global namespace (to get into the required namespace). This is also inconvenient.
For what do you need the unique integer (i.e., what does it buy you)? Will it be used as an integral constant template parameter, or as a key during runtime, or what?
Integral constant. Each class/template can be assigned a unique integer constant, so that the whole class (template tree) can be encoded into an mpl::vector_c (or similar), to be later decoded into the original type. Everything is done at compile time (as you would expect with typeof). Regards, Arkadiy

On Thu, 22 Apr 2004 10:03:00 -0400 "Arkadiy Vertleyb" <vertleyb@hotmail.com> wrote:
Integral constant. Each class/template can be assigned a unique integer constant, so that the whole class (template tree) can be encoded into an mpl::vector_c (or similar), to be later decoded into the original type. Everything is done at compile time (as you would expect with typeof).
As I thought. I apologize in advance for being thick, but I still do not see how you can solve your problem with an integral constant that changes between compilation units. As I understnad, even Paul's COUNTER gets reset for each compilation unit so there is no guarantee that you will get the same value across compilation units, and this could cause problems between object modules and libraries.

"Jody Hagins" <jody-boost-011304@atdesk.com> wrote
I still do not see how you can solve your problem with an integral constant that changes between compilation units. As I understnad, even Paul's COUNTER gets reset for each compilation unit so there is no guarantee that you will get the same value across compilation units, and this could cause problems between object modules and libraries.
You may be right. I don't see any problems with compilation because encoding/decoding are parts of the same "typeof", and therefore always happen in the same compilation unit, and if different constants are used in different compilation units for the same type, it's probably OK. However there might be linker problems when the same template specialisation, say decode<mpl::int_<1> >, has different bodies. I admit, I did not check for this. I kind of hope maybe this will be discarded by the linker before the conflict gets discovered, since the class doesn't have any runtime stuff in it. However, if it's not the case, you are right, this won't work :( The ideal would be to somehow convert the file name in a number, and use it in conjunction with the line number, but it doesn't seem possible... Regards, Arkadiy

Arkadiy Vertleyb wrote:
There are two problems I can see here. One is related to getting a unique integer (what I am using __COUNTER__ for). I am trying to figure out the portable way of doing this (Paul just showed me something I think can work).
See attached. Instead of __COUNTER__, it is used via GET and SET. SET bumps the value by 1. It can also be used inside functions and namespaces. The attached test.cpp outputs 0 2 4 1 12 with g++ 3.4 (-W -Wall -pedantic) and icc 8.0 (-strict_ansi). Jim
participants (4)
-
Arkadiy Vertleyb
-
Jim Apple
-
Jody Hagins
-
Joel de Guzman