Reputation: 881
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)}};
};
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?
Upvotes: 3
Views: 775
Reputation: 5095
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.
Upvotes: 2
Reputation: 75853
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);
}
Upvotes: 0