I can't and won't bore you with the details, but my system has these specific requirements:
Call
function at runtime.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:
Call()
a macro. This could work, but I haven't figured out how.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?
You might get rid of C variadic and use types from C++:
std::map<std::string, std::function<bool(int, std::vector<std::any>)>> RegisteredActions{
{ "FooAction", &FooAction},
// ...
};
RegisteredActions["BarAction"] = &BarAction;
Function looks like:
bool FooAction(int target, std::vector<std::any> v) {
if (v.size() != 1) { // check size
return false;
}
if (auto* s = std::any_cast<std::string>(&v[0])) { // check type
std::cout << target << ": " << *s << std::endl;
return true;
}
return false;
}
Note:
Care with type and type conversion: const char*
is not std::string
assert(FooAction(42, "const char*") == false);
assert(FooAction(42, "std::string"s) == true);
Call look like:
int target = 42;
std::vector<std::any> args = { "Hello"s };
const auto it = RegisteredActions.find(Name);
if (it == RegisteredActions.end())
{
// ERROR: No action of name has been registered
throw std::runtime_error("Action not found");
}
auto res = it->second(target, args);