如果Template Argument无效,请在Compile-Time检查

时间:2012-03-08 21:48:37

标签: c++ c++11 variadic-templates compile-time typechecking

我正在尝试包装Windows API函数以在我选择时检查错误。正如我在之前的SO问题中发现的那样,我可以使用模板函数来调用API函数,然后调用GetLastError()来检索它可能设置的任何错误。然后我可以将此错误传递给我的Error课程,让我知道它。

以下是模板功能的代码:

template<typename TRet, typename... TArgs>
TRet Wrap(TRet(WINAPI *api)(TArgs...), TArgs... args)
{
    TRet ret = api(args...);
    //check for errors
    return ret;
}

使用此我可以使用以下代码

int WINAPI someFunc (int param1, BOOL param2); //body not accessible

int main()
{
    int ret = someFunc (5, true); //works normally
    int ret2 = Wrap (someFunc, 5, true); //same as above, but I'll get a message if there's an error
}

这非常有用。但是,有一个可能的问题。采取功能

void WINAPI someFunc();

将其转换为模板函数时,它看起来如下:

void Wrap(void(WINAPI *api)())
{
    void ret = api(); //<-- ahem! Can't declare a variable of type void...
    //check for errors
    return ret; //<-- Can't return a value for void either
}

为了解决这个问题,我尝试创建一个模板版本,用TRet替换void。不幸的是,这实际上只会导致使用哪一个模糊不清。

除此之外,我尝试使用

if (strcmp (typeid (TRet).name(), "v") != 0) //typeid(void).name() == "v"
{
    //do stuff with variable to return
}

else
{
    //do stuff without returning anything
}

但是,typeid是一个运行时比较,因此代码仍然无法编译,因为尝试声明一个void变量,即使它永远不会。

接下来,我尝试使用std::is_same <TRet, void>::value而不是typeid,但发现它也是运行时比较。

此时,我不知道下一步该尝试什么。有没有可能让编译器相信我知道我在做什么会运行正常?我不介意在Wrap附加一个额外的参数,但我也无法从中得到任何结论。

我使用Code :: Blocks与GNU G ++ 4.6.1,Windows XP以及Windows 7.感谢您提供任何帮助,即使它告诉我我最终也不会使用{{ 1}}用于返回void的函数。

3 个答案:

答案 0 :(得分:8)

您可以使用辅助类来微调专业化:

template <typename F>
struct wrapper
{};

template <typename Res, typename... Args>
struct wrapper<Res(Args...)>
{
    static Res wrap(Res (WINAPI *f)(Args...), Args&& args...)
    {
        Res r = f(std::forward<Args>(args)...);
        // Blah blah
        return r;
    }
};

template <typename... Args>
struct wrapper<void(Args...)>
{
    static void wrap(void (WINAPI *f)(Args...), Args&& args...)
    {
        f(std::forward<Args>(args)...);
        // Blah blah
    }
};

现在,您可以编写包装器:

template <typename Res, typename... Args>
Res Wrap(Res (WINAPI *f)(Args...), Args&& args...)
{
    return wrapper<Res(Args...)>::wrap(f, std::forward<Args>(args)...);
}

请注意,即使Resvoid,它仍然有效。您可以return在返回void的函数中返回void的表达式。{/ p>

推导出正确的类型,如Wrap(someFunc, 5, true)中所示,即使对于返回void的函数也是如此。

答案 1 :(得分:2)

  

为了解决这个问题,我尝试创建一个模板版本,用虚拟替换TRet。不幸的是,这实际上只会导致使用哪一个模糊不清。

应该工作,我相信,因为voidTRet更专业,但正如你所指出的那样。我可能会遗漏某些内容,但无论如何,这都无关紧要,您可以阻止选择TRet重载。

template<typename TFun, typename... TArgs>
auto Wrap(TFun api, TArgs&&... args) ->
typename std::enable_if<
    !std::is_void<typename std::result_of<TFun(TArgs...)>::type>::value,
    typename std::result_of<TFun(TArgs...)>::type
>::type
{
    auto result = api(std::forward<TArgs&&>(args)...);
    return result;
}

template<typename TFun, typename... TArgs>
auto Wrap(TFun api, TArgs&&... args) ->
typename std::enable_if<
    std::is_void<typename std::result_of<TFun(TArgs...)>::type>::value,
    typename std::result_of<TFun(TArgs...)>::type
>::type
{
    api(std::forward<TArgs&&>(args)...);
}

void WINAPI f1()
{
}

void WINAPI f2(double)
{
}

int WINAPI f3()
{
    return 0;
}

int WINAPI f4(double)
{
    return 0;
}

int main() {
    Wrap(f1);
    Wrap(f2, 0);
    return Wrap(f3) * Wrap(f4, 0);
}

更新:已调整为允许从参数类型转换为参数类型。

答案 2 :(得分:1)

从评论到答案的推广我理解为什么将返回类型专门化为void不起作用(不能消除返回类型的歧义)但是专注于void并添加一个额外的参数应该有用,什么发生了什么?您可能必须使用显式类型进行调用。