将函数传递给模板

时间:2016-03-17 14:06:46

标签: c++ templates c++11 winapi

我正在为我正在处理的应用程序编写Win32Exception,头文件看起来像这样:

class Win32Exception : std::exception {
public:
    Win32Exception() = default;
    explicit Win32Exception(const std::wstring& message);
    explicit Win32Exception(const std::string& message);
    explicit Win32Exception(const char* message);
    virtual ~Win32Exception() throw() override = default;
    char const* what() const override;
    std::wstring message() const;
};

what函数的实现如下所示:

char const* Win32Exception::what() const
{
    DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER;

    DWORD errorCode = GetLastError();

    DWORD systemLocale = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);

    LPSTR buffer = nullptr;

    DWORD length = FormatMessageA(flags, nullptr, errorCode, systemLocale, (LPSTR)&buffer, 0, nullptr);

    if(buffer != nullptr)
    {
        LocalFree(buffer);
    }
    else if (length == 0)
    {
        buffer = "Cannot get an error message. FormatMessage failed.";
    }

    return buffer;
}

message函数的实现如下所示:

std::wstring Win32Exception::message() const
{
    DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER;

    DWORD errorCode = GetLastError();

    DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);

    LPWSTR buffer = nullptr;

    DWORD length = FormatMessageW(flags, nullptr, errorCode, systemLocale, (LPWSTR)&buffer, 0, nullptr);

    if (buffer != nullptr)
    {
        LocalFree(buffer);
    }
    else if (length == 0)
    {
        buffer = L"Cannot get an error message. FormatMessage failed.";
    }

    return buffer;
}

现在,我想摆脱这种重复,所以我想我可以用模板做一些事情,比如:

    template <typename TResult, DWORD (*formatMessageVersion)(DWORD, LPCVOID, DWORD, DWORD, TResult, DWORD, va_list*)>
TResult formatMessage(DWORD& systemLocale, DWORD& errorCode, TResult& fallbackMessage) const {
    DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER;

    TResult buffer = nullptr;

    DWORD length = formatMessageVersion(flags, nullptr, errorCode, systemLocale, (TResult)&buffer, 0, nullptr);

    if (buffer != nullptr)
    {
        LocalFree(buffer);
    }
    else if (length == 0)
    {
        buffer = fallbackMessage;
    }

    return buffer;
}

在我的一次尝试中,我尝试通过std :: function将函数FormatMessageA/W作为参数传递给formatMessage,但我不确定为什么它不起作用..

我认为我使用它的方式如下:

char const* Win32Exception::what() const
{
    DWORD errorCode = GetLastError();

    DWORD systemLocale = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);

    return formatMessage<LPSTR, FormatMessageA>(systemLocale,
                                                errorCode,
                                                "Cannot get an error message. FormatMessage failed.");
}

std::wstring Win32Exception::message() const
{
    DWORD errorCode = GetLastError();

    DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);

    return formatMessage<LPWSTR, FormatMessageW>(systemLocale,
                                                errorCode,
                                                L"Cannot get an error message. FormatMessage failed.");
}

但是我收到了很多错误:

Severity    Code    Description Project File    Line    Suppression State
Error   C2664   'TResult Yalla::Win32Exception::formatMessage<LPSTR,DWORD FormatMessageA(DWORD,LPCVOID,DWORD,DWORD,LPSTR,DWORD,va_list *)>(DWORD &,DWORD &,TResult &) const': cannot convert argument 3 from 'const char [51]' to 'LPSTR &' RunBox  d:\users\eyal\projects\code\yalla\core\src\runbox\win32\win32-exception.cpp 29  

Severity    Code    Description Project File    Line    Suppression State
Error   C2664   'TResult Yalla::Win32Exception::formatMessage<LPSTR,DWORD FormatMessageA(DWORD,LPCVOID,DWORD,DWORD,LPSTR,DWORD,va_list *)>(DWORD &,DWORD &,TResult &) const': cannot convert argument 3 from 'const char [51]' to 'LPSTR &' RunBox  d:\users\eyal\projects\code\yalla\core\src\runbox\win32\win32-exception.cpp 29  

