I can't and won't bore you with the details, but my system has these specific requirements:
- Actions must be called and registered at runtime.
- Each Action can have multiple targets, and these targets should be processed in a loop.
- Actions may accept special arguments, but any potentially available action can be invoked through a generalized
Callfunction at runtime. - If incorrect argument count or types are passed to the Call function, it should return false instead of causing a runtime crash.
Conceptually, this is quite simple: We register an action, and then we call the action by its name and with arguments of given type. This would mean the call can be done from anywhere in code at runtime.
Smallest reproducible example: https://onlinegdb.com/qNErEq_85
Currently, an example Action looks like this:
bool DoAction(const int Target, ...)
{
va_list args;
va_start(args, Target); // Initialize the va_list with the last named parameter
// Assume the first argument after Targets is a std::string
const char* stringArg = va_arg(args, const char*); // Get the string argument
std::string myString(stringArg); // Convert it to std::string
va_end(args); // Clean up the va_list
std::cout << myString << " \n";
return true;
}
This is how the Call() function looks like:
bool Call(const int Target, ...) override
{
std::cout << "called";
va_list args;
va_start(args, Target);
bool returnValue = (Instance->*ActionFunction)(Target, args);
va_end(args);
return returnValue;
}
The critical issue with this implementation is that va_list consumes the arguments, so they cannot be nested.
I have tried replacing virtual with a template, but the std::map<std::string, Action*> RegisteredActions; still requires derivation. dynamic_cast into the derived type is however not possible, because Call() should not take any templates or arguments other than the function name and function arguments (otherwise there would not be much point to using this system).
Solutions I have not tried:
- Hard-coding specific argument types. This should work, but then there wouldn't be much of a point to using this system.
- Make
Call()a macro. This could work, but I haven't figured out how. - Not doing this.
There are no questions about this in the site, it's mostly about asking about specific compilation errors. My question is how to implement a specific type of runtime functionality.
How do I create nested variadic functions?
DoActiontakes an ellipsis instead of template variadics ?std::string, and then you immediately assume it's really aconst char *. Which is used at the call site? They're not interchangeable just because one is constructible from the other...