
Hello everybody, A couple of weeks ago I initiated the discussion about simple typeof emulation, and provided an example on how it can be done with the help of MPL vector of integral constants. I then used MS-specific __COUNTER__ macro to spare the user from specifying the unique integer for each registered class/template. I hoped to find a way to do something similar in standard C++, and asked preprocessor experts for help. Indeed, Paul Mensonides suggested such a way. At first I thought that it will not be good enough replacement for the __COUNTER__, but then I realized that I was wrong. So I re-worked my implementation of typeof, and now offerring it to your attention. Besides removing __COUNTER__, I used the Boost.Preprocessor library to cleanup the implementation. Also, after the discussion with Jody Hagins, I put the encoding/decoding classes inside anonimous namespace to avoid collisions between translation units. The provided example shows what registration needs to be done to calculate the type of a lambda functor: #include BOOST_TYPEOF_BEGIN_REGISTRATION() BOOST_TYPEOF_REGISTER_TEMPLATE(tuples::tuple, 2); BOOST_TYPEOF_REGISTER_TEMPLATE(lambda_functor, 1); BOOST_TYPEOF_REGISTER_TEMPLATE(lambda_functor_base, 2); BOOST_TYPEOF_REGISTER_TEMPLATE(relational_action, 1); BOOST_TYPEOF_REGISTER_TEMPLATE(logical_action, 1); BOOST_TYPEOF_REGISTER_TYPE(greater_action); BOOST_TYPEOF_REGISTER_TYPE(less_action); BOOST_TYPEOF_REGISTER_TYPE(and_action); BOOST_TYPEOF_REGISTER_TYPE(placeholder<1>); BOOST_TYPEOF_REGISTER_TYPE(placeholder<2>); #include BOOST_TYPEOF_END_REGISTRATION() main() { BOOST_TYPEOF_ALLOCATE(fun, _1 > 15 && _2 < 20); // <unspecified> fun(_1
15 && _2 < 20); int n = 19; assert(fun(n, n)); }
One of remaining problems is inability to process top-level consts and references, that get eatten up at the function level, before the partial template specialization gets involved... Although I believe that I don't do anything non-standard now, unfortunately I can't prove it myself. The only compiler I have besides MSVC is GCC, and it gives me "sorry not implemented" on something as simple as mpl::int_<sizeof(foo(...))>... Besides, GCC already has a typeof. Would anybody be so kind as to try the "typeof" with some other compiler? I would really apprciate this... Here is the link: http://groups.yahoo.com/group/boost/files/typeof.zip Any comments are greatly appreciated. Thanks, Arkadiy

Arkadiy Vertleyb wrote:
Although I believe that I don't do anything non-standard now, unfortunately I can't prove it myself. The only compiler I have besides MSVC is GCC, and it gives me "sorry not implemented" on something as simple as mpl::int_<sizeof(foo(...))>... Besides, GCC already has a typeof. Would anybody be so kind as to try the "typeof" with some other compiler? I would really apprciate this...
Here is the link:
I've successfully compiled your sample code with Intel V8.0 (Windows and Linux) and VC8 (alpha). Gcc 3.2.x doesn't work for me either. Regards Hartmut

Hartmut Kaiser wrote:
I've successfully compiled your sample code with Intel V8.0 (Windows and Linux) and VC8 (alpha). Gcc 3.2.x doesn't work for me either.
Shouldn't we use the compiler's native typeof-implementation where available? IIRC both GCC and Intel support __typeof__ Regards, Daniel -- Daniel Frey aixigo AG - financial solutions & technology Schloß-Rahe-Straße 15, 52072 Aachen, Germany fon: +49 (0)241 936737-42, fax: +49 (0)241 936737-99 eMail: daniel.frey@aixigo.de, web: http://www.aixigo.de

On May 3, 2004, at 4:55 AM, Daniel Frey wrote:
Shouldn't we use the compiler's native typeof-implementation where available? IIRC both GCC and Intel support __typeof__
Metrowerks as well (same spelling, same semantics). Note that this is not a reference-preserving operation like the proposed decltype. -Howard

