
Hi all, The new version of typeof library can be found at: http://groups.yahoo.com/group/boost/files/typeof.zip This version contains the following improvements: 1) The compilation speed is significantly improved for simple types by not instantiating unnecessary templates; 2) Support for integral template parameters has been added so that, for example, a template: template<class T, unsigned int ui, bool b, short s> class foo; can be registered like this: BOOST_REGISTER_TEMPLATE_X(foo, (class)(unsigned int)(bool)(short)); (_X stands for "extra" or "extended". Any naming improvement suggestions are welcome) A template parameter can be described as: class typename [unsigned] char [unsigned] short [unsigned] int [unsigned] long unsigned bool 3) The vector size limit is now controled by BOOST_TYPEOF_LIMIT_SIZE. If it is bigger than BOOST_MPL_LIMIT_VECTOR_SIZE, the additional vector definitions are generated automatically (using the method suggested and explained by Aleksey Gurtovoy). (There appears to be one more limitation that I wasn't aware of. It is related to the max number of template parameters allowed by the compiler. For example, VC7.1 appears to have a limit of 64) 4) All is compiled against current Boost CVS. Any comments are most welcome. Regards, Arkadiy

Arkadiy Vertleyb wrote:
Any comments are most welcome.
Just some small nit-picking I'm afraid: When running your tests, the intel linux compiler gives this warning: main.cpp(143): warning #858: type qualifier on return type is meaningless const int cf(); which means that BOOST_TYPEOF_PRESERVE_LVALUE(cf()) gives int, and your test fails, so you might want to remove that test. Also neither encode_signed nor encode_unsigned seem to work for zero values. I think encode_signed is missing a check for zero, and I guess encode_unsigned should add 1 to value when encoding, and then subtract 1 when decoding - or have a special case like encode_signed does. Other than those small points the library is looking good. Daniel

"Daniel James" <daniel@calamity.org.uk> wrote
When running your tests, the intel linux compiler gives this warning:
main.cpp(143): warning #858: type qualifier on return type is meaningless const int cf();
which means that BOOST_TYPEOF_PRESERVE_LVALUE(cf()) gives int, and your test fails, so you might want to remove that test.
OK. Is it the only problem with Intel? I did not realize things are so good -- I've been only testing with VC7.1 and GCC 3.3.
Also neither encode_signed nor encode_unsigned seem to work for zero values. I think encode_signed is missing a check for zero, and I guess encode_unsigned should add 1 to value when encoding, and then subtract 1 when decoding - or have a special case like encode_signed does.
I see, both are buggy... I guess I'll have to use the second integer for unsigned to properly handle UINT_MAX (somehow I think this value is likely to be used).
Other than those small points the library is looking good.
Thanks for your comments. I am glad you liked the library after all... I'll make the fixes and post the modified version sometime soon. Regards, Arkadiy

Arkadiy Vertleyb wrote:
OK. Is it the only problem with Intel? I did not realize things are so good -- I've been only testing with VC7.1 and GCC 3.3.
It's always worked with intel. Without precompiled headers it's a little slow, but with them it's fine. On linux, intel has a built in typeof, because it tries to emulate g++. You can tell when it has this because it defines __GNUC__. Also, I noticed that you're using 'typeof' on g++, if you use __typeof__ instead it will work with the -ansi switch. This is what I'm using in the config file: #elif defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC) // Intel # if defined __GNUC__ # define BOOST_TYPEOF __typeof__ # undef BOOST_TYPEOF_EMULATION # endif # elif defined __GNUC__ // GNU C++: # define BOOST_TYPEOF __typeof__ # undef BOOST_TYPEOF_EMULATION
Thanks for your comments. I am glad you liked the library after all... I'll make the fixes and post the modified version sometime soon.
Sorry if I seemed too negative, I've always liked it. I was a little worried about the limits on the complexity of types and the number of calls to foo that were generated, but I'm over that now. You've implemented some improvements, and I now think it wasn't that big a problem in the first place. By the way, I did some small experiments with using mpl views instead of vectors. It seems that the performance depends on the compiler, on some they're faster, on some they're slower. Which I guess was inevitable. So, I'm dropping that idea. thanks, Daniel

Hi, again. I have done some more tinkering with the compile time constant typeof emulation. It now compiles for VC 6.5,VC 7.0 and GCC 3.4 I have split the one argument constant into two. One per typeof instance, and one to iterate (from 1 to N) down the "type tree". This method also improves on the previous in that when a type T has been deduced using BOOST_TYPEOF, the type is reused when BOOST_TYPEOF is used again. Anyhow, I have also implemented a feature for improving on the 64 template depth restriction. Haven't got access to an EDG based compiler, but the implementation worked fine with VC 6.5 I have also looked at the newes implementation of your integral encoding, Arkadiy, and it is basically the same as I am doing. I use one value to encode an integer, with the exception of 0xffffffff and 0xfffffffe, but the side effect is that I can not use arrays to create a size for sizeof. I have to use a struct containing two arrays, and I don't know if this is as portable, so I should perhaps adapt to your way of doing it. Also, your version of TYPEOF_REGISTER_TEMPLATE_X differs from mine. Where I use TYPEOF_REGISTER_TEMPLATE(some_class,2,(typename,unsigned int)), you use TYPEOF_REGISTER_TEMPLATE_X(some_class,(typename)(unsigned int)) My has the advantage of looking more like a template argument list. Your has the advantage of not having to specify the number of template arguments... Which one is best? I haven't had time to do any work on integrating the two solutions yet, my typeof implementation is not mature enough, I guess, as I haven't tested it against any other unfortunate compilers without partial template specialization support. Help on this would be greatly appreciated. -- Peder Holt http://groups.yahoo.com/group/boost/files/typeof_fast.zip

