【问题标题】:Wrapping any API Function包装任何 API 函数
【发布时间】:2012-02-28 21:00:55
【问题描述】:

我正在包装 Windows API,我希望使错误检查易于使用且有用。目前,我有一个全局错误对象,带有一个函数set 来处理一个新错误。 set 函数接受四个参数: bool Error::set (const int code, const char * file, const char * const function, const int line); 该函数使用文件、函数和行参数以格式良好的消息显示它们。

为了简化错误设置,有一个宏#define setError() error.set (GetLastError(), __FILE__, __FUNCTION__, __LINE__); 这样我就可以随时使用setError() 来响应API 函数设置的错误,方法是在我调用该API 后添加它功能。

不幸的是,这会导致代码如下所示:

SomeAPIFunction();
setError();
AnotherAPIFunction();
setError();

构造函数也有问题:

MyClass:MyClass()
    : a (SomeAPIFunction), b (AnotherAPIFunction)
{
    setError(); //what if both functions set an error?
}

如您所见,通过使用成员初始化器语法,我实际上是在限制自己。

解决此问题的一种方法是包装每个 API 函数:

int someAPIFunction()
{
    int ret = SomeAPIFunction();
    setError();
    return ret;
}

错误消息的function 部分会告诉我是哪个函数导致了错误。当然,这必须是最糟糕的处理方式。

解决方案似乎是使用可变参数模板。问题是,我不知道我应该怎么做才能让他们为此工作。我想最终的代码看起来像以下之一:

wrap<int, SomeAPIFunction (5)>();
wrap<int, SomeAPIFunction, 5>();
wrap<int, SomeAPIFunction> (5);

我已经阅读了有关开始可变参数模板的内容,但它们都让我对如何设置这样的东西一无所知。谁能指出我正确的方向?

我在a similar question上找到了以下内容:

#include <iostream>

template<void f(void)>
struct Wrap {
   void operator()() const {
      std::cout << "Pre call hook" << std::endl;
      f();
   }
};

namespace {
   void test_func() {
      std::cout << "Real function" << std::endl;
   }
}

const Wrap<&test_func> wrapped_test_func = {};

int main() {
   wrapped_test_func();
   return 0;
}

受访者指出,可变参数模板是使其足够通用的必要条件。这是一个开始,但我对此事的任何帮助感到迷茫和感激。

【问题讨论】:

  • 首先,并非所有 API 函数都使用 GetLastError 机制。然后,更麻烦的是,只有在那些 API 函数报告失败时调用 GetLastError 才有效。所以你问题中的代码注定要失败。
  • 大多数 Win32 API 函数设置LastError仅在实际发生错误时。在成功的情况下,他们不理会LastError 值。这也有例外; Win32 API 文档准确地描述了每个函数的作用。
  • 我知道这一点。如果GetLastError 返回 0 (ERROR_SUCCESS),它会被忽略。我知道有些部分也使用其他机制,但我还没有为它们添加任何支持。随着我继续扩展,我将添加必要的内容。当我完成处理它以准备下一个 API 函数调用时调用 SetLastError (0); 也是可能的。
  • 你用的是什么编译器? (MSVC 不支持可变参数模板)。
  • 请注意,您原来的“包装器”比需要为每个函数编写它有更多的问题。对于初学者,__LINE____FILE____FUNCTION__ 参数是无用的。

标签: c++ function templates wrapper variadic


【解决方案1】:

我认为您可以使用以下语法使其工作:

wrap(&SomeAPIFunction, arg1, arg2);

关键是让编译器使用类型推导来确定模板类型参数,因为它们很快就会变得很乱。

代码应该类似于:

template<typename TRet, typename... TArgs>
TRet wrap( TRet(WINAPI *api)(TArgs...), TArgs... args )
{
    return api(args...);
}

当然,您会想要使用宏来隐藏函数地址运算符,使用字符串化来存储函数名,同时存储文件名和行号,将所有这些传递给实际的可变参数函数。为此,您将需要可变参数宏。事实上,您可以只使用可变参数宏而不使用模板来完成所有这些工作吗?

【讨论】:

  • 这看起来应该可以快速浏览,并且也易于使用:) 我会尝试一下。
  • 我试过了,它似乎与std::forward()(int&amp;) 或任何你没有被声明的泵有问题,只是其他形式。我试着玩了一会儿,但什么也没得到。我认为编译器实际上也不需要 &,我永远不必将它放入函数参数中。关于可变参数宏,听起来它会起作用。在所有更令人困惑的模板匆忙中,我忘记了那个选项。
  • @chris:你说得对,调用 C 兼容 API 时不需要完美转发。所以只需删除std::forward 和右值引用。
  • 这很好,谢谢。现在唯一的问题是错误消息将我引导至wrap 函数。我尝试放入一个可变参数宏来调用具有正确位置的wrap 函数并让它手动设置错误,但现在编译器抱怨__VA_ARGS__ 没有被定义。其他 C++0x 东西都可以,我需要升级其他东西吗? (GNU GCC + 代码块)
  • @chris:我不知道你在宏中缺少什么。你读过gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html 吗?
猜你喜欢
  • 2020-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多