函数包装避免重复

wrap of functions avoid duplicating

我有第三方 C 库。我想在 C++ 中使用它 每个函数 return 错误代码。 当我必须使用它时,我必须编写这样的代码:

int err;
err=libFun1(....);
check(err);
err=libFun2(....);
check(err);

我想包装这个函数并避免代码重复检查。每个库函数都有不同的数字参数。什么是好的设计?

/编辑 4:C++11-Way 使用可变参数模板,灵感来自 Gill Bates 的解决方案:

template <typename T, class ...A> int CallWrapper(T func, A... args) {
    int error = func(args...);
    check(error);
    return error;
}

CallWrapper(libFun1);
CallWrapper(libFun2, 4711);
CallWrapper(libFun3, 42, 23);

/编辑 5:旧的解决方案,从第一个解决方案开始:

#define LIBFUN1() do { \
    int err = libFun1(); \
    check(err); \
} while (0)

#define LIBFUN2() do { \
    int err = libFun2(); \
    check(err); \
} while (0)

LIBFUN1();
LIBFUN2();

#define 放入某个头文件中。请注意 while () 后缺少的分号。这样,您可以在任何上下文中天真地使用 LIBFUN1() 等等,其中允许使用 if (...) LIBFUN1(); else LIBFUN2();

/edit 3: 除了使用 #defines,静态内联函数也可以完成这项工作:

static inline int checked_libFun1() {
    int err = libFun1();
    check(err);
    return err;
}

static inline int checked_libFun2() {
    int err = libFun2();
    check(err);
    return err;
}

checked_libFun1();
checked_libFun2();

/编辑 2:@Myst 建议使用包含要调用的函数名称的可变参数宏。这可能看起来像这样:

#define call_void(name) do { \
    int err = name(); \
    check(err); \
} while (0)

#define call_args(name, ...) do { \
    int err = name(__VA_ARGS__); \
    check(err); \
} while (0)

call_void(libFun1);
call_args(libFun2, 42);

这两个宏是必需的,因为您必须区分不接受任何参数的函数和接受大于一个的任意数量参数的函数。所以,在这里,libFun2(42) 将被称为

模板函数怎么样:

template <typename T>
int CallWrapper(T func)
{
  int error = func();
  check(error);
  return error;
}

然后用CallWrapper(libFun1);调用它。

您可以使用异常并捕获它们。

一种方法是使用如下宏:

#include <exception>

#define THROW_ON_ERROR(libFunc) do{ \
    int err = libFunc(); \
    if(check(err)) \
        throw std::exception(); \
    }while(0); \

您的代码将如下所示:

try
{   
    THROW_ON_ERROR(libFun1);
    THROW_ON_ERROR(libFun2);
}catch(const std::exception& e)
{
//handle...
}

这种方法不是很现代,但可以完成工作。它只是为了传递将错误状态代码约定(这是 C 中的常见方式)转换为异常处理的要点,这是 C++ 中的一种很好的方式(不确定是否常见)。 您也可以使用自己的异常来传递一些数据。 您可以通过调用如下函数来做同样的事情:

#include <functional>

class LibFuncException : public std::exception
{
public:
    explicit LibFuncException(int err) : m_err(err) {}
    int GetError() const { return m_err; }
private:
    int m_err;
};

void ThrowOnError(std::function<int()> libFunc)
{
    int err = libFunc();
    if(check(err)
        throw LibFuncException(err);
}

并且您的代码可以:

try{
    ThrowOnError(libFunc1);
    ThrowOnError(libFunc2);
} catch(const LibFuncException& e)
{
    std::cout << "Error: << e.GetError() << std::endl;
}

编辑:

如果您的库函数有时会接收参数,您可以像这样使用 lambda 调用 ThrowOnError

int x = 10;
const char* str = "Hello World";
ThrowOnError([x, str]() { return libFuncWithArgs(x, str); });

或者如果你想要极端一点,你可以像有人建议的那样使用可变参数模板

我可以看到一些方法:

  1. 模板函数(@GillBates 已涵盖)。
  2. 一个内联函数(可能带有可变参数)。
  3. 简单的宏(@usr 和@ZivS 已涵盖)。
  4. 可变参数宏。

由于您是为 C++ 编写的,我可能会使用模板函数...该选项唯一不利的是函数参数处理(假设库函数接受参数)可能是头疼。

我认为可变宏的 C 方法会更好,还提供编译时解包(相对于运行时解包)。

即(这只是一个例子,我什至没有测试代码)

int checkerr(int err) {
   if(!err)
      return 0;
   // ... handle errors, exit if need be.
}

#define CALL_CLIB(fn_name, ...) checkerr((fn_name)(__VA_ARGS__))

你可以这样使用它:

 // will print "this is only 1 example"
 CALL_CLIB(printf, "this is only %d, %s", 1, "example");

如果需要,您也可以使其更复杂,添加 trycatch 或您想要的任何内容。

根据给定的代码,您可以简单地编写

check(libFun1(/*...*/));
check(libFun2(/*...*/));

可能,您可能希望将每个方法包装成只有一个来自用户端的调用:

void chekedLibFun1(/*...*/) { check(libFun1(/*...*/)); }
void chekedLibFun2(/*...*/) { check(libFun2(/*...*/)); }

因此之前的代码变为:

checkedLibFun1(/*...*/);
checkedLibFun2(/*...*/);