Peder Holt <peder.holt@gmail.com> writes:
Hi, again. I have done some more tinkering with the compile time constant typeof emulation. It now compiles for VC 6.5,VC 7.0 and GCC 3.4 I have split the one argument constant into two. One per typeof instance, and one to iterate (from 1 to N) down the "type tree". This method also improves on the previous in that when a type T has been deduced using BOOST_TYPEOF, the type is reused when BOOST_TYPEOF is used again.
How could it not be? Templates only have one point of instantiation for any given set of arguments.
Anyhow, I have also implemented a feature for improving on the 64 template depth restriction. Haven't got access to an EDG based compiler, but the implementation worked fine with VC 6.5
I have also looked at the newes implementation of your integral encoding, Arkadiy, and it is basically the same as I am doing. I use one value to encode an integer, with the exception of 0xffffffff and 0xfffffffe, but the side effect is that I can not use arrays to create a size for sizeof. I have to use a struct containing two arrays, and I don't know if this is as portable, so I should perhaps adapt to your way of doing it.
Also, your version of TYPEOF_REGISTER_TEMPLATE_X differs from mine. Where I use TYPEOF_REGISTER_TEMPLATE(some_class,2,(typename,unsigned int)), you use TYPEOF_REGISTER_TEMPLATE_X(some_class,(typename)(unsigned int))
My has the advantage of looking more like a template argument list. Your has the advantage of not having to specify the number of template arguments...
Which one is best?
I haven't had time to do any work on integrating the two solutions yet, my typeof implementation is not mature enough, I guess, as I haven't tested it against any other unfortunate compilers without partial template specialization support. Help on this would be greatly appreciated.
Sounds like integration instead of a big #ifdef switch might be appropriate now (?) -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote
Sounds like integration instead of a big #ifdef switch might be appropriate now (?)
Very problematic, IMO. The implementations are so fundamentally different, that there isn't much opportunity for reuse, except maybe small things, like BOOST_TYPEOF_UNIQUE_ID() or converting "unsigned int" into "unsigned_int" for the purpose of handling integral template parameters. Also please imagine the review of such a unified library. Reviewers would be forced to simultaniously judge two totally different, even alternative ideas... Please correct me if I am wrong. Regards, Arkadiy

On Mon, 13 Sep 2004 17:19:33 -0400, David Abrahams <dave@boost-consulting.com> wrote:
Peder Holt <peder.holt@gmail.com> writes:
Hi, again. I have done some more tinkering with the compile time constant typeof emulation. It now compiles for VC 6.5,VC 7.0 and GCC 3.4 I have split the one argument constant into two. One per typeof instance, and one to iterate (from 1 to N) down the "type tree". This method also improves on the previous in that when a type T has been deduced using BOOST_TYPEOF, the type is reused when BOOST_TYPEOF is used again.
How could it not be? Templates only have one point of instantiation for any given set of arguments.
With my previous implementation, the next available compile time constant was included in the encoding and decoding structs, forcing new instantiations to be created for every typeof This has now been fixed.
Anyhow, I have also implemented a feature for improving on the 64 template depth restriction. Haven't got access to an EDG based compiler, but the implementation worked fine with VC 6.5
I have also looked at the newes implementation of your integral encoding, Arkadiy, and it is basically the same as I am doing. I use one value to encode an integer, with the exception of 0xffffffff and 0xfffffffe, but the side effect is that I can not use arrays to create a size for sizeof. I have to use a struct containing two arrays, and I don't know if this is as portable, so I should perhaps adapt to your way of doing it.
Also, your version of TYPEOF_REGISTER_TEMPLATE_X differs from mine. Where I use TYPEOF_REGISTER_TEMPLATE(some_class,2,(typename,unsigned int)), you use TYPEOF_REGISTER_TEMPLATE_X(some_class,(typename)(unsigned int))
My has the advantage of looking more like a template argument list. Your has the advantage of not having to specify the number of template arguments...
Which one is best?
I haven't had time to do any work on integrating the two solutions yet, my typeof implementation is not mature enough, I guess, as I haven't tested it against any other unfortunate compilers without partial template specialization support. Help on this would be greatly appreciated.
Sounds like integration instead of a big #ifdef switch might be appropriate now (?)
The parts that can be reused, are: - much of the scheme for registering templates with integral constants - All the decoding stuff (with different iterators) The difference in encoding is basically that of iterators as well, My input to the encode struct is 2 integers (which are incremented at each step) Arkadiys input to the encode struct is a vector of integers. But I agree, the difference between encoding is rather large, and it may be more confusing than not to make a single implementation of it. -- Peder Holt
-- Dave Abrahams Boost Consulting http://www.boost-consulting.com
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

