constexpr string-literal checking: short syntax, no runtime possibility - c++

EDIT: Renamed, as my final solution does not use a poisoning method.
I'm looking for a way to prevent a constexpr method from being called at runtime. I'm writing a function that accepts a string literal, so I cannot simply use a NTTP as a way to require a constexpr parameter:
template<const char* str>
auto func() {...}
Because then even legitimate constexpr uses become cumbersome, requiring values to have static linkage, and you can't feed in a string literal. I want to do:
constexpr auto func(const char* str) {...}
The reason is because I check the string against a list of values, and want to STATICALLY check that the parameter is contained in the allowed set. I can do this easily: by just throw()'ing in a constexpr function, you can cause a compile-time error. But I do not want the possibility of generating production code with some branch that causes the program to exit at runtime. That would cause a major problem in my field; this feature is a nice-to-have in a program that does "other important stuff" and bad things happen if the program terminates.
I read about a whole bunch of possible ways to do this:
Use C++20 consteval - don't have C++20
Use C++20 std::is_constant_evaluated - don't have C++20
Poison the method at runtime by returning a result to an undefined symbol (e.g. extern int i that never gets defined). The compiler never creates code which returns that symbol if the method is called at compile-time, but it does if the method is called at runtime, resulting in a link error. Works, but ugly linker error; not my favorite. A version of that is shown in my post here: Constexpr functions not called at compile-time if result is ignored.
In C++17, noexcept gets automatically added onto any call to a constexpr function that actually gets called in a constexpr context. So you can do noexcept(foo(1)) vs noexcept(foo(i)) for constexpr int foo(int i) (not explicitly declared noexcept) to detect whether i causes the call to be constexpr/not. But you can't do that from within a constexpr function that has accepted some parameter - you need to do it from the call-site. So: probably requires a helper MACRO (not my favorite but it works).
Create a lambda whose return type is invalid if certain variables outside the scope of the lambda are not constexpr. This post goes into detail: https://stackoverflow.com/a/40413051
So I'm leaning towards using #3 or #4 + a macro, but ***this post is about #5 ***, or totally new ideas.
Can anyone come up with a way to do #5 without a lambda, for example? After that, I want to see if I can come up with a way to use it within the constexpr function itself rather than requiring it to be used from the call-site. For now, just try to poison a constexpr function if it is called at runtime, forget about "detecting" whether the function call is constexpr.
I can re-create the results of #5 by creating a lambda in main as the author did, but that's not actually very useful, and I'm still not convinced that it's fully legal. To start with, anything that can be done with a lambda can be done without a lambda -- right??? I can't even get the original author's method to work without a lambda. That seems like a first required step to get it to work outside of main().
Below are a couple ideas I've tried to recreate #5 without a lambda. Live example with a billion more permutations, none of which work: https://onlinegdb.com/B1oRjpTGP
// Common
template<int>
using Void = void;
// Common
struct Delayer {
constexpr auto delayStatic(int input) { return input; }
};
// Attempt 1
template<typename PoisonDelayer>
constexpr auto procurePoison(int i) {
struct Poison {
// error: use of parameter from containing function
// constexpr auto operator()() const -> Void<(PoisonDelayer::delayStatic(i), 0)> {}
} poison;
return poison;
}
// Attempt 2
struct PoisonInnerTemplate {
const int& _i;
// Internal compiler error / use of this in a constexpr
template<typename PoisonDelayer>
auto drink() const -> Void<(PoisonDelayer::delayStatic(_i), 0)> {}
};
int main()
{
auto attempt1 = procurePoison<Delayer>(1);
constexpr int i = 1;
auto attempt2 = PoisonInnerTemplate{i};
attempt2.drink<Delayer>();
return 0;
}
One more thing: I toyed with the idea of making a pre-defined list of allowed tags (wrap the string in a struct so it can be a NTTP), and putting them in some kind of container, and then having a method to retrieve them. The problems are: (1) the call-site syntax gets quite verbose to use them, (2) although it'd be fine for the call-site to use a syntax like MyTags::TAG_ONE, my program needs to be able to know the full set of tags, so they need to be in an array or a template variable, (3) using an array doesn't work, because getting an array element produces an rvalue, which doesn't have linkage, so can't be fed as a NTTP, (4) using a template variable with explicit specialization to define each tag requires the template variable to be global-scope, which doesn't work well for me...
This is about the best I can do - I think it's kind of ugly...:
struct Tag {
const char* name;
};
template<auto& tag>
void foo() {}
struct Tags {
static constexpr Tag invalid = {};
static constexpr Tag tags[] = {{"abc"}, {"def"}};
template<size_t N>
static constexpr Tag tag = tags[N];
template<size_t N = 0>
static constexpr auto& getTag(const char* name) {
if constexpr(N<2) {
if(string_view(name)==tag<N>.name) {
return tag<N>;
} else {
return getTag<N+1>(name);
}
} else {
return invalid;
}
}
};
int main()
{
foo<Tags::getTag("abc")>();
}