Howard Hinnant <hinnant@twcny.rr.com> writes:
On May 3, 2004, at 4:55 AM, Daniel Frey wrote:
Shouldn't we use the compiler's native typeof-implementation where available? IIRC both GCC and Intel support __typeof__
Metrowerks as well (same spelling, same semantics). Note that this is not a reference-preserving operation like the proposed decltype.
I think it's easy enough to write a macro that will add back the reference if neccessary. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
main() { BOOST_TYPEOF_ALLOCATE(fun, _1 > 15 && _2 < 20); // <unspecified> fun(_1 > 15 && _2 < 20);
What's that line supposed to mean? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:u1xm11xeb.fsf@boost-consulting.com...
main() { BOOST_TYPEOF_ALLOCATE(fun, _1 > 15 && _2 < 20); // <unspecified> fun(_1 > 15 && _2 < 20);
What's that line supposed to mean?
Shortcut for: BOOST_TYPEOF(_1 > 15 && _2 < 20) fun(_1 > 15 && _2 < 20); Regards, Arkadiy

"Arkadiy Vertleyb" <vertleyb@hotmail.com> wrote
main() { BOOST_TYPEOF_ALLOCATE(fun, _1 > 15 && _2 < 20); // <unspecified> fun(_1 > 15 && _2 < 20);
What's that line supposed to mean?
Shortcut for:
BOOST_TYPEOF(_1 > 15 && _2 < 20) fun(_1 > 15 && _2 < 20);
On the other hand, since a lambda expression returns a default-constructed functor (does it? -- I am unable to verify it right now) the above doesn't make a lot of sence for Lambda... One can just write: BOOST_TYPEOF(_1 > 15 && _2 < 20) fun; Arkadiy

On Mon, May 03, 2004 at 09:42:16AM -0400, Arkadiy Vertleyb wrote:
Shortcut for:
BOOST_TYPEOF(_1 > 15 && _2 < 20) fun(_1 > 15 && _2 < 20);
On the other hand, since a lambda expression returns a default-constructed functor (does it? -- I am unable to verify it right now) the above doesn't make a lot of sence for Lambda... One can just write:
BOOST_TYPEOF(_1 > 15 && _2 < 20) fun;
I don't think so. I am pretty sure that _1 < 3.3 and _1 < 4.4 have the same _type_ (but they have different "double" _values_ encoded in the run-time structure when constructed). -- -Brian McNamara (lorgon@cc.gatech.edu)

"Brian McNamara" <lorgon@cc.gatech.edu> wrote
On Mon, May 03, 2004 at 09:42:16AM -0400, Arkadiy Vertleyb wrote:
On the other hand, since a lambda expression returns a
default-constructed
functor (does it? -- I am unable to verify it right now) the above doesn't make a lot of sence for Lambda... One can just write:
BOOST_TYPEOF(_1 > 15 && _2 < 20) fun;
I don't think so. I am pretty sure that
_1 < 3.3 and _1 < 4.4
have the same _type_ (but they have different "double" _values_ encoded in the run-time structure when constructed).
Yes, thanks for correcting me. This makes one still want to use the BOOST_TYPEOF_ALLOCATE macro in this case. Arkadiy

Arkadiy Vertleyb wrote:
"Brian McNamara" <lorgon@cc.gatech.edu> wrote
On Mon, May 03, 2004 at 09:42:16AM -0400, Arkadiy Vertleyb wrote:
On the other hand, since a lambda expression returns a
default-constructed
functor (does it? -- I am unable to verify it right now) the above
doesn't
make a lot of sence for Lambda... One can just write:
BOOST_TYPEOF(_1 > 15 && _2 < 20) fun;
I don't think so. I am pretty sure that
_1 < 3.3 and _1 < 4.4
have the same _type_ (but they have different "double" _values_ encoded in the run-time structure when constructed).
Yes, thanks for correcting me. This makes one still want to use the BOOST_TYPEOF_ALLOCATE macro in this case.
I'd rather want to see: BOOST_AUTO(fun, _1 > 15 && _2 < 20); That would be very nifty for Spirit parsers: BOOST_AUTO(my_rule, int_p >> *(',' >> int_p)); I would love to incorporate this stuff into Spirit !!! 1) It would simplify grammars a lot. 2) It would give a significant performance boost 3) It would reduce the generated code size by as much as 80%. See http://tinyurl.com/2wuqr and http://tinyurl.com/2wuqr for reference. Spirit would definitely benifit from Arkadiy's typeof. I'd like to offer this challenge to Arkadiy. If you can easily retrofit Spirit with a generic typeof/auto facility, I would love to make it an integral part of the library. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

"Joel de Guzman" <joel@boost-consulting.com> wrote
I'd rather want to see:
BOOST_AUTO(fun, _1 > 15 && _2 < 20);
That would be very nifty for Spirit parsers:
BOOST_AUTO(my_rule, int_p >> *(',' >> int_p));
This is just a naming issue (no wonder -- I got the idea from the Spirit documentation), and your suggestion does look nicer.
I would love to incorporate this stuff into Spirit !!! 1) It would simplify grammars a lot. 2) It would give a significant performance boost 3) It would reduce the generated code size by as much as 80%.
Just out of curiousity, why would performance be affected?
Spirit would definitely benifit from Arkadiy's typeof. I'd like to offer this challenge to Arkadiy. If you can easily retrofit Spirit with a generic typeof/auto facility, I would love to make it an integral part of the library.
I accept. I would first have to get better acqainted with Spirit, though. May I ask a few preliminary questions: 1) Does Spirit operate exclusively on the library-defined classes/templates or some user-defined types are also involved (ease of use issue)? 2) Do Spirit templates usualy accept type template parameters or integral values are often used as well (ease of registration issue)? 3) If we wanted to represent a spirit expression as a tree, where each node would be a type (leaf) or a template, what would be a reasonable amount of nodes, default template parameters excluded (limitations/compile speed issue)? 4) How many typeofs would be reasonable to expect per translation unit (compile speed issue)? Considering my current schedule, I am not expecting to be ready by tomorrow :) May take me a week or two to produce some result. Is this OK? Regards, Arkadiy