"Peder Holt" <peder.holt@gmail.com> wrote
With my previous implementation, the next available compile time constant was included in the encoding and decoding structs, forcing new instantiations to be created for every typeof This has now been fixed.
What I totaly don't understand is how you can get the compiler reuse templates, and still set the compile-time variables. As far as I understand, setting a compile-time variable is a side-effect of a template instantiation, which IMO contradicts the reuse...
The parts that can be reused, are: - much of the scheme for registering templates with integral constants
I agree here.
- All the decoding stuff (with different iterators)
Are you going to make an MPL sequence from compile time variables?
The difference in encoding is basically that of iterators as well,
My input to the encode struct is 2 integers (which are incremented at each step) Arkadiys input to the encode struct is a vector of integers. But I agree, the difference between encoding is rather large, and it may be more confusing than not to make a single implementation of it.
We would most likely end up with, instead one big #ifdef switch, a dozen #ifdef switches in every file... Regards, Arkadiy

On Tue, 14 Sep 2004 06:04:12 -0400, Arkadiy Vertleyb <vertleyb@hotmail.com> wrote:
"Peder Holt" <peder.holt@gmail.com> wrote
With my previous implementation, the next available compile time constant was included in the encoding and decoding structs, forcing new instantiations to be created for every typeof This has now been fixed.
What I totaly don't understand is how you can get the compiler reuse templates, and still set the compile-time variables. As far as I understand, setting a compile-time variable is a side-effect of a template instantiation, which IMO contradicts the reuse...
Earlier, I created a function with a return value that instantiated a compile time constant. encode_type<T,NEXT_INDEX()> start(const T&); On VC 6.5, anyway, this caused TYPEOF to be reinstantiated, but VC 6.5 isn't the most conforming compiler on the market, so this may have been another of its bugs :)
The parts that can be reused, are: - much of the scheme for registering templates with integral constants
I agree here.
- All the decoding stuff (with different iterators)
Are you going to make an MPL sequence from compile time variables?
My implementation of decoding: template<> struct decode_impl<CONST_ID> { template<typename Iter> struct decoder { typedef typename decode_impl<Iter::value>::decoder<BOOST_DEDUCED_TYPENAME Iter::next> next_decoder; typedef typename next_decoder::type const type; typedef typename next_decoder::end end; }; }; Your implementation of decode: template<class Iter> struct decode_type_impl<mpl::int_<CONST_ID>, Iter> { typedef decode_type<Iter> d1; typedef const typename d1::type type; typedef typename d1::iter iter; }; Where template<class Iter> struct decode_type : decode_type_impl<typename mpl::deref<Iter>::type, typename mpl::next<Iter>::type> {}; As far as I can see, these implementation are very similar. All that is needed is to either implement the proper mpl traits for my compile time constants iterator, or create a common abstraction for the next and deref<Iter> (value, in my implementation)
The difference in encoding is basically that of iterators as well,
My input to the encode struct is 2 integers (which are incremented at each step) Arkadiys input to the encode struct is a vector of integers. But I agree, the difference between encoding is rather large, and it may be more confusing than not to make a single implementation of it.
We would most likely end up with, instead one big #ifdef switch, a dozen #ifdef switches in every file...
Agree. I'll take a look at it anyway. -- Peder Holt
Regards, Arkadiy
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

"Peder Holt" <peder.holt@gmail.com> wrote <vertleyb@hotmail.com> wrote:
What I totaly don't understand is how you can get the compiler reuse templates, and still set the compile-time variables. As far as I understand, setting a compile-time variable is a side-effect of a template instantiation, which IMO contradicts the reuse...
Earlier, I created a function with a return value that instantiated a compile time constant. encode_type<T,NEXT_INDEX()> start(const T&);
Could you explain this in more detail? I am pretty uncomfortable with the whole idea of compile-time variables... As far as I understand now, to set them, the compiler needs to instantiate the template which contains the code that sets the variable, something like this: template<> struct encode { SET(compile_var); }; If encode is reused (not instantiated), the variable is not set. I believe I raised this question some time ago, and you said you added an additional parameter to force the instantiation... How then it can be reused?
My implementation of decoding: template<> struct decode_impl<CONST_ID> { template<typename Iter> struct decoder { typedef typename decode_impl<Iter::value>::decoder<BOOST_DEDUCED_TYPENAME Iter::next> next_decoder; typedef typename next_decoder::type const type; typedef typename next_decoder::end end; }; }; Your implementation of decode: template<class Iter> struct decode_type_impl<mpl::int_<CONST_ID>, Iter> { typedef decode_type<Iter> d1; typedef const typename d1::type type; typedef typename d1::iter iter; };
Where template<class Iter> struct decode_type : decode_type_impl<typename mpl::deref<Iter>::type, typename mpl::next<Iter>::type> {};
As far as I can see, these implementation are very similar.
I actually don't think they are VERY similar. We would write more code trying to fit them together, then both of them already have. This is a matter of taste, of course, but I am against reuse in such cases. This would just put more fog on things that are not that easy to understand in the first place. Regards, Arkadiy

"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
I actually don't think they are VERY similar. We would write more code trying to fit them together, then both of them already have. This is a matter of taste, of course, but I am against reuse in such cases. This would just put more fog on things that are not that easy to understand in the first place.
Okay, withdrawn. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

