如何创建嵌套可变参数函数?

问题描述 投票:0回答:1

我不能也不会用细节来烦你,但我的系统有这些特定要求:

  1. 动作必须在运行时调用和注册。
  2. 每个Action可以有多个目标,并且这些目标应该循环处理。
  3. 操作可以接受特殊参数,但任何潜在可用的操作都可以在运行时通过通用
    Call
    函数调用。
  4. 如果将不正确的参数计数或类型传递给 Call 函数,它应该返回 false 而不是导致运行时崩溃。

从概念上讲,这非常简单:我们注册一个操作,然后通过其名称和给定类型的参数调用该操作。这意味着可以在运行时从代码中的任何位置进行调用。

最小的可重现示例:https://onlinegdb.com/qNErEq_85

目前,示例操作如下所示:

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;
}

这就是

Call()
函数的样子:

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;
}

此实现的关键问题是

va_list
消耗了参数,因此它们不能嵌套。

我尝试用模板替换

virtual
,但
std::map<std::string, Action*> RegisteredActions;
仍然需要推导。然而,
dynamic_cast
转换为派生类型是不可能的,因为
Call()
不应该采用除函数名和函数参数之外的任何模板或参数(否则使用这个系统就没有多大意义)。

我没有尝试过的解决方案:

  1. 对特定参数类型进行硬编码。这应该可行,但是使用这个系统就没有多大意义了。
  2. 使
    Call()
    成为宏。这可能行得通,但我还没弄清楚如何。
  3. 不这样做。

网站上没有这方面的问题,主要是询问具体的编译错误。我的问题是如何实现特定类型的运行时功能。

如何创建嵌套可变参数函数?

c++ c++17 polymorphism variadic-templates variadic-functions
1个回答
0
投票

您可以摆脱 C 变量并使用 C++ 中的类型:

std::map<std::string, std::function<bool(int, std::vector<std::any>)>> RegisteredActions{
    { "FooAction", &FooAction},
    // ...
};

RegisteredActions["BarAction"] = &FooAction;

函数看起来像:

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;
}

注意: 注意类型和类型转换:

const char*
不是
std::string

assert(FooAction(42, "const char*") == false);
assert(FooAction(42, "std::string"s) == true);

调用方式如下:

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);

演示

© www.soinside.com 2019 - 2024. All rights reserved.