try / catch宏后这个分号的影响是什么?

时间:2015-11-24 08:54:30

标签: c++

我正在看一些有

的项目
    PREPAID_TRY ;

在别处被定义为

#define PREPAID_TRY try {

我认为通过使用上面的;,程序员基本上取消了try的使用?尝试不再生效,我是对的吗?

但是在它之下有这样的代码 - 我想知道在它之前没有try时如何编译?

PREPAID_CATCH_WITH_LOG(pErrInfo, "(Connect)") ;

定义为

#define PREPAID_CATCH_WITH_LOG(x,t) } \
  catch (const dErrorStruct& ex) {ex.FillErrorStruct(x);Log.LogPrintf(t ## " - %s", x->Description);} 

为什么程序员在第一个;中使用try时会编译上述内容? 上面的catch没有前一个try,我是对的吗?

2 个答案:

答案 0 :(得分:7)

假设你有

#define PREPAID_TRY try {
#define PREPAID_CATCH_WITH_LOG(x,t) } \
  catch (const dErrorStruct& ex) {ex.FillErrorStruct(x);Log.LogPrintf(t ## " - %s", x->Description);} 

你编写的代码如

PREPAID_TRY;
  SomeFunction();
PREPAID_CATCH_WITH_LOG(pErrInfo, "(Connect)");

如果您自己伪装成预编译器,则可以在重新格式化后填写宏定义并检查编译器是否看到以下代码:

try { 
  ; // Empty statement
  SomeFunction();  // Your original code is still part of the try
}  // Note this brace is part of PREPAID_CATCH_WITH_LOG
catch (const dErrorStruct& ex) {
  ex.FillErrorStruct(pErrInfo);
  Log.LogPrintf("(Connect)" " - %s", pErrInfo->Description); // Note compile-time string concatenation
} 

这给出了一个有效的try / catch块。内部有一个空语句(;),但由于已经编写了宏以便获得一组适当的大括号,因此这不是问题。 另请注意,CATCH宏负责处理try构造的右大括号。

实际上PREPAID_TRY之后的分号可以省略,这将消除最终代码中不必要的空语句,但包含它会使行读取更像是一个语句本身而且很可能是编写它的程序员甚至没有注意到他把它放在那里(我有时会发现自己在写了一整天的C ++或C#之后将普通文本中的分号放在了普通文本中。)

当您说“取消试用块”时,您可能会想到的是以下内容:

#define PREPAID_TRY if(not_out_of_memory())
#define PREPAID_CATCH_WITH_LOG(x,t) \
  else { raise_out_of_memory_exception(); } 

请注意,我必须使用try语句替换if,因为首先没有大括号的尝试是非法的(例如,不允许try SomeFunction() catch { ... }),但是对于如果只调用一个函数,if(...) { SomeFunction(); }中的大括号是可选的。这导致

if(not_out_of_memory())
  ;
SomeFunction();
else { raise_out_of_memory_exception(); } 

我将原始SomeFunction代码缩进到左侧以指示编译器如何解析它。这确实会导致编译器错误,因为当编译器到达else时,不再有活动的if语句。正确使用此宏的唯一方法是包括您自己的大括号:

PREPAID_TRY {
  SomeFunction();
  ;;;;;; // Now you can put as many semicolons here as you like.
} 
PREPAID_CATCH_WITH_LOG(...)

答案 1 :(得分:0)

这个宏有点危险/混乱。

首先你应该知道预处理器只是一种宏扩展语言,它只是削减和粘合实际编译器看到的代码。那就是当您编写PREPAID_TRY预处理器更改时,编译器将其视为您编写的try {,如果您在此之后放置一个分号,则编译器会看到try { ; 。这不是问题,因为分号只是一个空语句。

然而,后者有点危险,因为编写PREPAID_CATCH_WITH_LOG(pErrInfo, "(Connect)") ;时编译器会看到

catch (const dErrorStruct& ex) {ex.FillErrorStruct(pErrInfo);Log.LogPrintf("(Connect)" " - %s", x->Description);} ;

同样在PREPAID_TRYPREPAID_CATCH_WITH_LOG之间还有一些看起来很像的陈述。但这一切都扩展为两个语句,一个复合try-catch语句和一个空语句。这意味着你所写的并不完全是它的样子。

现在,如果您认为您“知道”PREPAID_TRYPREPAID_CATCH_WITH_LOG是一个复合语句,您可能会执行以下操作:

if( condition1 )
    PREPAID_TRY;
    some_code();
    PREPAID_CATCH_WITH_LOG;
else
    some_other_code();

它会导致语法错误(因为PREPAID_CATCH_WITH_LOG之后的分号将是一个空的“取消”if语句的代码。代码看起来也很奇怪,但这是另一回事。< / p>