On Tue, 14 Sep 2004 09:31:59 -0400, Arkadiy Vertleyb <vertleyb@hotmail.com> wrote:
"Peder Holt" <peder.holt@gmail.com> wrote
<vertleyb@hotmail.com> wrote:
What I totaly don't understand is how you can get the compiler reuse templates, and still set the compile-time variables. As far as I understand, setting a compile-time variable is a side-effect of a template instantiation, which IMO contradicts the reuse...
Earlier, I created a function with a return value that instantiated a compile time constant. encode_type<T,NEXT_INDEX()> start(const T&);
Could you explain this in more detail? I am pretty uncomfortable with the whole idea of compile-time variables... As far as I understand now, to set them, the compiler needs to instantiate the template which contains the code that sets the variable, something like this:
template<> struct encode { SET(compile_var); };
If encode is reused (not instantiated), the variable is not set.
I believe I raised this question some time ago, and you said you added an additional parameter to force the instantiation... How then it can be reused?
My implementation of decoding: template<> struct decode_impl<CONST_ID> { template<typename Iter> struct decoder { typedef typename decode_impl<Iter::value>::decoder<BOOST_DEDUCED_TYPENAME Iter::next> next_decoder; typedef typename next_decoder::type const type; typedef typename next_decoder::end end; }; }; Your implementation of decode: template<class Iter> struct decode_type_impl<mpl::int_<CONST_ID>, Iter> { typedef decode_type<Iter> d1; typedef const typename d1::type type; typedef typename d1::iter iter; };
Where template<class Iter> struct decode_type : decode_type_impl<typename mpl::deref<Iter>::type, typename mpl::next<Iter>::type> {};
As far as I can see, these implementation are very similar.
I actually don't think they are VERY similar. We would write more code trying to fit them together, then both of them already have. This is a matter of taste, of course, but I am against reuse in such cases. This would just put more fog on things that are not that easy to understand in the first place.
I don't agree. Consider the following code: #ifdef BOOST_NO_PARTIAL_TEMPLATE_SPECIALIZATION #define DECODE_TYPE_IMPL_BEGIN(name,Iter)\ template<>\ struct decode_type_impl<CONST_ID>{\ template<typename Iter>\ struct decoder { #define DECODE_TYPE_IMPL_END() };}; #define DECODE_TYPE(Iter)\ typename decode_impl<Iter::value>::decoder<BOOST_DEDUCED_TYPENAME Iter::next> #else #define DEDOCE_TYPE_IMPL_BEGIN(name,Iter)\ template<typename Iter> struct decode_type_impl<mpl::int_<name>,Iter>{ #define DECODE_TYPE_IMPL_END() }; #define DECODE_TYPE(Iter) decode_type<Iter> #end This would allow us to create a unified decode implementation: DECODE_TYPE_IMPL_BEGIN(CONST_ID) typedef DECODE_TYPE(Iter) d1; typedef typename d1::type const type; typedef typename d1::iter iter; DECODE_TYPE_IMPL_END() DECODE_TYPE_IMPL_BEGIN(PTR_ID) typedef DECODE_TYPE(Iter) d1; typedef typename d1::type* type; typedef typename d1::iter iter; DECODE_TYPE_IMPL_END() I think this is at least worth consideration. -- Peder Holt
Regards, Arkadiy
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

"Peder Holt" <peder.holt@gmail.com> wrote
On Tue, 14 Sep 2004 09:31:59 -0400, Arkadiy Vertleyb <vertleyb@hotmail.com> wrote:
I actually don't think they are VERY similar. We would write more code trying to fit them together, then both of them already have. This is a matter of taste, of course, but I am against reuse in such cases. This would just put more fog on things that are not that easy to understand in the first place.
I don't agree. Consider the following code:
#ifdef BOOST_NO_PARTIAL_TEMPLATE_SPECIALIZATION #define DECODE_TYPE_IMPL_BEGIN(name,Iter)\ template<>\ struct decode_type_impl<CONST_ID>{\ template<typename Iter>\ struct decoder {
#define DECODE_TYPE_IMPL_END() };}; #define DECODE_TYPE(Iter)\ typename decode_impl<Iter::value>::decoder<BOOST_DEDUCED_TYPENAME Iter::next>
#else
#define DEDOCE_TYPE_IMPL_BEGIN(name,Iter)\ template<typename Iter> struct decode_type_impl<mpl::int_<name>,Iter>{
#define DECODE_TYPE_IMPL_END() }; #define DECODE_TYPE(Iter) decode_type<Iter>
#end
This would allow us to create a unified decode implementation:
DECODE_TYPE_IMPL_BEGIN(CONST_ID) typedef DECODE_TYPE(Iter) d1; typedef typename d1::type const type; typedef typename d1::iter iter; DECODE_TYPE_IMPL_END()
DECODE_TYPE_IMPL_BEGIN(PTR_ID) typedef DECODE_TYPE(Iter) d1; typedef typename d1::type* type; typedef typename d1::iter iter; DECODE_TYPE_IMPL_END()
I think this is at least worth consideration.
A matter of taste, as I said. I don't think the above code buys us much in terms of reuse (the amount of code is apprximately the same, if not more), but it definitely complicates understanding of things... I would prefer a more light-weight integration, where some utilities can be shared, of course, such as integral encoding, unique ID generation, etc. However, trying to totaly blend systems together would IMO be harmful for both of them, considering major differences inevitably imposed by different techniques allowed by the target compilers. Regards, Arkadiy

