5

I have the following code:

#include <cstdlib>
#include <cstdio>
#include <atomic>

enum ATYPE { Undefined = 0, typeA, typeB, typeC };

template<ATYPE TYPE = Undefined>
struct Object
{
    Object() { counter++; }
    static std::atomic<int> counter;
};

template<ATYPE TYPE>
std::atomic<int> Object<TYPE>::counter(1);

template<ATYPE TYPE>
void test()
{
    printf("in test\n");
    Object<TYPE> o;
}

int main(int argc, char **argv)
{
    test<typeA>();
    printf("%d\n", Object<typeA>::counter.load());
    Object<typeA>::counter.store(0);
    for (int i = 0; i < sizeof(ATYPE); ++i) {
        Object<static_cast<ATYPE>(i)>::counter.store(0);
    }
    return 0;
}

When I compile with the following command line:

clang++ -o test -std=c++11 -stdlib=libc++ test.cpp

I get the following errors:

test.cpp:32:20: error: non-type template argument is not a constant expression
Object<static_cast<ATYPE>(i)>::counter.store(0);
^~~~~~~~~~~~~~~~~~~~~
test.cpp:32:39: note: read of non-const variable 'i' is not allowed in a constant expression
Object<static_cast<ATYPE>(i)>::counter.store(0);
^
testray.cpp:31:18: note: declared here
for (int i = 0; i < sizeof(ATYPE); ++i) {

I understand the problem I believe. The argument of the template needs to be a constexpr and i clearly is not. So the question is, are they possible changes I can do to get this working. By this working, I mean, can I somehow have a better way of resetting these static counters from this template class for each type in ATYPE, other than just doing it manually:

Object<Undefined>::counter.store(0);
Object<typeA>::counter.store(0);
...

Which is not so elegant and practical when ATYPE contains many types.

Thanks a lot for your help and advices.

Jesse Good
  • 50,901
  • 14
  • 124
  • 166
user18490
  • 3,546
  • 4
  • 33
  • 52
  • 1
    You're looping through your enum wrong anyway. `i` isn't a constant expression either as the error shows. – Rapptz Aug 11 '13 at 23:31
  • @Rapptz, could you please be more specific about your first comment (looping through the enum wrong). I know i is not const, so that's why I am asking if there's somehow a way of making this work. Is there somehow a way of looping over all the elements of the enum and resetting the counters? thank you. – user18490 Aug 11 '13 at 23:36
  • What Rapptz means is that i is dynamic and cannot be used as a compile time template argument – a.lasram Aug 11 '13 at 23:37
  • Yes that's good, I understand that now, hence the message of i being non const which is required etc. I get that. I would like to know if anyone can suggest a technique other than just doing it manually for each element of the enum ...? thank you. – user18490 Aug 11 '13 at 23:39
  • @user18490 hint: `sizeof(enum) == sizeof(std::underlying_type::type)` – Rapptz Aug 11 '13 at 23:40
  • @Rapptz, arg, yes I am stupid, thanks a lot... I know this but I made the mistake. Okay so sizeof(ATYPE) in this case returns sizeof(unsigned), because they type of the enum is this case is unsigned (and not number of constants in the enum). Would the best solution for this sort of thing, to put a constant to mark the end of the enum which we can use as the upper limit? enum E = { c0 = 0, c1, c2, c3, end }; for (unsigned i = c0; i < end; ++i) { ... } – user18490 Aug 11 '13 at 23:46
  • @user18490 the sizeof the enum isn't the same as the number of possible enum values it takes on. For example, if you add typeD, typeE, typeF then your loop is wrong since `sizeof(ATYPE)` is still the same. – greatwolf Aug 11 '13 at 23:46
  • @greatwolf, yes thank you I finally understand what Rapptz meant to say. I am still interested in finding a way of looping over all the possible Object<>::counter and reset them, other than doing this for each constant in the enum by hand. Anyone? – user18490 Aug 11 '13 at 23:49

1 Answers1

7

For these kind of things, recursion is typically a simple solution:

#include <type_traits>

enum ATYPE { Undefined = 0, typeA, typeB, typeC, ATYPE_END };

void reset_Object_counter(std::integral_constant<ATYPE, ATYPE_END>)
{}

template < ATYPE n = Undefined >
void reset_Object_counter(std::integral_constant<ATYPE, n> p = {})
{
    Object<p>::counter.store(0);
    reset_Object_counter(std::integral_constant<ATYPE,
                                                static_cast<ATYPE>(n+1)>{});
}

For this case, AFAIK, function template specialization works as well (instead of the first overload):

template<>
void reset_Object_counter<ENUM_END>(std::integral_constant<ATYPE, ENUM_END>)
{}

Either way, the usage is just reset_Object_counter(); to set all Object<...>'s counters to 0.


The integral_constant solution is actually a bit overkill here, for this problem a non-type template parameter is sufficient (because the function template specialization can take the place of the overload ending the recursion).

template < ATYPE n = Undefined >
void reset_Object_counter()
{
    Object<n>::counter.store(0);
    reset_Object_counter<static_cast<ATYPE>(n+1)>();
}
template<>
void reset_Object_counter<ENUM_END>()
{}
dyp
  • 38,334
  • 13
  • 112
  • 177
  • Fantastic. That's where I realize I don't know C++ enough. I have never heard of integral_constant... but I am catching up on all the new things we can find in C++11 and it seems like it can take a lifetime. Thanks a lot great answer, and thx to everybody else. – user18490 Aug 11 '13 at 23:54
  • @user18490 The `integral_constant` isn't even required here ;) – dyp Aug 12 '13 at 00:00
  • yes I am actually trying to write the code without, because I guessed it could work without, but I was glad to learn something anyway. Thanks for this addition remark. – user18490 Aug 12 '13 at 00:10
  • thanks for adding the second solution. I was almost half-way through but having your example saved me time. Thanks again, great great help. – user18490 Aug 12 '13 at 00:19