Arkadiy Vertleyb wrote:
"Joel de Guzman" <joel@boost-consulting.com> wrote
I would love to incorporate this stuff into Spirit !!! 1) It would simplify grammars a lot. 2) It would give a significant performance boost 3) It would reduce the generated code size by as much as 80%.
Just out of curiousity, why would performance be affected?
1) No more virtual function for rules. 2) The Spirit grammar implementation employs a workaround to make it accept any type of scanner by delaying the instantiation of rules (the culprits) at parse time when the scanner type is known. Such a scheme is possible if the typed-definition (a nested template class) is a static object in the "parse" function. This scheme involves some esoteric tricks to make it multi-thread safe. These tricks bloat the code and degrade the performance considerably.
Spirit would definitely benifit from Arkadiy's typeof. I'd like to offer this challenge to Arkadiy. If you can easily retrofit Spirit with a generic typeof/auto facility, I would love to make it an integral part of the library.
I accept.
Cool!
I would first have to get better acqainted with Spirit, though. May I ask a few preliminary questions:
1) Does Spirit operate exclusively on the library-defined classes/templates or some user-defined types are also involved (ease of use issue)?
Spirit operates *mostly* (but not exclusively) on library-defined classes. It is possible for clients to write their own parsers. On some rare occassions, people do write their own parsers.
2) Do Spirit templates usualy accept type template parameters or integral values are often used as well (ease of registration issue)?
A few accept integral constants (the subrule comes to mind).
3) If we wanted to represent a spirit expression as a tree, where each node would be a type (leaf) or a template, what would be a reasonable amount of nodes, default template parameters excluded (limitations/compile speed issue)?
That's hard to estimate. I've seen rules that break the compiler limits of newer compilers such as VC7.1. But, then again, the general recommendation is to always break down rules to smaller fragments.
4) How many typeofs would be reasonable to expect per translation unit (compile speed issue)?
This is also hard to estimate. Ditto, the general recommendation is to always break down rules to smaller fragments.
Considering my current schedule, I am not expecting to be ready by tomorrow :) May take me a week or two to produce some result. Is this OK?
Take as much time as you want. I'd be willing to help in any way I can. My ultimate goal is to eliminate all rules, hence making all parsers fully polymorphic. I'm sure that would involve some new facilities to make forward rules work. Consider the calculator for example. Here's how I envision the calculator will be once we have auto and typeof: struct calc : grammar<calc> { auto definition() const { forward_rule<1> expression; auto group = '(' >> expression >> ')'; auto factor = integer | group; auto term = factor >> *(('*' >> factor) | ('/' >> factor)); return expression = term >> *(('+' >> term) | ('-' >> term)); } }; forward_rule<1> borrows from the subrule technique. It is necessary because expression is used *before* it is defined. Think of it as a placeholder like phoenix::arg1 and LL::_1. The base grammar class uses CRTP, as usual. It will probably be written as: template <typename Derived> struct grammar : parser<Derived> { grammar() : start(derived().definition()) {} Derived const& derived() const { return *static_cast<Derived const*>(this); } typeof(derived().definition()) start; }; It will contain a single member named "start". I'll give you CVS access to Spirit (tell me your SF login). I'd like to start a new CVS branch for typeof/auto experimentations. This would involve not only Spirit but Phoenix as well. I have a feeling that this will take more than a week or two. Yet, I'm sure that the results would be gratifying, at the very least. Thanks you and Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

"Joel de Guzman" <joel@boost-consulting.com> wrote
I'll give you CVS access to Spirit (tell me your SF login).
I would tell you if I knew what it is and if I had one :)
I'd like to start a new CVS branch for typeof/auto experimentations. This would involve not only Spirit but Phoenix as well. I have a feeling that this will take more than a week or two. Yet, I'm sure that the results would be gratifying, at the very least.
Actually, I do think that this should be done fast, since it is intended to close the gap (maybe a year or two) until the real typeof becomes available. Thank you, Arkadiy

Arkadiy Vertleyb wrote:
"Joel de Guzman" <joel@boost-consulting.com> wrote
I'll give you CVS access to Spirit (tell me your SF login).
I would tell you if I knew what it is and if I had one :)
You can get one from source forge :)
I'd like to start a new CVS branch for typeof/auto experimentations. This would involve not only Spirit but Phoenix as well. I have a feeling that this will take more than a week or two. Yet, I'm sure that the results would be gratifying, at the very least.
Actually, I do think that this should be done fast, since it is intended to close the gap (maybe a year or two) until the real typeof becomes available.
Agreed. Let's do it! Thanks! -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
The provided example shows what registration needs to be done to calculate the type of a lambda functor:
#include BOOST_TYPEOF_BEGIN_REGISTRATION()
Where would that line go, in a header file?
BOOST_TYPEOF_REGISTER_TEMPLATE(tuples::tuple, 2); BOOST_TYPEOF_REGISTER_TEMPLATE(lambda_functor, 1); BOOST_TYPEOF_REGISTER_TEMPLATE(lambda_functor_base, 2); BOOST_TYPEOF_REGISTER_TEMPLATE(relational_action, 1); BOOST_TYPEOF_REGISTER_TEMPLATE(logical_action, 1); BOOST_TYPEOF_REGISTER_TYPE(greater_action); BOOST_TYPEOF_REGISTER_TYPE(less_action); BOOST_TYPEOF_REGISTER_TYPE(and_action); BOOST_TYPEOF_REGISTER_TYPE(placeholder<1>); BOOST_TYPEOF_REGISTER_TYPE(placeholder<2>);
#include BOOST_TYPEOF_END_REGISTRATION()
Where would that line go, in a source file? If so, that sounds error-prone to me. FYI, to take advantage of GCC's buggy __typeof__, you may have to accept some limitations on its type argument. I think it has problems with references or something. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote
The provided example shows what registration needs to be done to calculate the type of a lambda functor:
#include BOOST_TYPEOF_BEGIN_REGISTRATION()
Where would that line go, in a header file?
BOOST_TYPEOF_REGISTER_TEMPLATE(tuples::tuple, 2); BOOST_TYPEOF_REGISTER_TEMPLATE(lambda_functor, 1); .... BOOST_TYPEOF_REGISTER_TYPE(placeholder<2>);
#include BOOST_TYPEOF_END_REGISTRATION()
Where would that line go, in a source file? If so, that sounds error-prone to me.
Everything would go into one file -- source or header. The BEGIN_REGISTRATION establishes a unique REGISTRATION_GROUP (using the counter suggested by Paul Mensonides). The REGISTRATION_GROUP is then used by the registration macros in conjunction with __LINE__ to generate unique integer identifiers. The END_REGISTRATION is technicaly not necessary. It just undefs the REGISTRATION_GROUP to force other files use the BEGIN_REGISTRATION (and so avoid potential clashes). Arkadiy

