【问题标题】:Specify function return type as template parameter in c++在 C++ 中将函数返回类型指定为模板参数
【发布时间】:2020-10-20 01:25:21
【问题描述】:

我正在尝试为我的函数提供其模板参数的返回类型(以改进类型检查),这是一个函数引用。 到目前为止,这是我尝试过的:

#include 

模板 
decltype(auto) proxyFunction(LPCSTR dllPath, LPCSTR functionName){
    自动 funcType = decltype(func);

    funcType funcPtr = (funcType) GetProcAddress(LoadLibraryA(dllPath), functionName);

    如果(函数指针)
        std::cout ("C:\\Windows\\System32\\Version.dll", "GetFileVersionInfoA");
    getFileVersion(lptstrFilename, dwHandle, dwLen, lpData);
}

但是,关于proxyFunction,我收到以下编译时错误:

没有函数模板的实例与参数列表参数类型匹配:(const char [32], const char [20])

我不太确定如何处理这个错误,因为它相当模糊。所以我想知道是否有人可以解释我的 sn-p 中的问题是什么?

附:我正在使用带有 C++ 17 标准的 MS VS 2019,以防万一。

【问题讨论】:

  • typename funcdecltype(func) 不能很好地协同工作。 func 应该是什么?类型还是值?
  • @max66 func应该是一个函数的类型,如GetFileVersionInfoProxy所示。
  • 但是GetFileVersionInfoProxy本身并不是一个类型,而是一个函数。模板参数可能应该是auto func
  • 所以,你应该直接使用func,而不是funcTypefunc funcPtr = (func) GetProcAddress(LoadLibraryA(dllPath), functionName);
  • @arslancharyev31:与一切无关,考虑将auto getFileVersion 设为static 局部变量,这样GetProcAddress 只会被调用一次,而不是每次都调用

标签: c++ function templates


【解决方案1】:

你的func模板参数已经是你要返回的类型了,直接用就行了,不用decltype(auto)。而您对auto funcType = decltype(func); 的使用完全是错误的。

试试这个:

template <typename funcType>
funcType proxyFunction(LPCSTR dllPath, LPCSTR functionName)
{
    funcType funcPtr = (funcType) GetProcAddress(LoadLibraryA(dllPath), functionName);

    if (funcPtr)
        std::cout << "Proxy success" << std::endl;
    else
        std::cout << "Proxy fail" << std::endl;
    
    return funcPtr;
}