Severity    Code    Description Project File    Line    Suppression State
Error   C2664   'TResult Yalla::Win32Exception::formatMessage<LPWSTR,DWORD FormatMessageW(DWORD,LPCVOID,DWORD,DWORD,LPWSTR,DWORD,va_list *)>(DWORD &,DWORD &,TResult &) const': cannot convert argument 3 from 'const wchar_t [51]' to 'LPWSTR &'   RunBox  d:\users\eyal\projects\code\yalla\core\src\runbox\win32\win32-exception.cpp 40  

Severity    Code    Description Project File    Line    Suppression State
Error   C2664   'TResult Yalla::Win32Exception::formatMessage<LPWSTR,DWORD FormatMessageW(DWORD,LPCVOID,DWORD,DWORD,LPWSTR,DWORD,va_list *)>(DWORD &,DWORD &,TResult &) const': cannot convert argument 3 from 'const wchar_t [51]' to 'LPWSTR &'   RunBox  d:\users\eyal\projects\code\yalla\core\src\runbox\win32\win32-exception.cpp 40  

Severity    Code    Description Project File    Line    Suppression State
Error (active)      no instance of function template "Yalla::Win32Exception::formatMessage" matches the argument list   RunBox  d:\Users\Eyal\Projects\Code\Yalla\core\src\runbox\win32\win32-exception.cpp 27  

Severity    Code    Description Project File    Line    Suppression State
Error (active)      no instance of function template "Yalla::Win32Exception::formatMessage" matches the argument list   RunBox  d:\Users\Eyal\Projects\Code\Yalla\core\src\runbox\win32\win32-exception.cpp 38  

请记住,我正在重新学习C ++并且多年没有触及它,所以如果你能告诉我如何改进代码或做一些我想要做的更好的方式,我真的很高兴听到它! ;)

更新:感谢所有人,您可以在GitHub找到最终解决方案。

2 个答案:

答案 0 :(得分:2)

不要通过模板解决重复问题。您已经有一个函数message(),它调用FormatMessageW并获取消息文本。这就是你所需要的一切。要实现what(),您需要一个ANSI字符串。所以先调用message()然后再转换为ANSI。

换句话说,首先调用what()来实施message()。然后将std::wstring转换为std::string。然后返回const char* c_str()收益率std::string。您需要将c_str存储在成员字段中,以便what()返回的指针仍然有效。

要执行转换,请参阅此处出现的主题的众多问题之一。例如:How to convert wstring into string?

目前,LocalFree的实现返回一个无效指针,因为它在随后返回的值上调用FormatMessageW。但如果你按照我的建议完成所有代码的话,那就不要太依赖了。

您对0的错误处理是错误的。成功或其他方式由返回值表示,如文档所示。值buffer表示失败。这就是你需要测试的东西。我不能非常强调阅读文档的重要性。我说,我们在这里看到的&gt; 90%的winapi问题在错误处理方面存在错误。

此外,您在释放后再次访问LPWSTR buffer; DWORD length = FormatMessageW(flags, nullptr, errorCode, systemLocale, (LPWSTR)&buffer, 0, nullptr); if (length == 0) return L"Cannot get an error message. FormatMessage failed."; std::wstring retval = buffer; LocalFree(buffer); return retval; 。代码看起来应该更像这样:

SetLastError

您应该允许该类传递错误代码。一些错误代码由API直接返回,例如注册表API。此外,在您设法访问调用GetLastError的代码之前,有时可能会被调用<!-- The Right navigation drawer --> <fragment android:id="@+id/fragment_navigation_filters" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="end" android:name="com.ui.newdesign.FilterNavigationFragment"/> 的C ++运行时混淆。

答案 1 :(得分:1)

此处的错误消息中描述了直接问题:

[...] cannot convert argument 3 from 'const char [51]' to 'LPSTR &' [...]

您正在将字符文字传递给期望非const引用的函数。您不能将非const引用绑定到临时引用,因此简单修复只是将最后一个参数类型从TResult&更改为TResult const&。这应该可以解决你的问题。