2

I use a compile-time counter in some applications, and it is really useful. Yesterday I wanted to compile a program with gcc (I was using msvc before), and the behavior of the counter changed in templated classes (it is not working anymore in template classes).

The overly simplified code:

// Maximum value the counter can hold
#define MAX_COUNT 64

// Used to store a number as the size of the structure
template<unsigned int n> struct Count { bool data[n]; };

// Used to overload functions while automatically selecting the
// right one based on the counter value (see following code)
template<int n> struct cn : public cn<n-1> {};
template<> struct cn<0> {};

struct Test
{
    #define COUNT \
        ((sizeof(f(cn<MAX_COUNT + 1>())) / sizeof(bool)) - 1)

    static Count<1> f(cn<1>);

    /*
        f(cn<65>()) will 'call' f(cn<1>) and return a Count<1> with size 1
        -> count = 0;
    */
    static const int a = COUNT;
    static Count<COUNT + 2> f(cn<COUNT + 2>); // increase counter

    /*
        now Count<2> f(cn<2>) is defined, so:
        f(cn<65>()) will 'call' f(cn<2>) and return a Count<2> with size 2
        -> count = 1;
    */
    static const int b = COUNT;
};

The idea is to use function overloading, and if you test the above code it will work perfectly (a == 0 and b == 1).

However, if the struct Test is made template (by just adding template<something> before its declaration for example, no need to use the template parameter), the counter breaks and I end up with a == b == 1. It also implies that it is impossible to increment the counter in these conditions.

So here are my questions :

  • what template rules are at work here ?
  • why that specific behavior ?
  • do you have an idea of a work-around for the counter to actually work ?

Note: I would like a C++03 answer for compatibility with old compilers (even if I'm curious to know if rules for that specific case changed for C++11)

EDIT: Some outputs:

  • VC2010:

    Templated
    a = 0
    b = 1
    
  • GCC 4.8.1:

    Templated
    a = 1
    b = 1
    
  • Clang 3.4 (thanks to dyp):

    Templated
    a = 0
    b = 1
    

EDIT 2

GCC seems to take Count as a dependent name, as observable here (thanks to dyp). I posted a bug report in gcc bugzilla here.

Synxis
  • 9,236
  • 2
  • 42
  • 64
  • Taken a look at the macro `__COUNTER__`? – Ylisar May 07 '14 at 18:54
  • `__COUNTER__` is not a choice here, because 1/ it is global per translation unit, 2/ it is incremented every use, and 3/ you can only have 1 counter with this macro... – Synxis May 07 '14 at 19:09
  • 1
    `#define MAX_COUNT 64` :( There are so nice alternatives even in C++03. Why do you use a macro here? – dyp May 07 '14 at 20:24
  • 1
    Different behaviour between clang++3.4 and g++4.8.2 -- clang++ outputs `0 1`, g++ outputs `1 1`. So either a compiler bug or unspecified/undefined behaviour. – dyp May 07 '14 at 20:29
  • Yes, I could use a constant (although remember that this is a simplified example, in real code all this is put inside a detail namespace). However that's not the point of the question... – Synxis May 07 '14 at 20:29
  • Ah, different behaviors, that's interesting ! Do you know where I can test with clang online ? – Synxis May 07 '14 at 20:30
  • Here: http://coliru.stacked-crooked.com/a/4794e609ca60a131 – dyp May 07 '14 at 20:34
  • Didn't know that you could use clang with coliru ! Actually you linked the version that was supposed to work. But you're still right: the non-working-on-gcc version does work on clang. Thanks for the info. – Synxis May 07 '14 at 20:38
  • g++ seems to think that `COUNT` is dependent. It might therefore postpone name lookup of `f`. See http://coliru.stacked-crooked.com/a/daa2c7d956f9ea9a – dyp May 07 '14 at 20:45
  • I was thinking that there was a rule so that all was instantiated only later, and then when it was it already had all function prototypes, so that there would be only one value for the counter. But I think you've probably found the cause of the problem. – Synxis May 07 '14 at 20:54
  • 1
    @Danvil haha... and that can qualify as 'simple' next to some template metaprogramming I have. **To all:** [I posted a bug report in gcc bugzilla](http://gcc.gnu.org/bugzilla/show_bug.cgi?id=61103), let's see if they find something (and at least answer ;) ) – Synxis May 07 '14 at 21:26

1 Answers1

0

This could be attributes to the two-phase name look-up in C++. This is a feature of C++03 that also exists in C++11. The LLVM project did an article on this feature and compare its Clang compiler to GCC and Visual C++. GCC and Visual C++ do not support this feature, but Visual C++ does have a method of handling name look-ups that will allow code dependent on the two-phase look-up to work more often than on GCC. It also allows code that is not valid C++ code to work as well according to the article.

EDIT: I misread the article, GCC does implement the two-phase look-up, but it can choose to delay the look-up until template instantiation. This does appear to be a bug in GCC though.

Matthew
  • 771
  • 6
  • 15
  • Yeah, I thought is was related in some sort to the two-phase lookup, but with dyp example it seems to be a false positive about an independent name taken for dependent. However, I think gcc implements the two-phase name lookup (at least since 4.7) ? – Synxis May 08 '14 at 08:18
  • I think your right, I misread the article. If it is a bug in GCC, then it would probably be related to it's implementation. It seems it chooses to delay some name look-ups until instantiation of the template. Probably the code that makes the choice of when to perform the look-up could be at fault. – Matthew May 08 '14 at 12:45