On Wed, 15 Sep 2004 09:00:10 -0400, Arkadiy Vertleyb <vertleyb@hotmail.com> wrote:
"Peder Holt" <peder.holt@gmail.com> wrote
On Tue, 14 Sep 2004 09:31:59 -0400, Arkadiy Vertleyb
<vertleyb@hotmail.com> wrote:
I actually don't think they are VERY similar. We would write more code trying to fit them together, then both of them already have. This is a matter of taste, of course, but I am against reuse in such cases. This would just put more fog on things that are not that easy to understand in the first place.
I don't agree. Consider the following code:
#ifdef BOOST_NO_PARTIAL_TEMPLATE_SPECIALIZATION #define DECODE_TYPE_IMPL_BEGIN(name,Iter)\ template<>\ struct decode_type_impl<CONST_ID>{\ template<typename Iter>\ struct decoder {
#define DECODE_TYPE_IMPL_END() };}; #define DECODE_TYPE(Iter)\ typename decode_impl<Iter::value>::decoder<BOOST_DEDUCED_TYPENAME Iter::next>
#else
#define DEDOCE_TYPE_IMPL_BEGIN(name,Iter)\ template<typename Iter> struct decode_type_impl<mpl::int_<name>,Iter>{
#define DECODE_TYPE_IMPL_END() }; #define DECODE_TYPE(Iter) decode_type<Iter>
#end
This would allow us to create a unified decode implementation:
DECODE_TYPE_IMPL_BEGIN(CONST_ID) typedef DECODE_TYPE(Iter) d1; typedef typename d1::type const type; typedef typename d1::iter iter; DECODE_TYPE_IMPL_END()
DECODE_TYPE_IMPL_BEGIN(PTR_ID) typedef DECODE_TYPE(Iter) d1; typedef typename d1::type* type; typedef typename d1::iter iter; DECODE_TYPE_IMPL_END()
I think this is at least worth consideration.
A matter of taste, as I said. I don't think the above code buys us much in terms of reuse (the amount of code is apprximately the same, if not more), but it definitely complicates understanding of things...
I would prefer a more light-weight integration, where some utilities can be shared, of course, such as integral encoding, unique ID generation, etc. However, trying to totaly blend systems together would IMO be harmful for both of them, considering major differences inevitably imposed by different techniques allowed by the target compilers.
Ok. What remains then is to decide which of the two template registration schemes should be used: REGISTER_TEMPLATE(type,2,(typename,int)) or REGISTER_TEMPLATE(type,(typename)(int)) -- Peder Holt
Regards, Arkadiy
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Peder Holt <peder.holt@gmail.com> writes:
What remains then is to decide which of the two template registration schemes should be used: REGISTER_TEMPLATE(type,2,(typename,int)) or REGISTER_TEMPLATE(type,(typename)(int))
I generally dislike interfaces that require counting, so prefer #2. However, in MPL's new assertions we were able to avoid the big problems with parenthesization and commas in the PP by actually using a parenthesized macro argument to form the argument list of a function type, so in: BOOST_MPL_ASSERT((is_same<std::pair<int,int>, B>)) a function type is formed, something like: int (*)(is_same<std::pair<int,int>, B>) I don't know whether that's possible in this case, but if so it's a significant usability win. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote
REGISTER_TEMPLATE(type,2,(typename,int)) or REGISTER_TEMPLATE(type,(typename)(int))
I generally dislike interfaces that require counting, so prefer #2. However, in MPL's new assertions we were able to avoid the big problems with parenthesization and commas in the PP by actually using a parenthesized macro argument to form the argument list of a function type, so in:
BOOST_MPL_ASSERT((is_same<std::pair<int,int>, B>))
a function type is formed, something like:
int (*)(is_same<std::pair<int,int>, B>)
I don't know whether that's possible in this case, but if so it's a significant usability win.
I am afraid this will not help in this particular case, since we need all the items separately. For example, in my implementation, "unsigned int" is converted to something like BOOST_TYPEOF_unsigned_int, which expands into a PP sequence containing items necessary to create the case for unsigned int. OTOH, "typename" or "class" is converted to another PP sequence. The differences between resulting PP sequences are on the PP level, for example to extract the result of decoding one of the following has to be used: typename X::type; // for typename/class X::value; // for integrals This kind of differences have to be resolved at PP time. OTOH, I might be missing some other posibilities... Regards, Arkadiy

"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
templates, and still set the compile-time variables. As far as I ^^^^^^^^^^^^^^^^^^^^^^
Excuse me, but what the heck does that mean? As far as I know, there's no mutable data at compile-time. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:uk6uxvv7a.fsf@boost-consulting.com...
"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
templates, and still set the compile-time variables. As far as I ^^^^^^^^^^^^^^^^^^^^^^
Excuse me, but what the heck does that mean? As far as I know, there's no mutable data at compile-time.
Let Peder explain the idea his implementation is based upon. Frankly speaking, I think the whole concept of "compile-time variables" should be considered and accepted or rejected before we discuss any implementation based on this idea. Regards, Arkadiy