Here's my own answer, which checks that a string literal is within an allowed set at COMPILE-TIME, then performs an action based upon the value of that string. No poisoning of constexpr functions is needed, and there are still no cumbersome requirements to provide string literals with static linkage.
Credit goes to Jarod42 for "shorthand option 2", which uses a gcc extension for string template user-defined literals, which is part of C++20 but not C++17.
I think I'm happy enough with any of the three "shorthand" call-site syntaxes. I would still welcome any alternatives or improvements, or pointers on what I messed up. Perfect forwarding, etc. is left as an exercise for the reader ;-)
Live demo: https://onlinegdb.com/S1K_7sb7D
// 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;
}

Related

Generate unique string literal for asm from class template parameter

I have a very special case, where I need to generate a unique assembler name for a variable declared in a class template. I need that name to be unique for every instance of the class template and I need to pass it to the asm keyword (see here).
My specific case looks somewhat like this:
template <size_t n>
struct StringLiteral
{
constexpr StringLiteral (const char (&str)[n])
{
std::copy_n (str, n, value);
}
char value[n];
static constexpr size_t len = n;
};
template <StringLiteral text>
class Foo
{
private:
template <size_t... i>
static const char* get (const std::index_sequence<i...>&)
{
__attribute__((section("<Some specific section>"), internal_linkage)) static const char t[text.len] asm ("<The unique label>") = { text.value[i]... };
return t;
}
public:
static const char* get()
{
return get (std::make_index_sequence<text.len>());
}
}
Where I'd like these three instances to contain three different replacements for "<The unique label>" in their get function:
auto* one = Foo<"Instance One">::get();
auto* two = Foo<"Instance Two">::get();
auto* three = Foo<"Instance Three">::get();
To my limited knowledge, it's only possible to pass hard coded string literals as assembler names to asm, but maybe someone knows some cool trickery. A name made up of a string prefix with a unique numerical postfix per template instance would be totally fine.
The compiler used is Apple Clang 14, so answers with compiler specific solutions are fine. The code uses C++ 20.

Constexpr constructible function object

