c++ - Compile-time counter in template class -


i have compile-time counter used years, inspired these answers. works in c++03/11, , far tested, relatively on major compilers:

namespace meta {     template<unsigned int n> struct count { char data[n]; };     template<int n> struct icount : public icount<n-1> {};     template<> struct icount<0> {};      #define max_count 64     #define make_counter( _tag_ ) \         static ::meta::count<1> _counter ## _tag_ (::meta::icount<1>)     #define get_count( _tag_ ) \         (sizeof(_counter ## _tag_ (::meta::icount<max_count + 1>())) - 1)     #define inc_count( _tag_ ) \         static ::meta::count<get_count(_tag_) + 2> _counter ## _tag_ (::meta::icount<2 + get_count(_tag_)>) } 

the following test compiles , runs perfectly (expected output 0 1 2 3):

struct test {     make_counter( uu );      static const unsigned int = get_count( uu );     inc_count( uu );     static const unsigned int b = get_count( uu );     inc_count( uu );     static const unsigned int c = get_count( uu );     inc_count( uu );     static const unsigned int d = get_count( uu );  };  template<typename t> void test() {     std::cout << t::a << " " << t::b << " " << t::c << " " << t::d << "\n"; }  int main() {     test<test>(); } 

however, found case see strange behavior happening clang , gcc. if change test template struct, taking int example (template<int> struct test, , test<test<42> >() in main), clang , gcc both fail compile, complaining redefining counter function (while msvc compiles without problems). reason compiler fails compute sizeof in template class.

clang find error @ third inc_count, while gcc find @ second one.

i manually expanded macro, and:

  • for clang, gives

    static ::meta::count<get_count(uu)+2> _counteruu(::meta::icount<(sizeof(_counteruu(::meta::icount<65>())) - 1)+2>); //                                                              ^                                            ^ 

    removing underlined parentheses solves issue.

  • for gcc: moving +2 before sizeof work-around

the sad note these workarounds seem not work when included in macros. it's compiler forgets how compute result of sizeof after time...

why happening ? doing wrong, or compiler bugs (since clang , gcc don't report same line) ?

note: know there gcc bug counter. question not bug.

your code ill-formed, no diagnostic required. §3.3.7/1, second bullet point1:

a name n used in class s shall refer same declaration in context , when re-evaluated in completed scope of s. no diagnostic required violation of rule.

you use overload resolution select appropriate overload of _counteruu. however, in initializer of e.g. a, overload (=declaration) selected wouldn't selected if perform overload resolution @ end of test, such in initializer of d. hence _counteruu refers another, distinct declaration when re-evaluated in completed scope of test.

to show calls i'm referring to, consider preprocessed definition of test:

struct test {     // (1)     static ::meta::count<1> _counteruu (::meta::icount<1>);     static const unsigned int = (sizeof(_counteruu (::meta::icount<64 + 1>())) - 1);     // (2)     static ::meta::count<(sizeof(_counteruu (::meta::icount<64 + 1>())) - 1) + 2> _counteruu (::meta::icount<(sizeof(_counteruu (::meta::icount<64 + 1>())) - 1) + 2>);     static const unsigned int b = (sizeof(_counteruu (::meta::icount<64 + 1>())) - 1);     // (3)     static ::meta::count<(sizeof(_counteruu (::meta::icount<64 + 1>())) - 1) + 2> _counteruu (::meta::icount<(sizeof(_counteruu (::meta::icount<64 + 1>())) - 1) + 2>);     static const unsigned int c = (sizeof(_counteruu (::meta::icount<64 + 1>())) - 1);     // (4)     static ::meta::count<(sizeof(_counteruu (::meta::icount<64 + 1>())) - 1) + 2> _counteruu (::meta::icount<(sizeof(_counteruu (::meta::icount<64 + 1>())) - 1) + 2>);     static const unsigned int d = (sizeof(_counteruu (::meta::icount<64 + 1>())) - 1);  }; 

simplification yields

struct test {     // (1)     static ::meta::count<1> _counteruu (::meta::icount<1>);     static const unsigned int = (sizeof(_counteruu (::meta::icount<65>())) - 1);     // (2)     static ::meta::count<2> _counteruu (::meta::icount<2>);     static const unsigned int b = (sizeof(_counteruu (::meta::icount<65>())) - 1);     // (3)     static ::meta::count<3> _counteruu (::meta::icount<3>);     static const unsigned int c = (sizeof(_counteruu (::meta::icount<65>())) - 1);     // (4)     static ::meta::count<4> _counteruu (::meta::icount<4>);     static const unsigned int d = (sizeof(_counteruu (::meta::icount<65>())) - 1); }; 

we can see how mechanism works now: overload resolution prefer last added overload when icount<some sufficiently large number> passed due way derived-to-base conversions ranked. however, call in initializer of a select first overload; re-evaluating initializer select last one.


1 bullet point existed in c++03 well, in §3.3.6.


Comments

Popular posts from this blog

qt - Using float or double for own QML classes -

Create Outlook appointment via C# .Net -

ios - Swift Array Resetting Itself -