Search code examples
c++c++17constexprtype-erasurestd-function

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?


Solution

  • 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.