I have a question somewhat similar to this one, but for a more limited case which I believe should be possible somehow: I want to construct a static constexpr array of function calls from a number of lambdas, each sharing the same signature. The static and constexpr part is important here since I'm on an embedded system, where I want to make sure such tables end up in Flash.
So basically what I want to do is
#include<vector>
#include<functional>
#include<variant>
using params_t = std::vector<std::variant<int, float /*maybe others*/ >>;
struct command_t {
using callable_t = std::function<void(params_t)>;
const callable_t func;
//other members..
};
class AClass {
template<typename func_t>
constexpr static command_t::callable_t make_callable(func_t fun) {
return [fun](params_t params){/*construct a call to fun using params and template magic*/};
}
static void mycommand();
static void mycommand2(int i);
//The following fails:
///"error: in-class initialization of static data member 'const command_t AClass::commands [2]' of non-literal type"
static constexpr command_t commands[2] = {command_t{make_callable(mycommand)},
command_t{make_callable(mycommand2)}};
};
On coliru
Note that the type erasure here is quite limited, since the signature of the lambda varies only by the signature of the capture of fun. The function call obviously doesn't (and cannot) need to be constexpr, only the construction.
So basically my question is can I somehow make the commands array static constexpr, either somehow using std::function, or something like inplace_function, or perhaps by spinning my own code for type-erasing the lambda in this specific case?
In general, this is not possible since the capture of a lambda can get arbitrary large and hence, at some point we need a heap allocation which then kills any hopes of constexpr pre-C++20 (and I don't think C++20 will help much for this case, either).
But you only want to capture a function pointer if I see this right and that we can do:
#include <vector>
#include<variant>
using params_t = std::vector<std::variant<int, float /*maybe others*/ >>;
struct command_t {
using callable_t = void (*)(std::vector<params_t>);
const callable_t func;
//other members..
};
template<auto f>
void wrap(std::vector<params_t>){
// make this dependent of f, maybe use function_traits for fancy stuff
}
class AClass {
static void mycommand();
static void mycommand2(int i);
static constexpr command_t commands[2] = {wrap<mycommand>, wrap<mycommand2>};
};
int main() {
}
Thanks to xskxzr for valuable suggestions.
Since mycommanN have different signatures and you need to capture them, I don't see a way to have a constexpr vector. Maybe someone can come up with a better design.
I have a solution: use std::tuple. But I don't really like as it is really cumbersome to work with tuple as a container. For instance iterating over it is ... let's say not a walk in the park. Anyway, here it is in case it does help:
using params_t = std::vector<std::variant<int, float /*maybe others*/>>;
// I needed to lift this out of AClass because of ... complicated reasons
// (short version: when both are AClass members
// the return type of `make_command` is not resolved in the init of `commands`
// because both are static, `commands` is not a template and `make_command` is a template
// tbh I don't know exactly what is happening. It's one of those dark corners of C++)
template <class RealFunc>
static constexpr auto make_command(RealFunc real_func) {
return [real_func](params_t params) { /*magic*/ };
}
struct AClass {
static void mycommand();
static void mycommand2(int i);
static constexpr std::tuple commands{make_command(mycommand),
make_command(mycommand2)};
};
// usage
auto test() {
constexpr auto command0 = std::get<0>(AClass::commands<>);
params_t params0 = {};
return command0(command0);
}

nested calls to constexpr functions

Following (generic) situation:
I am trying to call another constexpr member-function within a class, but I get the error that 'this' is not a constant expression. The question I now have is (as I am new to the constexpr idiom), is if such a use-case is not allowed. Otherwise, how would I fix it?
#include <iostream>
class cc
{
public:
cc()=default;
~cc()=default;
// public method to call th eprivate constexpr functions
template<int varbase>
constexpr void doSomething()
{
static_assert(varbase>0, "varbase has to be greater zero");
// nested call to constexpr functions
char x=testM2<testM1<varbase>()>();
}
private:
template<int var1>
constexpr int testM1()
{
int temp=var1;
return (++temp);
}
template<int var2>
constexpr char testM2()
{
int temp=var2;
if (temp==2)
return 'A';
else
return 'B';
}
};
int main()
{
cc obj;
obj.doSomething<1>();
return 0;
}
Run the above example
So constexpr does not mean "this function can only be called at compile time". It means "this function may be called both at compile and run time".
char x=testM2<testM1<varbase>()>();
If called at run time, the value of this is not a compile time constant. So the result of a constexpr method call is not a compile time value, so its result cannot be used as a template argument.
If you are not using this (as you appear not to be) then marking testM2 etc. as static fixes your problem. If you are using this then there isn't a simple workaround; there is no way to say "this function will only be called at compile time". Any workaround will depend on details of what you are doing with this and the return value, if even possible. Write a new question with those details added.
The problem is that every instance of cc will have its own version of doSomething(), testM1() and testM2().
To solve it, the compiler will apply this in front of all the functions, to identify which instance of cc you are trying to refer to.
this is not a constant expression. It is evaluated at runtime.
That is why you are getting the error : 'this' is not a constant expression
To solve this problem, either move your constexpr functions outside the class, or mark these functions as static.
Here is a working revision of your code, which will work in C++11 as well.
#include <iostream>
template<int var1>
constexpr int testM1() // move these functions outside, if they use no data of class cc
{
return int{var1 + 1};
}
template<int var2>
constexpr char testM2()
{
return (var2 == 2) ? 'A' : 'B';
}
class cc
{
public:
cc()=default;
~cc()=default;
// public method to call the private constexpr functions
template<int varbase>
static constexpr void doSomething()
{
/* if you want to keep it inside class, mark it `static`
so that the compiler understands that it will be shared by all instances. */
static_assert(varbase>0, "varbase has to be greater zero");
// nested call to constexpr functions
constexpr char x = testM2<testM1<varbase>()>(); // preferably x should be `constexpr`
}
};
int main()
{
cc obj;
obj.doSomething<2>();
return 0;
}

How to properly pass C strings to a lambda inside a variadic template function

Here is my complex situation:
I have a function using variadic template and lambda:
template<typename...Args>
void foo(Args...args) {
// the arguments are passed to the lambda
_func = [args...](){ do_sth(args...); };
}
On observing specific events, the lambda _func would be fired.
My problem is, some of the arguments I passed are C strings, they could be pointing to temporary std string like this:
const char *pStr = __temp_std__string__.c_str();
Given I call foo(pStr);, and when _func is called, the temporary string that pStr is pointing to, has been released. I would like to know whether there exists a generic way to handle this. I am using C++11.
EDIT:
Perhaps I should post my whole story, as many of you advise to pass std::string instead of C strings, there are reasons that I can't escape from it.
I am developing games using cocos2d-x, which deploys C++11. What I want to do is to support auto-localisation of labels when players change their preferences of languages (selected from a UI).
I have saved the text in a couple of files, and each of them contains the localised text of a single language, they are basically under the following structure:
{
"key1" : "_localized_text1_",
"key2" : "_localized_text2_",
...
}
The idea is to observe the event on change of language's preference (through a notification), and I would get a key indicating that language from it, so as to fetch the localised text from the proper file. Here is the way how I implement it in the object class Label:
class Label {
// this method would update the label's displayed text
void setString(const std::string& text);
// set a callback for changing language
void setOnLanguageChangeFunc(std::function<void(Notification*)> func);
// set a localised text, which would be updated on changing language
void setLocalizeString(const std::string& key);
};
the core function is setLocalizeString (I skip the implementations of the other 2 methods as they are intuitive enough from their declaration):
void Label::setLocalizeString(const std::string& key) {
// the callback lambda
auto callback = [=](Notification *pNotification){
setString(LOCALIZED_STRING(key));
}
// assign the lambda
setOnLanguageChangeFunc(callback);
}
where LOCALIZED_STRING is the macro helper of fetching localised string with a key; and the lambda callback would be saved as local member variable of Label in setOnLanguageChangeFunc.
This works great in most cases, what makes the situation complicated is, there are format specifiers involved in the localised text, for example:
{
...
"keyN" : "%s eats %d cookies",
...
}
Such format placeholders are passed dynamically in codes:
// formatStr = "Tom eats 5 cookies"
std::string formatStr = StringKit::stringWithFormat("%s eats %d cookies", "Tom", 5);
where StringKit is a utility to format the string, and it accepts variadic arguments which would be passed to vsnprintf to yield the output. Now you know why I need to pass C string and not std::string, its just due to the underlying method to format string.
Now I have to modify Label::setLocalizeString so that it could digest the possible variadic arguments:
template<typename... Args>
void setLocalizeString(const std::string& key, Args... args)
{
// the callback lambda
auto callback = [=](Notification *pNotification){
setString(StringKit::stringWithFormat(LOCALIZED_STRING(sKey), args...));
}
// assign the lambda
setOnLanguageChangeFunc(callback);
}
And this is its use case:
// on changing language, the label would display "Tom eats 5 cookies"
pLabel->setLocalizeString("keyN", "Tom", 5);
This case would work like a charm as that C string argument is global, but when it is passed from a temporary std::string:
std::string tempStr = "Tom";
pLabel->setLocalizeString("keyN", tempStr.c_str(), 5);
The C string "Tom" would lose the value on calling the lambda callback, since the pointed std::string, has been gone.
I have tried several ways, like playing with tuple things, or capturing a wrapper class of basic types in the lambda, but none of them could solve the problem. However, I think there should exist some tricky solutions.
This problem is not related to lambdas or variadic functions - it also occurs if you simply store the string:
const char* global_storage;
int main()
{
{
std::string s = "hi";
global_storage = s.c_str();
}
// !!! `global_storage` points to deleted memory!
use(global_storage);
}
You need to make sure the string lives long enough. Using std::string instead of const char* is a great starting point:
std::string global_storage;
int main()
{
{
std::string s = "hi";
global_storage = std::move(s);
}
// OK, local string was moved into `global_storage`.
use(global_storage.c_str());
}
If you really need to use a C-style string, just store it in the lambda/whatever as a std::string, then call .c_str() when you need to use it, not when storing it.
You need to convert your char const* arguments to std::string when storing it in lambda. This is one possible way, i can propose:
#include <iostream>
#include <tuple>
using namespace std;
template<typename T, typename R = conditional_t<is_same<T, char const*>::value, string, T>>
R bar (T &&value) {return value;}
template<class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(std::basic_ostream<Ch,Tr>& os,
const Tuple & t,
std::index_sequence<Is...>)
{
using swallow = int[]; // guaranties left to right order
(void)swallow{0, (void(os << (Is == 0? "" : ", ") << std::get<Is>(t)), 0)...};
}
template<class Ch, class Tr, class... Args>
decltype(auto) operator<<(std::basic_ostream<Ch, Tr>& os,
const std::tuple<Args...>& t)
{
os << "(";
print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
return os << ")";
}
template<typename...Args>
decltype(auto) foo(Args...args)
{
return [args = make_tuple(bar(args)...)] () { cout<< args; return; };
}
int main() {
string *s = new string("Hello, World!");
const char *p = s->c_str();
auto f = foo(1, p, 3.14);
delete s;
f();
return 0;
}
Function foo returns lambda that stores variadic arguments as tuple, where each char const* element is converted to std::string automatically. After that you can free temporary string. It's now should be safe to call that lambda after freeing.
IdeOne.com

C++ runtime type switching (avoiding switch)

I've been into C++ for some years but I have not found yet the solution to a problem I constantly have. Know how to solve it would be awesome.
What I have at the moment is:
// Client code:
switch(currentEnumValue)
{
case MyEnum::kValue01:
processData<MyEnum::kValue01>(data);
break;
case MyEnum::kValue02:
processData<MyEnum::kValue02>(data);
break;
default:
LOG("Invalid command");
break;
}
// Declarations
enum class MyEnum {kValue01, kValue02};
class MyClass
{
// code
template <MyEnum> void processData(char*); /* Implemented somewhere else */
}
template <> void MyClass::processData<MyEnum::kValue01>(char* data); /* Implemented somewhere else */
MyClass <> void MyClass::processData<MyEnum::kValue02>(char* data); /* Implemented somewhere else */
I would like to remove the switch because of many reasons. Instead of it I would need something like: processData<runtime-decltype(currentEnumValue)>(data);
I know about typeid and about not mixing compile time and runtime together... but despite this, I would like to find some solution anyway, preferably excluding macros.
This class makes a jump table for a given Enum up to a certain count size based off constructing some template and invoking it with the supplied args. It assumes the enum values start at 0, and go to Count-1.
template<class Enum, Enum Count, template<Enum>class Z>
struct magic_switch {
// return value of a call to magic_switch(Args...)
template<class...Args>
using R = std::result_of_t<Z<Enum(0)>(Args...)>;
// A function pointer for a jump table:
template<class...Args>
using F = R<Args...>(*)(Args&&...);
// Produces a single function pointer for index I and args Args...
template<size_t I, class...Args>
F<Args...> f() const {
using ret = R<Args...>;
return +[](Args&&...args)->ret{
using Invoke=Z<Enum(I)>;
return Invoke{}(std::forward<Args>(args)...);
};
}
// builds a jump table:
template<class...Args, size_t...Is>
std::array<F<Args...>,size_t(Count)>
table( std::index_sequence<Is...> ) const {
return {{
f<Is, Args...>()...
}};
}
template<class...Args>
R<Args...> operator()(Enum n, Args&&...args) {
// a static jump table for this case of Args...:
static auto jump=table<Args...>(std::make_index_sequence<size_t(Count)>{});
// Look up the nth entry in the jump table, and invoke it:
return jump[size_t(n)](std::forward<Args>(args)...);
}
};
then if you have an enum:
enum class abc_enum { a, b, c, count };
and a function object template:
template<abc_enum e>
struct stuff {
void operator()() const {
std::cout << (int)e << '\n';
}
};
you can dispatch:
magic_switch<abc_enum, abc_enum::count, stuff>{}(abc_enum::b);
in any case, within the template stuff, you get the enum value as a compile time constant. You call it with a run time constant.
Overhead should be similar to a switch statement, or a vtable call, depending on what the compiler does optimization wise.
live example.
Note that setting Enum to std::size_t is valid.
In C++11 you need make_index_sequence and index_sequence:
template<size_t...>
struct index_sequence {};
namespace details {
template<size_t Count, size_t...szs>
struct sequence_maker : sequence_maker<Count-1, Count-1, szs...> {};
template<size_t...szs>
struct sequence_maker<0,szs...> {
using type = index_sequence<szs...>;
};
}
template<size_t Count>
using make_index_sequence=typename details::sequence_maker<Count>::type;
template<class...Ts>
using index_sequence_for=make_index_sequence<sizeof...(Ts)>;
and this alias:
template<class Sig>
using result_of_t=typename std::result_of<Sig>::type;
then strip std:: off their use in the above code.
live example.
Boost variant does something like what you are doing. It lets you replace switch statements with a template based contruct that can check that all cases are defined at compile-time, but then select one at run-time.
e.g.,
using namespace boost;
using Data = variant<int, double>;
struct ProcessDataFn: static_visitor<void>
{
char* data;
void operator()(int& i)
{
// do something with data
}
void operator()(double& d)
{
// do something else
}
};
void processData(char* data, Data& dataOut)
{
apply_visitor(ProcessDataFn{data}, dataOut);
}
void example(char * data)
{
Data d = 0;
processData(data, d); // calls first overload of operator()
Data d = 0.0;
processData(data, d); // calls second overload
}
To expand on my comment, ideally we'd have compile-time reflection and be able to write a generic dispatch function. In its absence, one option is to unfortunately use macros to do that for you using the X Macro pattern:
#define LIST_OF_CASES \
X_ENUM(kValue0) \
X_ENUM(kValue1) \
X_ENUM(kValue2)
enum MyEnum
{
# define X_ENUM(a) a,
LIST_OF_CASES
# undef X_ENUM
};
void dispatch(MyEnum val)
{
switch (val)
{
# define X_ENUM(a) case a: processData<a>(); break;
LIST_OF_CASES
# undef X_ENUM
default:
// something's really wrong here - can't miss cases using this pattern
}
}
One benefit of this approach is that it scales to large numbers of enumerations, it gets really hard to omit a case, and that you can attach extra information by using a multi-argument X_ENUM macro.
I know you said you'd like to avoid macros, but the alternative without virtual functions then is to have some sort of a static table of function pointers indexed by the enum, and that is just a virtual function in disguise (with admittedly lower overhead, but still suffering the cost of an indirect function call).