I'm trying to learn some arcane stateful template metaprogramming tricks.
(Here's why I want to learn it. Unfortunately this library doesn't work on GCC 8 nor on Clang.)
The first obvious thing I need is a constexpr
counter:
/*something*/ constexpr int foo() /*something*/
int main()
{
constexpr int a = foo();
constexpr int b = foo();
constexpr int c = foo();
static_assert(a == 0 && b == 1 && c == 2);
}
Preferably it should be a tagged counter, so that I can have several counters at the same time:
/*something*/ constexpr int foo() /*something*/
struct TagA {};
struct TagB {};
int main()
{
constexpr int a = foo<TagA>();
constexpr int b = foo<TagA>();
constexpr int c = foo<TagA>();
constexpr int d = foo<TagB>();
constexpr int e = foo<TagB>();
constexpr int f = foo<TagB>();
static_assert(a == 0 && b == 1 && c == 2);
static_assert(d == 0 && e == 1 && f == 2);
}
I did some research, but alas, none of the counters I found worked with GCC 8.
- https://github.com/DaemonSnake/unconstexpr - Works with GCC 7, doesn't work with GCC 8 nor with Clang 6. (Try it online.)
- http://b.atch.se/posts/constexpr-counter/ - Once again, works with GCC 7 and doesn't work with GCC 8 nor with Clang 6. (Try it online.)
I also found some implementations here: Does C++ support compile-time counters?, but most of them are limited to namespace scope, and others once again don't work with GCC 8.
What I did find was a simple proof-of-concept settable constexpr flag: http://b.atch.se/posts/non-constant-constant-expressions/
/*something*/ constexpr bool foo() /*something*/
constexpr bool a = foo();
constexpr bool b = foo();
constexpr bool c = foo();
static_assert (a == 0 && b == 1 && c == 1);
This one is not tagged, i.e. you can only have one per translation unit, which is not good.
I've managed to write my own tagged implementation based on it:
Usage:
int main()
{
constexpr int c0_false = Meta::Flag<TagA>::ReadSet();
constexpr int c0_true = Meta::Flag<TagA>::ReadSet(); // Will continue to return true after this point.
static_assert(c0_false == 0);
static_assert(c0_true == 1);
constexpr int c1_false = Meta::Flag<TagB>::ReadSet();
constexpr int c1_true = Meta::Flag<TagB>::ReadSet(); // Will continue to return true after this point.
static_assert(c1_false == 0);
static_assert(c1_true == 1);
}
Implementation:
namespace Meta
{
template <typename T> class Flag
{
struct Dummy
{
constexpr Dummy() {}
friend constexpr void adl_flag(Dummy);
};
template <bool> struct Writer
{
friend constexpr void adl_flag(Dummy) {}
};
template <class Dummy, int = (adl_flag(Dummy{}),0)>
static constexpr bool Check(int)
{
return true;
}
template <class Dummy>
static constexpr bool Check(short)
{
return false;
}
public:
template <class Dummy = Dummy, bool Value = Check<Dummy>(0), int = sizeof(Writer<Value && 0>)>
static constexpr int ReadSet()
{
return Value;
}
template <class Dummy = Dummy, bool Value = Check<Dummy>(0)>
static constexpr int Read()
{
return Value;
}
};
}
Next, I tried to make an actual counter.
Desired usage:
constexpr int c0 = Meta::TaggedCounter<TagA>::Value();
constexpr int c1 = Meta::TaggedCounter<TagA>::Value();
constexpr int c2 = Meta::TaggedCounter<TagA>::Value();
static_assert(c0 == 0);
static_assert(c1 == 1);
static_assert(c2 == 2);
My naïve attempt: (For some reason it stops at 1
.)
namespace Meta
{
template <typename T> class TaggedCounter
{
template <int I> struct Tag {};
public:
template <int N = 0, bool B = Flag<Tag<N>>::ReadSet()> static constexpr int Value()
{
if constexpr (B)
return 1 + Value<N+1>();
else
return 0;
}
};
}
How can I fix it?