David Abrahams wrote:
"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
templates, and still set the compile-time variables. As far as I
^^^^^^^^^^^^^^^^^^^^^^
Excuse me, but what the heck does that mean? As far as I know, there's no mutable data at compile-time.
As far as I understand it, by creating a type like: template<int N> struct counter : counter<N - 1> {}; template<> struct counter<0> {}; you can create a counter by overloading on increasing values of N. So, you first create a function: size_type<0>::type check(...); And sizeof(check(counter<MAX_VALUE>)) == 0. Then overload it with: size_type<1>::type check(counter<1>); And sizeof(check(counter<MAX_VALUE>)) == 1. And so on. You can then overload another function based on the current value of counter to get a 'variable'. Daniel Wallin's implementation is at: http://lists.boost.org/MailArchives/boost/msg69842.php He adds an extra type parameter to get multiple counters. The function overloads are generated by friend functions of a templated class, which is probably not standard compliant - it doesn't work in strict mode on EDG compilers. Goran Mitrovic's version declares the functions in macros, so it works in strict mode, but this means you can't use it from inside classes, so it's no use here. He posted it at: http://lists.boost.org/MailArchives/boost/msg69850.php In Peder's code the name isn't really appropriate as most of the data is actually constant, the only actual variable is the counter is used to generate a unique index for each encoded type. Although all the data is stored using these function overloads. Daniel

Daniel James <daniel@calamity.org.uk> writes:
David Abrahams wrote:
"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
templates, and still set the compile-time variables. As far as I ^^^^^^^^^^^^^^^^^^^^^^ Excuse me, but what the heck does that mean? As far as I know, there's no mutable data at compile-time.
As far as I understand it, by creating a type like:
template<int N> struct counter : counter<N - 1> {}; template<> struct counter<0> {};
you can create a counter by overloading on increasing values of N. So, you first create a function:
size_type<0>::type check(...);
And sizeof(check(counter<MAX_VALUE>)) == 0. Then overload it with:
size_type<1>::type check(counter<1>);
And sizeof(check(counter<MAX_VALUE>)) == 1. And so on.
Hah. Relies on ODR violation. But we agreed that was okay for Arkadiy's implementation too, as long as it actually worked, because real typeof is coming. As usual, I worry about what happens on compilers with link-time instantiation.
You can then overload another function based on the current value of counter to get a 'variable'. Daniel Wallin's implementation is at:
http://lists.boost.org/MailArchives/boost/msg69842.php
He adds an extra type parameter to get multiple counters. The function overloads are generated by friend functions of a templated class, which is probably not standard compliant - it doesn't work in strict mode on EDG compilers.
Right. Boost.Python used to do that. Came as a terrible shock when I discovered it can't work.
Goran Mitrovic's version declares the functions in macros, so it works in strict mode, but this means you can't use it from inside classes
Because the declaration disappears when the class scope is exited and the counter will be reused?
so it's no use here. He posted it at:
http://lists.boost.org/MailArchives/boost/msg69850.php
In Peder's code the name isn't really appropriate as most of the data is actually constant, the only actual variable is the counter is used to generate a unique index for each encoded type. Although all the data is stored using these function overloads.
Thanks for the explanation. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Daniel James <daniel@calamity.org.uk> writes:
He adds an extra type parameter to get multiple counters. The function overloads are generated by friend functions of a templated class, which is probably not standard compliant - it doesn't work in strict mode on EDG compilers.
Right. Boost.Python used to do that. Came as a terrible shock when I discovered it can't work.
Peder Holt tried to fix this by making the class a parameter to the function, here: http://lists.boost.org/MailArchives/boost/msg70381.php but it still doesn't work in strict mode. I guess because it relies on an implicit cast from int to the type, but I'm not sure. I don't fully understand why the operators library works and this doesn't.
Goran Mitrovic's version declares the functions in macros, so it works in strict mode, but this means you can't use it from inside classes
Because the declaration disappears when the class scope is exited and the counter will be reused?
Yes. Incidently, it could be used for registering types since the type registration macros are invoked in the global namespace. I think that's why it was posted in the first place.

Daniel James <daniel@calamity.org.uk> writes:
David Abrahams wrote:
Daniel James <daniel@calamity.org.uk> writes:
He adds an extra type parameter to get multiple counters. The function overloads are generated by friend functions of a templated class, which is probably not standard compliant - it doesn't work in strict mode on EDG compilers. Right. Boost.Python used to do that. Came as a terrible shock when I discovered it can't work.
Peder Holt tried to fix this by making the class a parameter to the function, here:
http://lists.boost.org/MailArchives/boost/msg70381.php
but it still doesn't work in strict mode. I guess because it relies on an implicit cast from int to the type, but I'm not sure. I don't fully understand why the operators library works and this doesn't.
Because those friend functions can only be found via ADL, and naturally ADL doesn't look in the namespaces of classes to which the arguments can be implicitly converted. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Daniel James <daniel@calamity.org.uk> writes:
but it still doesn't work in strict mode. I guess because it relies on an implicit cast from int to the type, but I'm not sure. I don't fully understand why the operators library works and this doesn't.
Because those friend functions can only be found via ADL, and naturally ADL doesn't look in the namespaces of classes to which the arguments can be implicitly converted.
Well that's embarassingly simple. Thanks for that.

