簡體   English   中英

將給定的 vararg 參數傳遞給另一個函數的合適方法是什么?

[英]What is the appropriate way to pass given vararg parameter to another function?

我已經開始在 C++ 中的鈎子/事件系統上工作。 該系統應該處理應用程序其他部分通知的各種事件。

我面臨的問題是我希望它運行的方式。 一般來說,我希望它是這樣你調用一個帶有你希望傳遞的所有參數的特定函數,然后該函數處理調用該特定事件的所有注冊鈎子,將它們傳遞給參數,檢索它們的結果值並返回它給原來的來電者。

一般來說,它應該是這樣的:

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



但是,我遇到了一個問題。 我把它架在這種方式的Run功能,在鈎命名空間,調用CHookData_t結構的運行功能,需要通過對可變參數。 我找不到任何其他方式。 結果是這樣的:

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

上面的代碼提出了兩個警告,這讓我懷疑以這種方式執行它是否是一個好主意,以及是否有任何我應該研究的替代方案。

我收到的警告是:
Warning C6001 Using uninitialized memory 'valist'.
Warning C6386 Buffer overrun while writing to 'returnValues': the writable size is 'count*8' bytes, but '16' bytes might be written.

有一個更好的方法嗎?

使用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 :您的代碼太多 C,如果您使用的是 C++17,則不應使用const char*而是std::stringstd::string_view ,而va_args最好使用可變參數模板或std::initializer_list ,避免使用原始指針,支持std::unique_ptrstd::shared_ptr 我已經稍微調整了您的代碼以涵蓋這一點。
此外Hook不應該是一個namespace ,從它包含的函數和變量的外觀來看,它應該是一個類,所以你也應該修復它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM