c++ - 将给定的 vararg 参数传递给另一个函数的合适方法是什么?

标签 c++ visual-c++ event-handling variadic-functions

我已经开始使用 C++ 开发 Hook /事件系统。该系统应该处理应用程序其他部分通知的各种事件。

我遇到的问题是我希望它运行的方式。 通常,我希望它是这样的,您可以使用要传递的所有参数调用特定函数,然后该函数处理调用该特定事件的所有已注册 Hook ,将它们传递给参数,检索它们的结果值并返回它给原来的来电者。

通常,它应该是这样的:

CHookReturn* bInitializationStatus = Hook::Run("Initialize", gGame);
CHookReturn* bThinkSuccessful = Hook::Run("Think");



但是,我遇到了一个问题。 我以这样的方式设置它,即 Hook 命名空间中的 Run 函数,调用 CHookData_t 结构的 Run 函数,需要传递可变参数。我找不到任何其他方法。结果是这样的:

union CHookReturn
{
    const char* m_pszValue;
    int m_iValue;
    float m_flValue;
    double m_dlValue;
    bool m_bValue;
};

struct CHookData_t
{
    virtual void Run(CHookReturn* ret, ...) = 0;
};

namespace Hook
{
    std::unordered_map<const char*, std::unordered_map<const char*, CHookData_t*>> umHookList;

    bool Add(const char*, const char*, CHookData_t*);
    bool Exists(const char*, const char*);
    bool Remove(const char*, const char*);
    int Count(const char*);
    CHookReturn* Run(const char*, ...);
};

Hook::Run函数的CPP文件段:

CHookReturn* Hook::Run(const char* eventName, ...)
{
    // FIXME: Look into alternative execution.
    // This code seems more like a workaround
    // than what I originally wanted it to be.

    int count = Hook::Count(eventName);
    CHookReturn* returnValues = new CHookReturn[count];
    int c = 0;

    unordered_map<const char*, CHookData_t*>::iterator itr;
    unordered_map<const char*, CHookData_t*> res;
    res = umHookList.at(eventName);

    va_list valist;
    void* args;
    va_copy(args, valist);

    for (itr = res.begin(); itr != res.end(); itr++)
    {
        CHookReturn returnData;
        itr->second->Run(&returnData, args);

        returnValues[c] = returnData;
        ++c;
    }

    return returnValues;
}

上面的代码提出了两个警告,让我怀疑以这种方式执行它是否是个好主意,以及是否有任何我应该研究的替代方案。

我收到的警告是:
警告 C6001 使用未初始化的内存“valist”。
写入“returnValues”时警告 C6386 缓冲区溢出:可写大小为“count*8”字节,但可能写入“16”字节。

有更好的方法吗?

最佳答案

使用 va_list 修复您的代码:

struct CHookData_t
{
    virtual ~CHookData_t() {}
    virtual void Run(CHookReturn* ret, va_list arg) = 0;
};

namespace Hook
{
    using HooksList = std::unordered_map<
         std::string, 
         std::unordered_map<std::string, std::unique_ptr<CHookData_t>>;

    HooksList umHookList;
    ...
}

std::vector<CHookReturn> Hook::Run(const std::string& eventName, ....)
{
    va_list valist;
    va_start(valist, eventName);

    auto result = Hook::RunVarg(eventName, valist);

    va_end(valist);

    return result;
}

std::vector<CHookReturn> Hook::RunVarg(const std::string& eventName, va_list arg)
{
    int count = Hook::Count(eventName);
    std::vector<CHookReturn> returnValues(count);

    size_t c = 0;
    for (auto& item : umHookList.at(eventName))
    {
        va_list arg_copy;
        va_copy(arg_copy, arg);
        item.second->Run(&returnValues[c], arg_copy);
        va_end(arg_copy);
        ++c;
    }

    return returnValues;
}

我不知道 Hook::Run 的参数是什么,va_list 指向什么,所以我不能提供很好的 C++ 解决方案。

请注意,va_copy 在循环内部是必需的,因为一些编译器(不记得是哪个,可能是 msvc)va_list 表现得像一个指针,从中读取参数会对每次迭代都有影响。在其他编译器上,va_list 的行为就像一个值,而 va_copy 不会改变任何东西。

offtopic:您的代码太多了,您不应该使用 const char* 而应该使用 std::string std::string_view 如果你使用的是 C++17,而不是 va_args 最好使用可变参数模板或 std::initializer_list,避免原始指针支持 std::unique_ptrstd::shared_ptr。我对您的代码进行了一些调整以涵盖这一点。
另外 Hook 不应该是一个 namespace,从它包含的函数和变量来看它应该是一个类,所以你也应该修复它。

关于c++ - 将给定的 vararg 参数传递给另一个函数的合适方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56869969/

相关文章:

c - 为什么 GCC 内联汇编器需要破坏信息,而 MSVC 不需要

c++ - 窗口事件之间

c++ - 使用 qmake 执行 shell 命令

c++ - llvm - 如何用我的语言实现打印功能?

c++ - 如何将进度控件与一组在 Visual C++ 的对话框中加载的数据同步

javascript - 如果我单击 document.body.addEventListener 中的任何子级、孙级(以及更深层次),如何单击父级标记

javascript - 跨浏览器捕获回车键,我的解决方案不起作用

c++ - 带有 xcode 错误 : Undefined symbols for architecture x86_64: 的 OpenCV

c++ - 静态库中的隐藏符号

c++ - 谁能给我_dupenv_s的示例代码?