#include <array>
#include <iostream>
#include <tuple>
using namespace std;
// Helper for Shorthand Option 1 (below)
template<typename Singleton>
Singleton* singleton;
// Helper to store string literals at compile-time
template<typename ParentDispatcher>
struct Tag {
using Parent = ParentDispatcher;
const char* name;
};
// ---------------------------------
// DISPATCHER:
// ---------------------------------
// Call different functions at compile-time based upon
// a compile-time string literal.
// ---------------------------------
template<auto& nameArray, typename FuncTuple>
struct Dispatcher {
FuncTuple _funcs;
using DispatcherTag = Tag<Dispatcher>;
template<size_t nameIndex>
static constexpr DispatcherTag TAG = {nameArray[nameIndex]};
static constexpr DispatcherTag INVALID_TAG = {};
Dispatcher(const FuncTuple& funcs) : _funcs(funcs) {
singleton<Dispatcher> = this;
}
template<size_t nameIndex = 0>
static constexpr auto& tag(string_view name) {
if(name == nameArray[nameIndex]) {
return TAG<nameIndex>;
} else {
if constexpr (nameIndex+1 < nameArray.size()) {
return tag<nameIndex+1>(name);
} else {
return INVALID_TAG;
}
}
}
static constexpr size_t index(string_view name) {
for(size_t nameIndex = 0; nameIndex < nameArray.size(); ++nameIndex) {
if(name == nameArray[nameIndex]) {
return nameIndex;
}
}
return nameArray.size();
}
constexpr auto& operator()(const char* name) const {
return tag(name);
}
template<auto& tag, typename... Args>
auto call(Args... args) const {
static constexpr size_t INDEX = index(tag.name);
static constexpr bool VALID = INDEX != nameArray.size();
static_assert(VALID, "Invalid tag.");
return get<INDEX*VALID>(_funcs)(args...);
}
};
template<auto& nameArray, typename FuncTuple>
auto makeDispatcher(const FuncTuple& funcs) {
return Dispatcher<nameArray, FuncTuple>(funcs);
}
// ---------------------------------
// SHORTHAND: OPTION 1
// ---------------------------------
// Use a singleton pattern and a helper to let a tag be associated with a
// specific dispatcher, so that the call-site need not specify dispatcher twice
// ---------------------------------
template<auto& tag, typename... Args>
auto call(Args... args) {
using Tag = remove_reference_t<decltype(tag)>;
using ParentDispatcher = typename Tag::Parent;
static auto dispatcher = singleton<ParentDispatcher>;
return dispatcher->template call<tag>(args...);
}
// ---------------------------------
// SHORTHAND: OPTION 2
// ---------------------------------
// Use a string template user-defined literal operator to shorten call-site syntax
// gcc supports this as an extension implementing proposal N3599 (standardized in C++20)
// If warnings occur, try pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template"
// ---------------------------------
// Need characters to be in contiguous memory on the stack (not NTTPs) for TAG_FROM_LITERAL
template<char... name>
constexpr char NAME_FROM_LITERAL[] = {name..., '\0'};
// Don't need to specify Dispatcher with user-defined literal method; will use dispatcher.check<>()
struct TagFromLiteral {};
// Need to have a constexpr variable with linkage to use with dispatcher.check<>()
template<char... name>
constexpr Tag<TagFromLiteral> TAG_FROM_LITERAL = {NAME_FROM_LITERAL<name...>};
// Create a constexpr variable with linkage for use with dispatcher.check<>(), via "MyTag"_TAG
template<typename Char, Char... name>
constexpr auto& operator"" _TAG() {
return TAG_FROM_LITERAL<name...>;
}
// ---------------------------------
// SHORTHAND: OPTION 3
// ---------------------------------
// Use a macro so the call-site need not specify dispatcher twice
// ---------------------------------
#define DISPATCH(dispatcher, name) dispatcher.call<dispatcher(name)>
// ---------------------------------
// COMMON: TEST FUNCTIONS
// ---------------------------------
bool testFunc1(int) { cout << "testFunc1" << endl; }
bool testFunc2(float) { cout << "testFunc2" << endl; }
bool testFunc3(double) { cout << "testFunc3" << endl; }
static constexpr auto funcs = make_tuple(&testFunc1, &testFunc2, &testFunc3);
static constexpr auto names = array{"one", "two", "three"};
int main()
{
// Create a test dispatcher
auto dispatcher = makeDispatcher<names>(funcs);
// LONG-HAND: call syntax: a bit verbose, but operator() helps
dispatcher.call<dispatcher("one")>(1);
// SHORTHAND OPTION 1: non-member helper, singleton maps back to dispatcher
call<dispatcher("one")>(1);
// SHORTHAND OPTION 2: gcc extension for string UDL templates (C++20 standardizes this)
dispatcher.call<"one"_TAG>(1);
// SHORHAND OPTION 3: Macro
DISPATCH(dispatcher, "one")(1);
return 0;
}