0

I'm trying to make a library for a sort of command framework. I want the syntax for adding a command to be like Command<"name">([](ArgType1 arg1, ArgType2 doThing){}), where the arguments to the lambda will all be subclasses of a base argument class that provides methods for casting to the actual type from the string of the command. There is meant to be any number of arguments of whichever types. Getting the name of the command works fine, however the templating for the dynamic lambda args does not work.

I've tried structuring the template in other ways and it's always caused more errors without fixing that one.

Apologies ahead of time for any tears caused by this code/idea.

Code:

#include <iostream>
#include <functional>

class Context{};

template<size_t N>
struct TemplateString {
    constexpr TemplateString(const char (&str)[N]) {
        std::copy_n(str, N, value);
    }
    
    char value[N];
};

template<TemplateString name, typename... Args, std::invocable<Context, Args...> TLambda>
void command(TLambda function) {
   std::vector types = { typeid(Args).name()... };
   constexpr auto cname = name.value;
   std::cout << cname << std::endl;
}


int main() {
    command<"ping">([](Context ctx, int i){std::cout << i << std::endl;});
    return 0;
}

Error:

<source>:24:5: error: no matching function for call to 'command'
    command<"ping">([](Context ctx, int i){std::cout << i << std::endl;});
    ^~~~~~~~~~~~~~~
<source>:16:6: note: candidate template ignored: constraints not satisfied [with name = {{112, 105, 110, 103, 0}}, Args = <>, TLambda = (lambda at <source>:24:21)]
void command(TLambda function) {
     ^
<source>:15:54: note: because 'std::invocable<(lambda at <source>:24:21), Context>' evaluated to false
template<TemplateString name, typename... Args, std::invocable<Context, Args...> TLambda>
                                                     ^
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/concepts:338:25: note: because 'is_invocable_v<(lambda at <source>:24:21), Context>' evaluated to false
    concept invocable = is_invocable_v<_Fn, _Args...>;
                        ^
1 error generated.
Compiler returned: 1
  • You have to explicitly specify template parameters for `Args...` such as `command<"ping", int>([](Context ctx, int i){ ... })`, because there is no way for the compiler to deduce what type `Args...` are. – 康桓瑋 Apr 14 '23 at 17:10

1 Answers1

0

The solution is to make helper functions so the compiler is capable of deducing the lambda arguments. The std::invocable unfortunately won't help with this.

The solution I came up with is this:

#include <iostream>
#include <functional>

class Context{};

template<size_t N>
struct TemplateString {
    constexpr TemplateString(const char (&str)[N]) {
        std::copy_n(str, N, value);
    }
    
    char value[N];
};

template<TemplateString name, typename F, typename... Args>
void command(void(F::*) (Context, Args...) const) {
   std::vector types = { typeid(Args).name()... };
   constexpr auto cname = name.value;
   std::cout << cname << std::endl;
}

template<TemplateString name, typename F>
void command(F function) {
    command<name>(&F::operator());
}

int main() {
    command<"ping">([](Context ctx, int i) {std::cout << i << std::endl;});
}

It is based on this answer. And can be improved to support all types of functions (global functions and mutable lambdas are left, maybe some others as well)

user16217248
  • 3,119
  • 19
  • 19
  • 37
muji4ok
  • 16