"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
"David Abrahams" <dave@boost-consulting.com> wrote
The provided example shows what registration needs to be done to calculate the type of a lambda functor:
#include BOOST_TYPEOF_BEGIN_REGISTRATION()
Where would that line go, in a header file?
BOOST_TYPEOF_REGISTER_TEMPLATE(tuples::tuple, 2); BOOST_TYPEOF_REGISTER_TEMPLATE(lambda_functor, 1); .... BOOST_TYPEOF_REGISTER_TYPE(placeholder<2>);
#include BOOST_TYPEOF_END_REGISTRATION()
Where would that line go, in a source file? If so, that sounds error-prone to me.
Everything would go into one file -- source or header.
The BEGIN_REGISTRATION establishes a unique REGISTRATION_GROUP (using the counter suggested by Paul Mensonides). The REGISTRATION_GROUP is then used by the registration macros in conjunction with __LINE__ to generate unique integer identifiers. The END_REGISTRATION is technicaly not necessary. It just undefs the REGISTRATION_GROUP to force other files use the BEGIN_REGISTRATION (and so avoid potential clashes).
So does it work if multiple headers using BEGIN_REGISTRATION/END_REGISTRATION are included? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote
So does it work if multiple headers using BEGIN_REGISTRATION/END_REGISTRATION are included?
Yes, although thechnically speaking, I did not verify this (all I tried was using multiple begin/end_registration groups in the same file, and seeing that the registration group is set to the next integer). I am almost sure this will work, but will be able to verify it only in the evening... Arkadiy

"David Abrahams" <dave@boost-consulting.com> wrote
So does it work if multiple headers using BEGIN_REGISTRATION/END_REGISTRATION are included?
I verified it -- it does work as I intended. The first group, in whichever file it is, uses 0x10000 + __LINE__, the second one uses 0x20000 + __LINE__, etc. I think, it is convenient to have one group per file. Arkadiy

David Abrahams wrote:
FYI, to take advantage of GCC's buggy __typeof__, you may have to accept some limitations on its type argument. I think it has problems with references or something.
Actually, GCC's typeof implements a reference-dropping semantic. In other words, typeof(int&) is int for GCC. The decltype paper has some insights about this. I thought the rest of the problems in using it was just ICEs here and there, which could be cured if there was enough interest. -- Giovanni Bajo

"Giovanni Bajo" <giovannibajo@libero.it> writes:
David Abrahams wrote:
FYI, to take advantage of GCC's buggy __typeof__, you may have to accept some limitations on its type argument. I think it has problems with references or something.
Actually, GCC's typeof implements a reference-dropping semantic.
As do all of them. That isn't the problem I'm referring to. It simply ICEs on some types that haven't been nicely wrapped. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

On 5/2/04 9:38 PM, "Arkadiy Vertleyb" <vertleyb@hotmail.com> wrote:
A couple of weeks ago I initiated the discussion about simple typeof emulation, and provided an example on how it can be done with the help of MPL vector of integral constants. I then used MS-specific __COUNTER__ macro to spare the user from specifying the unique integer for each registered class/template. I hoped to find a way to do something similar in standard C++, and asked preprocessor experts for help.
Indeed, Paul Mensonides suggested such a way. At first I thought that it will not be good enough replacement for the __COUNTER__, but then I realized that I was wrong. So I re-worked my implementation of typeof, and now offerring it to your attention.
Besides removing __COUNTER__, I used the Boost.Preprocessor library to cleanup the implementation. Also, after the discussion with Jody Hagins, I put the encoding/decoding classes inside anonimous namespace to avoid collisions between translation units.
The provided example shows what registration needs to be done to calculate the type of a lambda functor:
#include BOOST_TYPEOF_BEGIN_REGISTRATION()
BOOST_TYPEOF_REGISTER_TEMPLATE(tuples::tuple, 2); BOOST_TYPEOF_REGISTER_TEMPLATE(lambda_functor, 1); BOOST_TYPEOF_REGISTER_TEMPLATE(lambda_functor_base, 2); BOOST_TYPEOF_REGISTER_TEMPLATE(relational_action, 1); BOOST_TYPEOF_REGISTER_TEMPLATE(logical_action, 1); BOOST_TYPEOF_REGISTER_TYPE(greater_action); BOOST_TYPEOF_REGISTER_TYPE(less_action); BOOST_TYPEOF_REGISTER_TYPE(and_action); BOOST_TYPEOF_REGISTER_TYPE(placeholder<1>); BOOST_TYPEOF_REGISTER_TYPE(placeholder<2>);
#include BOOST_TYPEOF_END_REGISTRATION()
main() { BOOST_TYPEOF_ALLOCATE(fun, _1 > 15 && _2 < 20); // <unspecified> fun(_1
15 && _2 < 20); int n = 19; assert(fun(n, n)); } [TRUNCATE]
Wouldn't the program need exactly one master type-of list? Since C and C++ don't have cross-module consolidation, just text dumps of common files that work by "coincidence", couldn't you break ODR by: File1.cpp: #include BOOST_TYPEOF_BEGIN_REGISTRATION() BOOST_TYPEOF_REGISTER_TYPE(greater_action); //1 BOOST_TYPEOF_REGISTER_TYPE(less_action); //2 BOOST_TYPEOF_REGISTER_TYPE(and_action); //3 #include BOOST_TYPEOF_END_REGISTRATION() int my_func1() { /*...*/} File2.cpp: #include BOOST_TYPEOF_BEGIN_REGISTRATION() BOOST_TYPEOF_REGISTER_TYPE(placeholder<1>); //1 BOOST_TYPEOF_REGISTER_TYPE(less_action); //2 (only one the same) BOOST_TYPEOF_REGISTER_TYPE(greater_action); //3 #include BOOST_TYPEOF_END_REGISTRATION() int my_func2() { /*...*/} This wouldn't break if the preprocessor counter doesn't reset between files, but then the same type-of would get lots of Ids. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

