33

我的应用程序使用标准输出之外的另一个输出来记录信息,这就是我编写自己的、 和Log()函数Error()的原因。为了很好地组织事情,我将所有调试内容都包含在一个命名空间中。Panic()Assert()Debug

该函数还提供源文件和行号会更有意义Assert(),这只能使用__LINE__and__FILE__宏。然而,总是必须指定这两个参数是非常不愉快、低效的等等。

所以这就是我的代码的样子:

namespace Debug {
   void Assert (int condition, std::string message, std::string file, int line);
}

我的问题是,是否可以在命名空间内放置一个包含这两个参数的宏Debug?像这样:

namespace Debug {
   void Assert_ (int condition, std::string message, std::string file, int line);
   #define Assert(a,b) Assert_(a, b, __FILE__, __LINE__)
}

// .... Somewhere where I call the function ....
Debug::Assert (some_condition, "Some_condition should be true");

// Output: Assertion failed on line 10 in file test.cpp:
//           Some_condition should be true

这是有效的 C++ 吗?如果没有,有没有办法使这项工作?

4

5 回答 5

51

#define是一个预处理器指令。除了删除注释(这意味着,在编译之前)之外,宏会在其他任何事情之前被替换。因此,在替换宏时,编译器对您的命名空间一无所知。

正如其他人所说,在你的情况下它会很好。但是,这就是您遇到问题的方式:

namespace A
{
 void Assert_ (int condition, std::string message, std::string file, int line)
 {
     std::cout << "A";
 }
   #define Assert(a,b) Assert_(a, b, __FILE__, __LINE__)

}
namespace B
{
 void Assert_ (int condition)
 {
     std::cout << "B";
 }
   #define Assert(a,b) Assert_(a)

}

int main(int argc, char *argv[])
{
    A::Assert(0,"asdasd");
    B::Assert(0,"asdasd");
}

因此,虽然定义看起来像是“在命名空间中”,但它们不是,并且#define将始终使用最后一个,在这种情况下会导致编译时错误,因为 main 中的代码将被替换为:

A::Assert(0);
B::Assert(0);

代替

A::Assert(0,"asdasd", _FILE_, _LINE_);
B::Assert(0);
于 2012-08-03T07:28:21.513 回答
2
namespace Debug
{
    void Assert_(int condition, std::string message, std::string file, int line);
    #define Assert(a,b) Assert_(a, b, __FILE__, __LINE__)
}

// .... Somewhere where I call the function ....
Debug::Assert (some_condition, "Some_condition should be true"); 

这种特定的用法完全可以满足您的需求,但是Assert 宏绝不是 Debug 命名空间的一部分……就像您已经完成了一样:

namespace Debug
{
    void Assert_(int condition, std::string message, std::string file, int line);
}

#define Assert(a,b) Assert_(a, b, __FILE__, __LINE__)

// .... Somewhere where I call the function ....
Debug::Assert (some_condition, "Some_condition should be true"); 

在这里,替换工作不是因为AssertDebug命名空间中(它不在您的代码或此代码中,并且预处理器不知道命名空间是关于什么的) - 它之所以有效,是因为Assert被识别为宏的标识符,替换Assert_了,然后稍后正确的编译器碰巧发现有一个Debug::Assert_ 所以,假设您稍后在翻译单元中的某个地方有一些完全不相关的代码:

my_object.Assert(my_functor);

宏替换仍然会产生编译时错误,说明您的宏参数数量错误。说不相关的代码是:

my_object.Assert(my_functor, "some text");

然后将替换为:

my_object.Assert_(my_functor, "some text", __FILE__, __LINE__);

(另外,在预处理器宏名称中不使用小写字母是标准做法)。

于 2012-08-03T08:05:35.360 回答
1

不,预处理器根本不关心命名空间。事实上,预处理器至少在概念上是在编译器看到任何东西之前运行的。

对于我自己,我只是做一个标准的 ASSERT 宏,并期望没有“理智的命名空间”有一个叫做 ASSERT 的东西。问题解决了。如果我需要一个拥有自己的 ASSERT 的库,那么我仍然可以决定如何处理这个问题;但是,我目前使用自己的“断言”的唯一库将其称为 BOOST_ASSERT 或类似的东西......

于 2012-08-03T07:32:22.433 回答
0

您可以尝试使用 __PRETTY_FUNCTION __ 宏来打印所有命名空间,包括函数参数。

于 2016-11-10T02:03:43.170 回答
-2

是的,您的宏将扩展至您所期望的。

Debug::Assert (some_condition, "Some_condition should be true");

将被替换为

Debug::Assert_(some_condition, "Some_condition should be true", __FILE__, __LINE__)
于 2012-08-03T07:30:32.737 回答