重构 'execute and log' 模式

refactor 'execute and log' pattern

我发现自己一次又一次地遵循相同的模式:

if((return_code = doFoo1(...)) != CODE_OK) {
    log("useful log message based on return code");
    // very likely to return an error code. 
}

// continue

if((return_code = doFoo2(...)) != CODE_OK) {
    log("useful log message based on return code");
    // very likely to return an error code. 
}

知道如何避免这种恼人的模式并保持逻辑清晰吗?

有多种方法可以解决这个问题,这是一个基本的方法:

bool CheckAndLog( int code )
{
  if( code == CODE_OK )
    return true;
  log( "<some error based on code>" );
  return false;
}

if( !CheckAndLog( doFoo1(...) ) )
  return;

如果您还需要 return 代码供以后使用,您可以 return 来自 CheckAndLog 的代码,或者将其作为传递引用参数,甚至将 CheckAndLog 变成 class 它存储最新的错误代码,您可以使用(可能有范围的)记录器实例实例化它来使用。

update 如果你想要文件和行信息,你需要 __FILE____LINE__ 宏并避免在各处输入它们简单的宏就足够了:

bool CheckAndLog( int code, const char* file, unsigned line )
{
  if( code == CODE_OK )
    return true;
  std::cerr <<  "file " << file << " line " << line
            << "<errormessage>" << std::endl;
  return false;
}

#define CHECK( what ) (CheckAndLog( what, __FILE__, __LINE__ ))

更进一步,将调用的函数放入其中:因为它可以作为宏参数使用,所以它被预处理器扩展为这样。这意味着如果您键入 CHECK( foobar( 65 ) ),则 what 参数在字面上被视为 foobar( 65 ) 而不是 return 值。这是理想的,因为预处理器也可以 turn that into a string:

bool CheckAndLog( int code, const char* desc, const char* file, unsigned line )
{
  if( code == CODE_OK )
    return true;
  std::cerr <<  desc << " from file " << file << " line " << line
            << "<errormessage>" << std::endl;
  return false;
}

#define STRINGIZE1( x ) #x
#define STRINGIZE( x ) STRINGIZE1( x )
#define CHECK( what ) (CheckAndLog( what, STRINGIZE( what ), __FILE__, __LINE__ ))

我建议不要使用 CHECK 作为宏的名称,因为单元测试库也经常使用它。还要非常小心,不要将宏(无意或有意)重新定义为 #define CHECK( what ) (true) 之类的东西,因为这意味着如果你写 CHECK( foobar( 65 ) ),函数永远不会被调用。