BOOL GetFileVersionInfoProxy(LPCSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
{
    using GetFileVersionInfoA_FuncType = BOOL (WINAPI *)(LPCSTR, DWORD, DWORD, LPVOID);

    auto getFileVersion = proxyFunction<GetFileVersionInfoA_FuncType>("C:\\Windows\\System32\\Version.dll", "GetFileVersionInfoA");
    if (getFileVersion)
        return getFileVersion(lptstrFilename, dwHandle, dwLen, lpData);

    return FALSE;
}

或者,如果你让编译器为你推导出模板参数,你可以省略传递它:

template <typename funcType>
bool proxyFunction(LPCSTR dllPath, LPCSTR functionName, funcType &funcPtr)
{
    funcPtr = (funcType) GetProcAddress(LoadLibraryA(dllPath), functionName);

    if (funcPtr)
        std::cout << "Proxy success" << std::endl;
    else
        std::cout << "Proxy fail" << std::endl;
    
    return (funcPtr != nullptr);
}

BOOL GetFileVersionInfoProxy(LPCSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
{
    using GetFileVersionInfoA_FuncType = BOOL (WINAPI *)(LPCSTR, DWORD, DWORD, LPVOID);

    GetFileVersionInfoA_FuncType getFileVersion;
    if (proxyFunction("C:\\Windows\\System32\\Version.dll", "GetFileVersionInfoA", getFileVersion))
        return getFileVersion(lptstrFilename, dwHandle, dwLen, lpData);

    return FALSE;
}

更新:根据@MooingDuck 的评论,看起来您实际上是在尝试将代理函数传递给模板,并让它推断出必要的参数和返回类型以用于 DLL 函数。如果是这样,请尝试更多类似的方法:

template <typename RetType, typename... ArgTypes>
struct proxyTraits
{
    using funcType = RetType (WINAPI *)(ArgTypes...);
};

template <typename RetType, typename... ArgTypes>
auto proxyFunction(
    LPCSTR dllPath,
    LPCSTR functionName,
    RetType (*proxy)(ArgTypes...))
{
    using funcType = typename proxyTraits<RetType, ArgTypes...>::funcType;
    funcType funcPtr = (funcType) GetProcAddress(LoadLibraryA(dllPath), functionName);

    if (funcPtr)
        std::cout << "Proxy success" << std::endl;
    else
        std::cout << "Proxy fail" << std::endl;
    
    return funcPtr;
}

BOOL GetFileVersionInfoProxy(LPCSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
{
    auto getFileVersion = proxyFunction("C:\\Windows\\System32\\Version.dll", "GetFileVersionInfoA", &GetFileVersionInfoProxy);
    if (getFileVersion)
        return getFileVersion(lptstrFilename, dwHandle, dwLen, lpData);

    return FALSE;
}

Live Demo

【讨论】:

  • 我认为 OP 正试图将 GetFileVersionInfoProxy 本身传递给 proxyFunction 以推断函数签名,这可以通过 proxyFunction&lt;decltype(GetFileVersionInfoProxy)&gt;(...) 实现,然后你不需要 @987654331 @ 内部。
  • 另外,我认为他的问题是LPCSTR传递的是宽字符串,而"C:\\Windows\\System32\\Version.dll"是窄字符串。
  • @MooingDuck 不正确。此代码中没有任何内容使用宽字符串。一切都在使用窄字符串。 LPCSTR 仅是const char*,在此代码中没有使用TCHARL 前缀。也许您正在考虑 LPCTSTR 而不是 (const TCHAR*)。
  • @MooingDuck 关于将代理本身传递给模板以推断函数指针类型的要点。但是,这不会按原样工作,因为 GetFileVersionInfoProxy() 没有像 GetFileVersionInfoA() 那样使用 __stdcall 调用约定。尽管可以使用模板参数来解决该问题。我已经更新了我的答案以显示这一点。
  • 谢谢。更新后的答案似乎达到了我的期望,尽管方式有点不同。但是,我很难理解这里发生了什么。 proxyTraits 的目的是什么?所有这些using 声明是怎么回事?又怎么会在不指定模板参数的情况下调用proxyFunction?如果@RemyLebeau 或其他任何人可以出于教育目的扩展答案,我将不胜感激。
【解决方案2】:

所以事实证明std::function 实现了我正在寻找的那种行为。

例如,如果我的虚拟 DLL 函数定义如下:

#include 

#define DllExport extern "C" __declspec( dllexport )

DllExport int SQUARE(int number){
    返回号码 * 号码;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){
    返回真;
}

然后我将它导入如下:

#include 
#include 
#include 

HMODULE dummyHandle = nullptr;

模板
std::function proxyFunction(HMODULE hModule, LPCSTR functionName){
    auto funcPtr = GetProcAddress(hModule, functionName);
    std::cout (reinterpret_cast(funcPtr));
}

整数平方(整数){
    自动代理 = proxyFunction(dummyHandle, __FUNCTION__);
    返回代理?代理(号码):-1;
}

诠释主要(){
    dummyHandle = LoadLibraryA("C:\\Absolute\\Path\\To\\Dummy.dll");
    如果(虚拟句柄)
        std::cout 

我喜欢这种方法,因为它无需定义辅助结构就可以进行基本的静态类型检查。但是,我仍然认为还有改进的余地。每次调用proxyFunction 时,我们都必须将函数包装到decltype(...) 中。有谁知道如何以简单的方式在proxyFunction 中隐藏该位?

【讨论】:

  • 在代理函数和目标函数使用不同的调用约定的情况下,将decltype 用于signature 模板参数将不起作用,就像您原来的问题一样(GetFileVersionInfoA 使用__stdcall,但 GetFileVersionInfoProxy 使用 __cdecl)。我在写答案时遇到了同样的问题,这就是为什么我使用帮助器 proxyTraits 结构来解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-06
  • 1970-01-01
  • 1970-01-01
  • 2011-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多