generating unique ordinal value at compile time

For various reasons, it would be really nice to be able to generate a series of unique ordinal values (say integers, but really anything that is easily less_than_comparable) at compile time. A concrete example : struct system1 : ordinal<0> { }; struct system2 : ordinal<1> { }; etc... Now, if these tags can be defined in user code in various places, it can be difficult to ensure that there is no duplication of the ordinal values. What I want is something like this: struct system1 : ordinal<get_next_system_ordinal()> { }; struct system2 : ordinal<get_next_system_ordinal()> { }; where I don't really care what the specific order is, just that the ordinal values are unique... Anyone have a bright idea on how to accomplish this? Preprocessor? Matthias

values. What I want is something like this:
struct system1 : ordinal<get_next_system_ordinal()> { }; struct system2 : ordinal<get_next_system_ordinal()> { };
where I don't really care what the specific order is, just that the ordinal values are unique... Anyone have a bright idea on how to accomplish this? Preprocessor?
Hmm, I doubt you can do this with as much generality as you would like, especially since it would be hard to ensure uniqueness across multiple translation units. What about a solution like this: ///////////// struct ordinal_tag {}; template<class A> struct ordinal { static ordinal_tag const tag; template<class B> bool operator<(ordinal<B> const& other) { return &tag < &other.tag; } }; template<class A> ordinal_tag const ordinal<A>::tag = ordinal_tag(); struct system1 : ordinal<system1> { }; struct system2 : ordinal<system2> { }; struct system3 : ordinal<system3> { }; //////////////// This relies on being able to order pointers even when they don't point into the same array, which is technically undefined behavior. Now that I think about it, don't people use std::map<void*,T> all the time? How do they make sure that is portable? -Lewis

On 2/28/07, Lewis Hyatt <lhyatt@princeton.edu> wrote:
Now that I think about it, don't people use std::map<void*,T> all the time? How do they make sure that is portable?
std::less for pointers gives you a total order. 20.3.3.8. (Draft standard link: http://www.open-std.org/jtc1/sc22/open/n2356/lib-utilities.html )

On 3/1/07, Lewis Hyatt <lhyatt@princeton.edu> wrote:
values. What I want is something like this:
struct system1 : ordinal<get_next_system_ordinal()> { }; struct system2 : ordinal<get_next_system_ordinal()> { };
where I don't really care what the specific order is, just that the ordinal values are unique... Anyone have a bright idea on how to accomplish this? Preprocessor?
Hmm, I doubt you can do this with as much generality as you would like, especially since it would be hard to ensure uniqueness across multiple translation units.
What about a solution like this:
/////////////
struct ordinal_tag {};
template<class A> struct ordinal { static ordinal_tag const tag; template<class B> bool operator<(ordinal<B> const& other) { return &tag < &other.tag; } };
template<class A> ordinal_tag const ordinal<A>::tag = ordinal_tag();
struct system1 : ordinal<system1> { };
struct system2 : ordinal<system2> { };
struct system3 : ordinal<system3> { };
You need to actually define const static members of out of class. You usually can get away if the member is an integral type, is static const if you only read its _value_ (this is actually more or less guaranteed by the standard, see this thread in comp.lang.c++: http://groups.google.com/group/comp.lang.c++/browse_thread/thread/d81e99be8dec94dc/d25c4150d19ea60c?lnk=gst&q=definition+outside+of+class+scope+static&rnum=6#d25c4150d19ea60c ), unfortunately here you have a static member of an user defined type and also its address is taken, thus no luck. But wait, there is hope: you can do this: //////// template<class A> struct ordinal { void * tag() { static int const tag_; return &tag_; } template<class B> bool operator<(ordinal<B> const& other) { return std::less<void*>(tag(), other.tag()); } }; ////// This solution should also be MT safe.
////////////////
This relies on being able to order pointers even when they don't point into the same array, which is technically undefined behavior.
Now that I think about it, don't people use std::map<void*,T> all the time? How do they make sure that is portable?
Comparing pointers outside of arrays is not portable, on the other hand std::less<T*> is required to do the RightThing. Anyways, while pointers to static function variables are guaranteed unique, they are not constant expressions, so this might not be what the OP was looking for. Anyways^2 this is probably a bit off-topic here... gpd

Giovanni Piero Deretta <gpderetta <at> gmail.com> writes:
template<class A> ordinal_tag const ordinal<A>::tag = ordinal_tag();
You need to actually define const static members of out of class.
That's what the above line does. It would work fine.
Comparing pointers outside of arrays is not portable, on the other hand std::less<T*> is required to do the RightThing.
That is good to know, thanks!
Anyways, while pointers to static function variables are guaranteed unique, they are not constant expressions, so this might not be what the OP was looking for.
Pointers to class static members can be passed as template non-type arguments, though, which might be sufficient. -Lewis

AMDG Lewis Hyatt <lhyatt <at> princeton.edu> writes:
Anyways, while pointers to static function variables are guaranteed unique, they are not constant expressions, so this might not be what the OP was looking for.
Pointers to class static members can be passed as template non-type arguments, though, which might be sufficient.
-Lewis
Alas. Comparison of pointers does not yield a constant expression. In Christ, Steven Watanabe

Alas. Comparison of pointers does not yield a constant expression.
Right, of course not, but I think what I proposed satisfies everything the OP wanted. He didn't require that the comparison be done at compile time, only that the ordinal value be less-than-comparable and that the value should be generated and available at compile time. -Lewis

But wait, there is hope: you can do this:
////////
template<class A> struct ordinal { void * tag() { static int const tag_; return &tag_; } template<class B> bool operator<(ordinal<B> const& other) { return std::less<void*>(tag(), other.tag()); } };
//////
This solution should also be MT safe.
Unfortunately, this doesn't function at compile-time. That is, what I really want to be able to do is: struct system1 : ordinal<...> { }; struct system2 : ordinal<...> { }; struct system3 : ordinal<...> { }; typedef mpl::list<system2,system3,system1> list_type; typedef mpl::sort<list_type>::type sorted_list_type; where mpl::less<> can be defined for ordinal<...> It seems like a hard (maybe impossible) problem... Matthias ---------------------------------------------------------------- Matthias Schabel 2859 Glen Oaks Drive Salt Lake City, UT 84109 801-706-5760 (cell) 801-484-0811 (home) matthias at stanfordalumni dot org ---------------------------------------------------------------- 

2007/3/1, Matthias Schabel <boost@schabel-family.org>:
But wait, there is hope: you can do this:
////////
template<class A> struct ordinal { void * tag() { static int const tag_; return &tag_; } template<class B> bool operator<(ordinal<B> const& other) { return std::less<void*>(tag(), other.tag()); } };
//////
This solution should also be MT safe.
Unfortunately, this doesn't function at compile-time. That is, what I really want to be able to do is:
struct system1 : ordinal<...> { }; struct system2 : ordinal<...> { }; struct system3 : ordinal<...> { };
typedef mpl::list<system2,system3,system1> list_type; typedef mpl::sort<list_type>::type sorted_list_type;
where mpl::less<> can be defined for ordinal<...>
It seems like a hard (maybe impossible) problem...
You could always use the solution we use in boost.typeof. When we emulate typeof support for VC compilers, we use the following trick to associate a unique integer with each type: template<int N> struct encode_counter : encode_counter<N - 1> {}; template<> struct encode_counter<0> {}; //Need to default to a larger value than 4, as due to MSVC's ETI errors. (sizeof(int)==4) char (*encode_index(...))[5]; # define BOOST_TYPEOF_INDEX(T) (sizeof(*boost::type_of::encode_index((boost::type_of::encode_counter<1005>*)0))) # define BOOST_TYPEOF_NEXT_INDEX(next) friend char (*encode_index(encode_counter<next>*))[next]; //Tie it all together template<typename T=int> struct ordinal { //Get the next available compile time constants index BOOST_STATIC_CONSTANT(unsigned,value=BOOST_TYPEOF_INDEX(T)); BOOST_STATIC_CONSTANT(unsigned,next=value+1); //Increment the compile time constant BOOST_TYPEOF_NEXT_INDEX(next); }; It is not exactly a portable solution, though... Peder
Matthias
---------------------------------------------------------------- Matthias Schabel 2859 Glen Oaks Drive Salt Lake City, UT 84109 801-706-5760 (cell) 801-484-0811 (home) matthias at stanfordalumni dot org ----------------------------------------------------------------

_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

AMDG Peder Holt <peder.holt <at> gmail.com> writes:
You could always use the solution we use in boost.typeof. When we emulate typeof support for VC compilers, we use the following trick to associate a unique integer with each type:
<snip>
It is not exactly a portable solution, though...
Peder
And it doesn't work across translation units, which is what we need. In Christ, Steven Watanabe

"Matthias Schabel" <boost@schabel-family.org> wrote
For various reasons, it would be really nice to be able to generate a series of unique ordinal values (say integers, but really anything that is easily less_than_comparable) at compile time. A concrete example :
struct system1 : ordinal<0> { }; struct system2 : ordinal<1> { };
etc...
Now, if these tags can be defined in user code in various places, it can be difficult to ensure that there is no duplication of the ordinal values. What I want is something like this:
struct system1 : ordinal<get_next_system_ordinal()> { }; struct system2 : ordinal<get_next_system_ordinal()> { };
where I don't really care what the specific order is, just that the ordinal values are unique... Anyone have a bright idea on how to accomplish this? Preprocessor?
Yes, the preprocessor can be used. VC++ has a __COUNTER__ extension. Also there is BOOST_PP_COUNTER/BOOST_PP_UPDATE_COUNTER()(introduced in 1.34). One problem -- if this used in different headers, the generated numbers depend on the order of includes, which potentially can lead to the ODR violation :-( I once thought that the problem may be solved by compile-time GUID, but for me four numbers instead of one didn't work... but if you want _anything_ less_than_comparable, this might be an option. Regards, Arkadiy

where I don't really care what the specific order is, just that the ordinal values are unique... Anyone have a bright idea on how to accomplish this? Preprocessor?
Yes, the preprocessor can be used. VC++ has a __COUNTER__ extension. Also there is BOOST_PP_COUNTER/BOOST_PP_UPDATE_COUNTER()(introduced in 1.34).
One problem -- if this used in different headers, the generated numbers depend on the order of includes, which potentially can lead to the ODR violation :-(
Sigh. This seems like something that ought to get language support...
I once thought that the problem may be solved by compile-time GUID, but for me four numbers instead of one didn't work... but if you want _anything_ less_than_comparable, this might be an option.
I'm not familiar with compile-time GUIDs - where is this discussed? Matthias

"Matthias Schabel" <boost@schabel-family.org> wrote
I'm not familiar with compile-time GUIDs - where is this discussed?
I don't believe it was discussed -- this is just a thought. Windows (at least) has a tool (guidgen) that can be used to generate a unique 128-bit number. This is (or was) used extensively in COM. When you run the tool, you get something like: // {0E6AE480-9AE6-4f04-A24E-ABEAA55BB49A} static const GUID <<name>> = { 0xe6ae480, 0x9ae6, 0x4f04, { 0xa2, 0x4e, 0xab, 0xea, 0xa5, 0x5b, 0xb4, 0x9a } }; (you are supposed to paste this into your COM code, and give it a name, to get a globally-unique 128-bit number) If we define a template like: template<int n0, short n1, short n2, char n3, ..., char n10> struct guid; then we can use generated text to instantiate this template: typedef guid<0xe6ae480, 0x9ae6, 0x4f04, 0xa2, 0x4e, 0xab, 0xea, 0xa5, 0x5b, 0xb4, 0x9a> my_guid; Here we got a 128 bit compile-time universally-unique number. It's quite easy to define a compile-time less_then operation on such GUIDs. Regards, Arkadiy

Hello Matthias, Thursday, March 1, 2007, 12:08:50 AM, you wrote:
For various reasons, it would be really nice to be able to generate a series of unique ordinal values (say integers, but really anything that is easily less_than_comparable) at compile time. A concrete example :
struct system1 : ordinal<0> { }; struct system2 : ordinal<1> { };
etc...
Now, if these tags can be defined in user code in various places, it can be difficult to ensure that there is no duplication of the ordinal values. What I want is something like this:
struct system1 : ordinal<get_next_system_ordinal()> { }; struct system2 : ordinal<get_next_system_ordinal()> { };
where I don't really care what the specific order is, just that the ordinal values are unique... Anyone have a bright idea on how to accomplish this? Preprocessor?
Just a thought... Maybe something like this might help: typedef [implementation defined] uint_ptr_t; template< typename > void foo() {} template< typename T, uint_ptr_t TagV = (uint_ptr_t)&foo< T > > struct ordinal : public mpl::integral_c< uint_ptr_t, TagV > { }; struct system1 : ordinal< system1 > {}; struct system2 : ordinal< system2 > {}; The uint_ptr_t type should be large enough to accomodate a pointer to function. It is needed since function pointers cannot be ordered. I suspect, not all compilers would allow this. -- Best regards, Andrey mailto:andysem@mail.ru

Andrey Semashev <andysem <at> mail.ru> writes:
Just a thought... Maybe something like this might help:
typedef [implementation defined] uint_ptr_t;
template< typename > void foo() {}
template< typename T, uint_ptr_t TagV = (uint_ptr_t)&foo< T > > struct ordinal : public mpl::integral_c< uint_ptr_t, TagV > { };
struct system1 : ordinal< system1 > {}; struct system2 : ordinal< system2 > {};
The uint_ptr_t type should be large enough to accomodate a pointer to function. It is needed since function pointers cannot be ordered.
I suspect, not all compilers would allow this.
No compiler should allow it. (uint_ptr_t)&foo< T > is not a valid template argument. The address of foo<T> is not known until link time or later. In Christ, Steven Watanabe
participants (8)
-
Andrey Semashev
-
Arkadiy Vertleyb
-
Giovanni Piero Deretta
-
Lewis Hyatt
-
Matthias Schabel
-
me22
-
Peder Holt
-
Steven Watanabe