Ich versuche, einige arkane Stateful Template-Metaprogrammierungstricks zu lernen .
(Deshalb möchte ich es lernen. Leider funktioniert diese Bibliothek weder mit GCC 8 noch mit Clang.)
Das erste, was ich brauche, ist ein constexpr
Zähler:
/*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);
}
Vorzugsweise sollte es ein markierter Zähler sein, damit ich mehrere Zähler gleichzeitig haben kann:
/*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);
}
Ich habe einige Nachforschungen angestellt, aber leider funktionierte keiner der Zähler, die ich gefunden habe, mit GCC 8.
Ich habe hier auch einige Implementierungen gefunden: Unterstützt C ++ Zähler für die Kompilierungszeit? Die meisten von ihnen sind jedoch auf den Namespace-Bereich beschränkt, andere funktionieren wiederum nicht mit GCC 8.
Was ich gefunden habe , war eine einfache, durch Proof-of-Concept einstellbare Constexpr-Flagge: 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);
Dieser ist nicht markiert, dh Sie können nur einen pro Übersetzungseinheit haben, was nicht gut ist.
Ich habe es geschafft, meine eigene getaggte Implementierung basierend darauf zu schreiben:
Verwendung:
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);
}
Implementierung:
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;
}
};
}
Als nächstes habe ich versucht, einen tatsächlichen Zähler zu erstellen.
Gewünschte Verwendung:
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);
Mein naiver Versuch: (Aus irgendeinem Grund hört es bei auf 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;
}
};
}
Wie kann ich es reparieren?
Der Hauptteil einer constexpr-Funktionsvorlage muss für alle Instatationen mit denselben Vorlagenparametern und denselben Argumenten dieselbe Antwort liefern. Sie müssen eine Indirektionsebene hinzufügen, damit die Berechnung im Standardargument eines Vorlagenparameters erfolgen kann, das vom ersten abhängig ist.
Siehe https://gcc.godbolt.org/z/GHfKKf
namespace Meta
{
template <typename T,int I> struct Tag {};
template<typename T,int N,bool B>
struct Checker{
static constexpr int currentval() noexcept{
return N;
}
};
template<typename T,int N>
struct CheckerWrapper{
template<bool B=Flag<Tag<T,N>>::Read(),int M=Checker<T,N,B>::currentval()>
static constexpr int currentval(){
return M;
}
};
template<typename T,int N>
struct Checker<T,N,true>{
template<int M=CheckerWrapper<T,N+1>::currentval()>
static constexpr int currentval() noexcept{
return M;
}
};
template<typename T,int N,bool B=Flag<Tag<T,N>>::ReadSet()>
struct Next{
static constexpr int value() noexcept{
return N;
}
};
template <typename T> class TaggedCounter
{
public:
template <int N=CheckerWrapper<T,0>::currentval()> static constexpr int Value(){
return Next<T,N>::value();
}
};
}
Dieser Artikel stammt aus dem Internet. Bitte geben Sie beim Nachdruck die Quelle an.
Bei Verstößen wenden Sie sich bitte [email protected] Löschen.
Lass mich ein paar Worte sagen