用C++模板替换函数调用的C宏



是否可以替换这个预处理器宏:

#define AL_CALL(a) do { a;                              
                        ALenum e = alGetError();        
                        if(e != AL_NO_ERROR)            
                            UtilitySoundNode::printALError(e,__FILE__, __LINE__); 
                       } while(0)

使用C++模板?如果可能的话,这样做有意义吗(优点/缺点-开销/调试)?

注意:基本上,我想知道是否有一种优雅的方法来处理C++中的这种错误处理。

编辑:当然我犯了一个错误一个是一个函数调用。可以猜测,这是一个带有OpenAL函数参数的函数调用。

AL_CALL(someAlFunction(param1, param2))

注意:有人决定编辑这个宏,让它变得更好,但我也更喜欢保留原来的宏。现在是:

#define AL_CALL(a) {a; ALenum e = alGetError();if(e != AL_NO_ERROR)PUtilitySoundNode::printALError(e,__FILE__, __LINE__);}

这里的一个问题似乎是"a"可以是一些任意函数(带参数),它设置alGetError()返回的错误代码。

可以通过使用函子对象将其重写为C++。为了传递参数(以及必要时的对象实例),可以使用std::bindboost::bind(请注意,要绑定引用参数,std::ref/boost::ref是必要的)。

但是,如果您仍然希望__FILE____LINE__传递printError(),则C++模板仍然需要由将这些传递给模板的宏调用。__FILE____LINE__只是由预处理器扩展的,所以没有办法为它们使用宏。

但是宏可能会简单得多,而且大部分工作都可以在C++模板中完成(这有很多优点,例如调试,因为在大多数调试器中,你无法进入宏)。

编辑:添加代码作为示例:

template<typename T>
void ALcallAndCheck(T c, const char *file, size_t line)
{
    c();
    ALenum e = alGetError();
    if(e != AL_NO_ERROR)
        UtilitySoundNode::printALError(e, file, line); 
}
#define AL_CALL(a) ALcallAndCheck(a, __FILE__, __LINE__)

然后,代替

AL_CALL(SomeFunction(2, refAttr));

呼叫将变为:

AL_CALL(std::bind(SomeFunction, 2, std::ref(refAttr)));

编辑2:前一个确实不适用于原始宏允许的表达式。为了同样适用于表达式,可以将宏更改为:

#define AL_CALL(a) ALcallAndCheck([&]{ (a); }, __FILE__, __LINE__)

这将创建一个lambda,用于计算宏中的任何内容。那么即使是std::bind也不是必需的,它可以直接称为:

AL_CALL(SomeFunction(2, refAttr));
AL_CALL(SomeOtherFunction1()+SomeOtherFunction2(8));

否,__FILE____LINE__的使用非常需要预处理器。

请注意,使用模板而不是宏不会产生精确的模拟。问题中定义的宏允许a表示语句和表达式。模板没有那种灵活性。下面定义的模板假定a是非void表达式。

如果调用方不将信息传递给被调用函数,就没有标准的方法可以隐式注入函数调用方的文件名和行号。预处理器宏允许一种方法,使语法看起来是隐式注入,而事实上信息正在传递。

template <typename T>
void AL_CALL (T a, const char *file, int line) {
    ALenum e = alGetError();
    if(e != AL_NO_ERROR)
        UtilitySoundNode::printALError(e, file, line);
}
#define AL_CALL(X) AL_CALL((X), __FILE__, __LINE__)

您可以使用系统特定的工具(例如,CaptureStackBackTrace+SymFromAddrbacktrace+backtrace_symbols)隐式地获得大致相同的信息,但这可能需要调试符号,并且内联函数可能无法产生预期的输出。

template<class A>
void al_call(A&&a){
  ALenum e = a();
  if(e != AL_NO_ERROR)
    UtilitySoundNode::printALError(e,__FILE__, __LINE__);
}

用途:

al_call( [&]{ return bob(); });

代替:

AL_CALL( bob() )

上面的行/文件信息没有用处。

所以

template<class A>
void al_call(A&&a, char const*file, unsigned line){
  ALenum e = a();
  if(e != AL_NO_ERROR)
    UtilitySoundNode::printALError(e,file, line);
}
#define AL_CALL(...) al_call([&]()mutable{return __VA_ARGS__;}, __FILE__, __LINE__)

它几乎是一种替代品。

最新更新