Wouldn't the program need exactly one master type-of list? Since C and C++ don't have cross-module consolidation, just text dumps of common files
"Daryle Walker" <darylew@hotmail.com> wrote that
work by "coincidence", couldn't you break ODR by:
File1.cpp: #include BOOST_TYPEOF_BEGIN_REGISTRATION() BOOST_TYPEOF_REGISTER_TYPE(greater_action); //1 BOOST_TYPEOF_REGISTER_TYPE(less_action); //2 BOOST_TYPEOF_REGISTER_TYPE(and_action); //3 #include BOOST_TYPEOF_END_REGISTRATION() int my_func1() { /*...*/}
File2.cpp: #include BOOST_TYPEOF_BEGIN_REGISTRATION() BOOST_TYPEOF_REGISTER_TYPE(placeholder<1>); //1 BOOST_TYPEOF_REGISTER_TYPE(less_action); //2 (only one the same) BOOST_TYPEOF_REGISTER_TYPE(greater_action); //3 #include BOOST_TYPEOF_END_REGISTRATION() int my_func2() { /*...*/}
I could, but I use anonimous namespaces to avoid this. This ensures every compilation unit has it's own set of encoding/decoding classes, and takes care of ODR.
This wouldn't break if the preprocessor counter doesn't reset between files, but then the same type-of would get lots of Ids.
The same type may be encoded/decoded using different set of integral constants in different compilation units. I don't see a problem here since encoding/decoding always happens inside the same compile-time expression, and therefore inside the same translation unit. Regards, Arkadiy

