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
beforesizeof
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 classs
shall refer same declaration in context , when re-evaluated in completed scope ofs
. 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
Post a Comment