函数的包装避免重复

wrap of functions avoid duplicating

本文关键字:包装 函数      更新时间:2023-10-16

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

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

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

模板化函数如何:

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

然后用CallWrapper(libFun1);调用

/edit 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);

/edit 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 ()后面的MISSING分号。这样,您就可以在任何允许像if (...) LIBFUN1(); else LIBFUN2();

这样的语句的上下文中天真地使用LIBFUN1()等等。

/edit 3:不使用#define s,静态内联函数也可以完成这项工作:

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();

/edit 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)将被命名为

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

一种方法是使用宏,如:
#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); });

或者如果你想更极端,你可以有一个可变的模板,就像有人建议的那样

从给定的代码中,您可以简单地编写

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

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

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

,因此前面的代码变成:

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

我有几种方法可以做到这一点:

  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");

如果你愿意,你也可以让它更复杂,添加try, catch或任何你想要的混合