"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
"Daryle Walker" <darylew@hotmail.com> wrote
Wouldn't the program need exactly one master type-of list? Since C and C++ don't have cross-module consolidation, just text dumps of common files that work by "coincidence", couldn't you break ODR by:
File1.cpp: #include BOOST_TYPEOF_BEGIN_REGISTRATION() BOOST_TYPEOF_REGISTER_TYPE(greater_action); //1 BOOST_TYPEOF_REGISTER_TYPE(less_action); //2 BOOST_TYPEOF_REGISTER_TYPE(and_action); //3 #include BOOST_TYPEOF_END_REGISTRATION() int my_func1() { /*...*/}
File2.cpp: #include BOOST_TYPEOF_BEGIN_REGISTRATION() BOOST_TYPEOF_REGISTER_TYPE(placeholder<1>); //1 BOOST_TYPEOF_REGISTER_TYPE(less_action); //2 (only one the same) BOOST_TYPEOF_REGISTER_TYPE(greater_action); //3 #include BOOST_TYPEOF_END_REGISTRATION() int my_func2() { /*...*/}
I could, but I use anonimous namespaces to avoid this. This ensures every compilation unit has it's own set of encoding/decoding classes, and takes care of ODR.
I'm afraid it doesn't, technically, if typeof is used within a template that's seen in multiple translation units. That said, I'm willing to accept it if it really works. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote
"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
I could, but I use anonimous namespaces to avoid this. This ensures every compilation unit has it's own set of encoding/decoding classes, and takes care of ODR.
I'm afraid it doesn't, technically, if typeof is used within a template that's seen in multiple translation units. That said, I'm willing to accept it if it really works.
In my understanding, the problem goes like this: in one translation unit (after preprocessing is done): template<class T> struct encode<std::vector<T> > { typedef ... // use some integral constant to encode the vector }; in another translation unit: template<class T> struct encode<std::vector<T> > { typedef ... // use another integral constant to encode the vector }; Technically speaking, this is one class with different bodies, and can represent a problem with ODR (at linking? But isn't the linker concerned only with functions? The only functions here are those generated automatically, and never used, but they are similar in all respects. The actual difference is in typedefs). Having said this, I still think the anonimous namespace will take care of this by creating totaly distinct classes in different compilation units. Why not? Can you ellaborate? Regards, Arkadiy

"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
template<class T> struct encode<std::vector<T> > { typedef ... // use some integral constant to encode the vector };
in another translation unit:
template<class T> struct encode<std::vector<T> > { typedef ... // use another integral constant to encode the vector };
Technically speaking, this is one class with different bodies, and can represent a problem with ODR (at linking? But isn't the linker concerned only with functions? The only functions here are those generated automatically, and never used, but they are similar in all respects. The actual difference is in typedefs).
Having said this, I still think the anonimous namespace will take care of this by creating totaly distinct classes in different compilation units. Why not? Can you ellaborate?
Any time a template is instantitated, all names in its definition must map to the same entities in each translation unit, or you violate the ODR. By definition, names in the unnamed namespace refer to distinct entities in each translation unit. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote
Any time a template is instantitated, all names in its definition must map to the same entities in each translation unit, or you violate the ODR. By definition, names in the unnamed namespace refer to distinct entities in each translation unit.
Does this mean that it's not allowed to use anonimous namespace-level names inside templates? I assume there also should be problems with non-template classes, too, if anonimous namespace-defined names are used inside the body of such a class... OTOH, you said "all names in its definition". Does this also mean "all names used to produce this definition"? Because if we use "typeof" inside such template, it would still resolve to the same type in different translation units (after all the metaprogramming used to produce this type is done). IOW, such templates in different translation units will be absolutely the same, although in each translation unit the compiler will have to instantiate different intermediate classes in order to produce them. Do you still think this should be a problem? Arkadiy

"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
"David Abrahams" <dave@boost-consulting.com> wrote
Any time a template is instantitated, all names in its definition must map to the same entities in each translation unit, or you violate the ODR. By definition, names in the unnamed namespace refer to distinct entities in each translation unit.
Does this mean that it's not allowed to use anonimous namespace-level names inside templates?
Unless those, too, are in the unnamed namespace, I think that's technically correct. You can probably get away with it, though.
I assume there also should be problems with non-template classes, too, if anonimous namespace-defined names are used inside the body of such a class...
Yes, definitely.
OTOH, you said "all names in its definition". Does this also mean "all names used to produce this definition"?
I think so.
Because if we use "typeof" inside such template, it would still resolve to the same type in different translation units (after all the metaprogramming used to produce this type is done).
I understand that. I think you can get away with it, but that it's not legal. This is another reason I think Boost.Bind's placeholders have to leave the unnamed namespace.
IOW, such templates in different translation units will be absolutely the same, although in each translation unit the compiler will have to instantiate different intermediate classes in order to produce them.
Right.
Do you still think this should be a problem?
"_Should_ be?" no, I never did. I just think it's technically a violation of the rules. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
"David Abrahams" <dave@boost-consulting.com> wrote
Any time a template is instantitated, all names in its definition must map to the same entities in each translation unit, or you violate the ODR. By definition, names in the unnamed namespace refer to distinct entities in each translation unit.
But typedef only introduces aliases for an existing type. Does that qualify for "map[s] onto the same entity" ?
Does this mean that it's not allowed to use anonimous namespace-level names inside templates?
Unless those, too, are in the unnamed namespace, I think that's technically correct. You can probably get away with it, though.
I assume there also should be problems with non-template classes, too, if anonimous namespace-defined names are used inside the body of such a class...
Yes, definitely.
OTOH, you said "all names in its definition". Does this also mean "all names used to produce this definition"?
I think so.
Because if we use "typeof" inside such template, it would still resolve to the same type in different translation units (after all the metaprogramming used to produce this type is done).
I understand that. I think you can get away with it, but that it's not legal. This is another reason I think Boost.Bind's placeholders have to leave the unnamed namespace.
IOW, such templates in different translation units will be absolutely the same, although in each translation unit the compiler will have to instantiate different intermediate classes in order to produce them.
Those intermediate classes are in an unnamed namespace and not an ODR violation in themselves?
Right.
Do you still think this should be a problem?
"_Should_ be?" no, I never did. I just think it's technically a violation of the rules.
If I understand correctly, you are saying that // header.h namespace { typedef int foo; } struct bar { foo f; }; // end header.h causes an ODR violation if type bar (or even foo) is used in more than one translation unit? Can you quote chapter and verse? (or perhaps that should be asked on c.s.c++). Cheers, Ian McCulloch

Ian McCulloch <ianmcc@lorentz.leidenuniv.nl> writes:
David Abrahams wrote:
"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
"David Abrahams" <dave@boost-consulting.com> wrote
Any time a template is instantitated, all names in its definition must map to the same entities in each translation unit, or you violate the ODR. By definition, names in the unnamed namespace refer to distinct entities in each translation unit.
But typedef only introduces aliases for an existing type. Does that qualify for "map[s] onto the same entity" ?
Your question isn't specific enough for me to answer.
IOW, such templates in different translation units will be absolutely the same, although in each translation unit the compiler will have to instantiate different intermediate classes in order to produce them.
Those intermediate classes are in an unnamed namespace and not an ODR violation in themselves?
Not in and of themselves. Their use in the same template in multiple translation units is a violation, IIUC.
If I understand correctly, you are saying that
// header.h namespace { typedef int foo; }
struct bar { foo f; };
// end header.h
causes an ODR violation if type bar (or even foo) is used in more than one translation unit?
That appears to be clearly spelled out below. AFAICT unnamed namespaces in header files are evil.
Can you quote chapter and verse? (or perhaps that should be asked on c.s.c++).
Probably ;-) It's all there right under "One definition rule" in the standard. 3 Basic concepts 3.2 One definition rule -5- There can be more than one definition of a class type (clause class), enumeration type (dcl.enum), inline function with external linkage (dcl.fct.spec), class template (clause temp), non-static function template (temp.fct), static data member of a class template (temp.static), member function [core 249: template of a class template ] (temp.mem.func), or template specialization for which some template parameters are not specified (temp.spec, temp.class.spec) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then - each definition of D shall consist of the same sequence of tokens; and - in each definition of D, corresponding names, looked up according to basic.lookup, shall refer to an entity defined within the definition of D, or shall refer to the same entity, after ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overload resolution (over.match) and after matching of partial template specialization (temp.over), except that a name can refer to a const object with internal or no linkage if the object has the same integral or enumeration type in all definitions of D, and the object is initialized with a constant expression (expr.const), and the value (but not the address) of the object is used, and the object has the same value in all definitions of D; and - in each definition of D, the overloaded operators referred to, the implicit calls to conversion functions, constructors, operator new functions and operator delete functions, shall refer to the same function, or to a function defined within the definition of D; and - in each definition of D, a default argument used by an (implicit or explicit) function call is treated as if its token sequence were present in the definition of D; that is, the default argument is subject to the three requirements described above (and, if the default argument has sub-expressions with default arguments, this requirement applies recursively).* [Footnote: dcl.fct.default describes how default argument names are looked up. --- end foonote] - if D is a class with an implicitly-declared constructor (class.ctor), it is as if the constructor was implicitly defined in every translation unit where it is used, and the implicit definition in every translation unit shall call the same constructor for a base class or a class member of D. [Example: // translation unit 1: struct X { X(int); X(int, int); }; X::X(int = 0) { } class D: public X { }; D d2; // X(int) called by D() // translation unit 2: struct X { X(int); X(int, int); }; X::X(int = 0, int = 0) { } class D: public X { }; // X(int, int) called by D(); // D()'s implicit definition // violates the ODR --- end example] - If D is a template, and is defined in more than one translation unit, then the last four requirements from the list above shall apply to names from the template's enclosing scope used in the template definition (temp.nondep), and also to dependent names at the point of instantiation (temp.dep). If the definitions of D satisfy all these requirements, then the program shall behave as if there were a single definition of D. If the definitions of D do not satisfy these requirements, then the behavior is undefined. 14.6.4.1 Point of instantiation 14 Templates 7 A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit. A special- ization for a class template has at most one point of instantiation within a translation unit. A specialization for any template may have points of instantiation in multiple translation units. If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Ian McCulloch <ianmcc@lorentz.leidenuniv.nl> writes:
David Abrahams wrote:
"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
"David Abrahams" <dave@boost-consulting.com> wrote
Any time a template is instantitated, all names in its definition must map to the same entities in each translation unit, or you violate the ODR. By definition, names in the unnamed namespace refer to distinct entities in each translation unit.
But typedef only introduces aliases for an existing type. Does that qualify for "map[s] onto the same entity" ?
Your question isn't specific enough for me to answer.
Sorry, I meant: do two typedefs that are aliases for the same type, but differ in name, count as different entities with respect to the ODR? This is defined in 3 [basic]: -3- An "entity" is a value, object, subobject, base class subobject, array element, variable, function, instance of a function, enumerator, type, class member, template or namespace. This doesn't include typedef names, but doesn't specifically exclude them either. Moving onto 7.1.3 The typedef specifier: -1- ... (sorry, I cannot cut&paste from my (old) copy of the stamdard) A name declared with the"typedef" specifier becomes a typedef-name. Within the scope of its definition, a typedef-name is syntactially equivalent to a keyword and names the type associated with the identifier in the way described in clause 8. A typedef-name is thus a synonym for another type. A typedef-name does not introduce a new type in the way a class declaratin (9.1) or enum declaration does. [Example: after typedef int MILES, *KLICKSP; the constructions MILES distance; extern KLICKSP metricp; are all correct declarations; the type of distance is int; that of metricp is "pointer to int."] -- end quote Following on from the example, I take this to mean one translation unit declaring "extern int* metricp" and another declaring "extern KLICKSP metricp" is not an ODR violation. Similarly, declaring KLICKSP in an unnamed namespace in a header file should be OK too.
IOW, such templates in different translation units will be absolutely the same, although in each translation unit the compiler will have to instantiate different intermediate classes in order to produce them.
Those intermediate classes are in an unnamed namespace and not an ODR violation in themselves?
Not in and of themselves. Their use in the same template in multiple translation units is a violation, IIUC.
If I understand correctly, you are saying that
// header.h namespace { typedef int foo; }
struct bar { foo f; };
// end header.h
causes an ODR violation if type bar (or even foo) is used in more than one translation unit?
That appears to be clearly spelled out below. AFAICT unnamed namespaces in header files are evil.
Can you quote chapter and verse? (or perhaps that should be asked on c.s.c++).
Probably ;-) It's all there right under "One definition rule" in the standard.
3 Basic concepts 3.2 One definition rule
-5- There can be more than one definition of a class type (clause class), enumeration type (dcl.enum), inline function with external linkage (dcl.fct.spec), class template (clause temp), non-static function template (temp.fct), static data member of a class template (temp.static), member function [core 249: template of a class template ] (temp.mem.func), or template specialization for which some template parameters are not specified (temp.spec, temp.class.spec) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then
- each definition of D shall consist of the same sequence of tokens; and
- in each definition of D, corresponding names, looked up according to basic.lookup, shall refer to an entity defined within the definition of D, or shall refer to the same entity, after ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To my reading of the standard, different typedef-names that are synonyms for the same type are the same entity with respect to the ODR, so I don't think any of this applies here. Cheers, Ian McCulloch

Ian McCulloch <ianmcc@lorentz.leidenuniv.nl> writes:
Probably ;-) It's all there right under "One definition rule" in the standard.
3 Basic concepts 3.2 One definition rule
-5- There can be more than one definition of a class type (clause class), enumeration type (dcl.enum), inline function with external linkage (dcl.fct.spec), class template (clause temp), non-static function template (temp.fct), static data member of a class template (temp.static), member function [core 249: template of a class template ] (temp.mem.func), or template specialization for which some template parameters are not specified (temp.spec, temp.class.spec) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then
- each definition of D shall consist of the same sequence of tokens; and
- in each definition of D, corresponding names, looked up according to basic.lookup, shall refer to an entity defined within the definition of D, or shall refer to the same entity, after ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To my reading of the standard, different typedef-names that are synonyms for the same type are the same entity with respect to the ODR, so I don't think any of this applies here.
If you limit the question to typedefs, you're right, but I'm not limiting the question to typedefs. The problem I'm discussing is this one: namespace { template <class T> typeof (T() + T()) add(T*, T*) { typeof(T() + T()) x; ... return x; } } If the declaration of x (or add, for that matter, I'm pretty sure) uses any names that refer to different entities in different TUs, it's an ODR violation. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Ian McCulloch <ianmcc@lorentz.leidenuniv.nl> writes:
Probably ;-) It's all there right under "One definition rule" in the standard.
3 Basic concepts 3.2 One definition rule
-5- There can be more than one definition of a class type (clause class), enumeration type (dcl.enum), inline function with external linkage (dcl.fct.spec), class template (clause temp), non-static function template (temp.fct), static data member of a class template (temp.static), member function [core 249: template of a class template ] (temp.mem.func), or template specialization for which some template parameters are not specified (temp.spec, temp.class.spec) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then
- each definition of D shall consist of the same sequence of tokens; and
- in each definition of D, corresponding names, looked up according to basic.lookup, shall refer to an entity defined within the definition of D, or shall refer to the same entity, after ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To my reading of the standard, different typedef-names that are synonyms for the same type are the same entity with respect to the ODR, so I don't think any of this applies here.
If you limit the question to typedefs, you're right, but I'm not limiting the question to typedefs. The problem I'm discussing is this one:
namespace { template <class T> typeof (T() + T()) add(T*, T*) { typeof(T() + T()) x; ... return x; } }
If the declaration of x (or add, for that matter, I'm pretty sure) uses any names that refer to different entities in different TUs, it's an ODR violation.
Interesting. The purpose of typeof (or BOOST_TYPEOF) is to name a type, and that type will be the same in all TU's. But the declaration itself might involve a different type in different translation units, although the full declaration will end up naming the same entity. An interesting interaction with typedefs and templates. Or would you also claim that this would happen even in the absense of templates? If so, what makes the template case different? // header.h namespace { struct typeof_foo { typedef int type; }; } // namespace typeof_foo::type bar();// ODR violation here??? // Why is this different to previous examples? // does anything change when templates are involved? // end header.h Cheers, Ian

IOW, such templates in different translation units will be absolutely
"David Abrahams" <dave@boost-consulting.com> wrote the
same, although in each translation unit the compiler will have to instantiate different intermediate classes in order to produce them.
Right.
Do you still think this should be a problem?
"_Should_ be?" no, I never did. I just think it's technically a violation of the rules.
I have to admit that the solution with automatic ID generation would probably be non-conformant. I guess the next question would be, if we require to manually provide a unique integer for each group of types/templates, how practical such system would be, especially considering the fact that an expression may contain types defined in multiple libraries, as well as user-defined types... Are there any known examples of libraries that requre users to provide unique compile-time integers? Regards, Arkadiy

"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
"David Abrahams" <dave@boost-consulting.com> wrote
IOW, such templates in different translation units will be absolutely the same, although in each translation unit the compiler will have to instantiate different intermediate classes in order to produce them.
Right.
Do you still think this should be a problem?
"_Should_ be?" no, I never did. I just think it's technically a violation of the rules.
I have to admit that the solution with automatic ID generation would probably be non-conformant.
I guess the next question would be, if we require to manually provide a unique integer for each group of types/templates, how practical such system would be, especially considering the fact that an expression may contain types defined in multiple libraries, as well as user-defined types...
Are there any known examples of libraries that requre users to provide unique compile-time integers?
Can't we do something with a unique name per header file using typeof? #define BOOST_TYPEOF_HEADER boost_lambda_whatever We could use that to define a uniquely-named type and/or synthesize identifiers using __LINE__. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote
Can't we do something with a unique name per header file using typeof?
#define BOOST_TYPEOF_HEADER boost_lambda_whatever
We could use that to define a uniquely-named type and/or synthesize identifiers using __LINE__.
I am afraid I don't understand this... How would we use this unique name? We can't convert it to a unique number, can we? Each type/template is registered in some particular (header/source) file at some particular line. The line gives us a unique id inside the file. What's missing is a unique integer that would identify the file. Regards, Arkadiy

"Arkadiy Vertleyb" <vertleyb@hotmail.com> writes:
"David Abrahams" <dave@boost-consulting.com> wrote
Can't we do something with a unique name per header file using typeof?
#define BOOST_TYPEOF_HEADER boost_lambda_whatever
We could use that to define a uniquely-named type and/or synthesize identifiers using __LINE__.
I am afraid I don't understand this... How would we use this unique name? We can't convert it to a unique number, can we?
We can build a unique type and use that as one parameter to any templates we need to instantiate in this file.
Each type/template is registered in some particular (header/source) file at some particular line. The line gives us a unique id inside the file. What's missing is a unique integer that would identify the file.
Not everything has to be an integer ;-) -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote
We can build a unique type and use that as one parameter to any templates we need to instantiate in this file.
Each type/template is registered in some particular (header/source) file at some particular line. The line gives us a unique id inside the file. What's missing is a unique integer that would identify the file.
Not everything has to be an integer ;-)
But types/templates have to be encoded as integers in order to "return" them from the function using sizeof... Regards, Arkadiy
participants (10)
-
Arkadiy Vertleyb
-
Brian McNamara
-
Daniel Frey
-
Daryle Walker
-
David Abrahams
-
Giovanni Bajo
-
hartmutkaiser@t-online.de
-
Howard Hinnant
-
Ian McCulloch
-
Joel de Guzman