On Wed, 15 Sep 2004 11:42:06 +0100, Daniel James <daniel@calamity.org.uk> wrote:
David Abrahams wrote:
Daniel James <daniel@calamity.org.uk> writes:
but it still doesn't work in strict mode. I guess because it relies on an implicit cast from int to the type, but I'm not sure. I don't fully understand why the operators library works and this doesn't.
Because those friend functions can only be found via ADL, and naturally ADL doesn't look in the namespaces of classes to which the arguments can be implicitly converted.
Well that's embarassingly simple. Thanks for that.
From a quick search in boost/config, the compilers supported by boost
I guess there is no way of making compile time constants work with compliant compilers... But then compliant compilers don't need the compile time constants typeof in the first place, as they can use Arkadiys implementation. that does not support partial template specialization, are: VC 6.5 and 7.0 Sunpro 5.2 and earler MPW VC 6.5 and 7.0 are covered by my compile time constants implementation. The question is whether the other two compilers are compilant enough to have implemented ADL correctly :) -- Peder Holt
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Peder Holt <peder.holt@gmail.com> writes:
From a quick search in boost/config, the compilers supported by boost that does not support partial template specialization, are: VC 6.5 and 7.0 Sunpro 5.2 and earler MPW
VC 6.5 and 7.0 are covered by my compile time constants implementation. The question is whether the other two compilers are compilant enough to have implemented ADL correctly :)
Definitely not. But I'm not sure which parts of the ADL rules you're interested in here. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

On Wed, 15 Sep 2004 09:14:02 -0400, David Abrahams <dave@boost-consulting.com> wrote:
Peder Holt <peder.holt@gmail.com> writes:
From a quick search in boost/config, the compilers supported by boost that does not support partial template specialization, are: VC 6.5 and 7.0 Sunpro 5.2 and earler MPW
VC 6.5 and 7.0 are covered by my compile time constants implementation. The question is whether the other two compilers are compilant enough to have implemented ADL correctly :)
Definitely not. But I'm not sure which parts of the ADL rules you're interested in here.
I am not familiar with the full set of ADL rules, so I'll examplify instead. I'll list the most important concepts I rely on: As Daniel pointed out, the following construct is rather essential: template<int N> struct encode_counter : encode_counter<N - 1> {}; template<> struct encode_counter<1> {}; The template recursion should be reasonably large (eg. 64 or above) This limits the number of typeofs that can be used per compilation units This limit is not absolute. I have a method of getting around this limit. template<unsigned N> struct sizer { BOOST_STATIC_CONSTANT(unsigned,value=N); typedef char(&type)[value]; }; sizer<1>::type encode_value(...); sizer<1>::type encode_index(...); #define BOOST_TYPEOF_INDEX() (sizeof(encode_index((encode_counter<MAX_RECURSION_DEPTH>*)0))) template<typename T> struct encode_type { BOOST_STATIC_CONSTANT(unsigned,value=BOOST_TYPEOF_INDEX()); BOOST_STATIC_CONSTANT(unsigned,next=value+1); friend sizer<next> encode_index(encode_counter<next>*); sizer<value>::type resize; }; template<typename T> encode_type<T> encode_start(T const&); void main() { int index_before=BOOST_TYPEOF_INDEX() //should return 1 double* a; int index=sizeof(encode_start(a)); //should install a new encode_index function int index_after=BOOST_TYPEOF_INDEX() //should return 2, hence it must find the encode_index function embedded in the encode_type struct. } That is, Instantiating a template class with a friend function, should expose this function uncritically. In addition to the above requirements, I expect the compiler to recursively instantiate template types, as in the dummy-example below: template<typename T,unsigned Index,unsigned Position> struct dummy_encode2 { sizer<DOUBLE_ID>::type encode_value(sizer<Index> const*,sizer<Position>*); }; template<typename T,unsigned Index,unsigned Position=1> struct dummy_encode1 { BOOST_STATIC_CONSTANT(unsigned,instantiate=sizeof(dummy_encode2<T,Index,Position+1>)); sizer<POINTER_ID>::type encode_value(sizer<Index> const*,sizer<Position>*); }; Then the expression sizeof(dummy_encode1<double*,1>) should install two encode_value functions. -- Peder Holt
-- Dave Abrahams Boost Consulting http://www.boost-consulting.com
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Peder Holt wrote:
On Wed, 15 Sep 2004 09:14:02 -0400, David Abrahams
Definitely not. But I'm not sure which parts of the ADL rules you're interested in here.
I am not familiar with the full set of ADL rules, so I'll examplify instead.
I don't think it's the ADL rules that matter. It's whether the compiler adds friend methods to the namespace containing the class. This appears to be nonstandard but done by most compilers. Daniel

Daniel James <daniel@calamity.org.uk> writes:
Peder Holt wrote:
On Wed, 15 Sep 2004 09:14:02 -0400, David Abrahams
Definitely not. But I'm not sure which parts of the ADL rules you're interested in here. I am not familiar with the full set of ADL rules, so I'll examplify instead.
I don't think it's the ADL rules that matter. It's whether the compiler adds friend methods to the namespace containing the class. This appears to be nonstandard but done by most compilers.
Strictly speaking, in a conforming compiler they are added to the namespace but can only be found by ADL unless there's an additional out-of-class declaration (which, of course, can't be made to work automatically by templatizing it). -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

Peder Holt <peder.holt@gmail.com> writes:
I am not familiar with the full set of ADL rules, so I'll examplify instead. I'll list the most important concepts I rely on:
As Daniel pointed out, the following construct is rather essential:
template<int N> struct encode_counter : encode_counter<N - 1> {}; template<> struct encode_counter<1> {};
The template recursion should be reasonably large (eg. 64 or above) This limits the number of typeofs that can be used per compilation units
This limit is not absolute. I have a method of getting around this limit.
template<unsigned N> struct sizer { BOOST_STATIC_CONSTANT(unsigned,value=N); typedef char(&type)[value]; };
sizer<1>::type encode_value(...); sizer<1>::type encode_index(...);
#define BOOST_TYPEOF_INDEX() (sizeof(encode_index((encode_counter<MAX_RECURSION_DEPTH>*)0)))
template<typename T> struct encode_type { BOOST_STATIC_CONSTANT(unsigned,value=BOOST_TYPEOF_INDEX());
BOOST_STATIC_CONSTANT(unsigned,next=value+1); friend sizer<next> encode_index(encode_counter<next>*); sizer<value>::type resize; };
template<typename T> encode_type<T> encode_start(T const&);
void main() { int index_before=BOOST_TYPEOF_INDEX() //should return 1 double* a; int index=sizeof(encode_start(a)); //should install a new encode_index function int index_after=BOOST_TYPEOF_INDEX() //should return 2, hence it must find the encode_index function embedded in the encode_type struct. }
That is, Instantiating a template class with a friend function, should expose this function uncritically.
That's nonconforming, but works in vc6 (and most other major compilers).
In addition to the above requirements, I expect the compiler to recursively instantiate template types, as in the dummy-example below:
template<typename T,unsigned Index,unsigned Position> struct dummy_encode2 { sizer<DOUBLE_ID>::type encode_value(sizer<Index> const*,sizer<Position>*); };
template<typename T,unsigned Index,unsigned Position=1> struct dummy_encode1 { BOOST_STATIC_CONSTANT(unsigned,instantiate=sizeof(dummy_encode2<T,Index,Position+1>)); sizer<POINTER_ID>::type encode_value(sizer<Index> const*,sizer<Position>*); };
Then the expression sizeof(dummy_encode1<double*,1>) should install two encode_value functions.
Sorry, I don't see any recursion here. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

In addition to the above requirements, I expect the compiler to recursively instantiate template types, as in the dummy-example below:
template<typename T,unsigned Index,unsigned Position> struct dummy_encode2 { sizer<DOUBLE_ID>::type encode_value(sizer<Index> const*,sizer<Position>*); };
template<typename T,unsigned Index,unsigned Position=1> struct dummy_encode1 { BOOST_STATIC_CONSTANT(unsigned,instantiate=sizeof(dummy_encode2<T,Index,Position+1>)); sizer<POINTER_ID>::type encode_value(sizer<Index> const*,sizer<Position>*); };
Then the expression sizeof(dummy_encode1<double*,1>) should install two encode_value functions.
Sorry, I don't see any recursion here.
Here is a simplified example from my implementation: template<> struct encode_impl<POINTER_ID> { template<typename T,unsigned Index> struct encoder { enum(instantiate=sizeof(encode_modifier<T,Index+1>::type)); <- Recursion } }; template<typename T,unsigned Index> encode_impl<POINTER_ID>::encoder<T,Index> encode(void(*)(mpl::int_<POINTER_ID>,T*,mpl::int_<Index>)); where template<typename T,unsigned Index> struct encode_modifier { enum(instantiate=sizeof(encode(select_modifier<T>::type,T,mpl::int_<Index>))); <-Recursion }; Here: encode_modifier is instantiated with a type T. After analyzing the type, type deduction is done through the encode function. The encode function (or the sizeof operation) instantiates an encode_impl<N>::encoder<...> struct, which in turn instantiate one or more encode_modifier<...> structs which in turn ... What I fear is that some compiler will want to optimalize away the instantiate enums, as it is never referred to in the source. -- Peder Holt
-- Dave Abrahams Boost Consulting http://www.boost-consulting.com
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Just for fun, I implemented a compile-time-constant vector compatible with mpl. I used Arkadiy's source as a basis, and implemented a typeof_ctc (parallel to the typeof_impl) and added an optional compile to which one to include. Everything else I left untouched. On VC 7.1, the tests ran at a comparably similar speed, Original version: 12.7 (approx) CTC version: 10.9 (approx) I hereby withdraw all doubts about the efficiency of mpl::vector Still, this shows that our two solutions are not that far apart. What is different, is the encoding of the integers, and of cource the fact that compilers that don't support partial template specialization needs special treatment...

"Daniel James" <daniel@calamity.org.uk> wrote
When running your tests, the intel linux compiler gives this warning:
main.cpp(143): warning #858: type qualifier on return type is meaningless const int cf();
which means that BOOST_TYPEOF_PRESERVE_LVALUE(cf()) gives int, and your test fails, so you might want to remove that test.
Removed.
Also neither encode_signed nor encode_unsigned seem to work for zero values. I think encode_signed is missing a check for zero, and I guess encode_unsigned should add 1 to value when encoding, and then subtract 1 when decoding - or have a special case like encode_signed does.
Fixed. I actualy re-wrote the integral encoding part, taking more bit-wise approach, which allowed me to handle all integral types (signed, unsigned and boolean) an a uniform manner. I encode 4-byte types using two integers (because of zero, and also limitations on the array size I ran into in GCC), and shorter types using just one. I also switched from mpl::int_ to mpl::size_t. http://groups.yahoo.com/group/boost/files/typeof.zip Thanks, Arkadiy
participants (4)
-
Arkadiy Vertleyb
-
Daniel James
-
David Abrahams
-
Peder Holt