在C++中检测命名空间

4

在代码的某个给定点,是否可能检测出您所处的命名空间?特别是,如果一个文件被包含在全局命名空间中,我想发出警告。


这个文件只能包含在特定的命名空间中,还是可以放在任何命名空间中? - Luchian Grigore
@LuchianGrigore 我将其包含在不同的命名空间中以在其中执行一些typedef操作,并希望在生成这些typedef的全局命名空间的文件中获得警告。 - pythonic metaphor
3个回答

2

良好的实践是将所有标题包含在全局命名空间中。在文件开头打开所有需要的命名空间,在结尾之前关闭它们。其他方式不可避免地会导致一系列问题。

** 注释扩展 **

为了防止意外的包含,您可以采取以下措施:

In header:
#ifndef INTERNAL_INCLUDE
#error ...
#endif

When used:
#define INTERNAL_INCLUDE
#include "internal.h"
#undef INTERNAL_INCLUDE

2
不同意。这个规则有例外。例如在http://www.boost.org/doc/libs/1_51_0/boost/smart_ptr/shared_ptr.hpp中,有以下包含`// implicit conversion to "bool" #include <boost/smart_ptr/detail/operator_bool.hpp>`。 - ForEveR
在我的情况下,我使用文件来声明一些typedef,我希望它们被限定在一个命名空间内,然后将该文件包含在一些不同的命名空间中。但是,无论这是否是一个好主意,了解我的问题的答案都是有趣的。我会标记我的问题为“语言律师”。 - pythonic metaphor
我看到了,boost开发者们以类似的方式使用了这个方法。所以,如果你想要防止头文件被不经意地包含进来,我建议你添加一个预处理器条件:#ifndef _SOME_VAR_ #error bad include #endif - Pavel Ognev

2

我可以给你一个提示,如果头文件没有包含在全局命名空间中,则会生成编译错误。如果我知道确定会生成编译器警告(不是 #warning)的 C++ 构造方法,则可以将其用于替代编译错误。

在您的头文件中添加:

template <class A, class B>
struct AreSame { enum { VALUE = -1 }; };
template <class A>
struct AreSame<A,A> { enum { VALUE = 1 }; };

struct TestGlobalNamespace
{
   int test_namespace[AreSame<TestGlobalNamespace, ::TestGlobalNamespace>::VALUE];
};

当您的头文件包含在某个命名空间中时,会出现错误。

就像这个例子:

namespace Some {

struct TestGlobalNamespace
{
   int test_namespace[AreSame<TestGlobalNamespace, ::TestGlobalNamespace>::VALUE];
};

}

您将获得:

prog.cpp:17: error: size of array ‘test_namespace’ is negative

[更新]
然而,更可能出现的错误是这种类型的错误:
prog.cpp:17: error: ‘::TestGlobalNamespace’ has not been declared
prog.cpp:17: error: template argument 2 is invalid

无论如何,除了全局命名空间,没有人敢将您的头文件包含到其他命名空间中。

在我写完这个之后,我意识到你想要相反的效果,即当文件在全局命名空间中时发出警告。那么就做相反的操作。创建AreDifferent<A,B>模板,你就会得到相反的效果。如果你希望以这种方式实现,请给我留言。 - PiotrNycz
我意识到,仅仅是在我的答案中添加一组新的错误更有可能发生。如果你想让你的头文件只包含在“Some”名称空间中 - 只需使用“Some::TestSomeNamespace”。 - PiotrNycz
这对我不起作用... 我需要一个 static_assert<>,当不在全局命名空间时触发。编译错误行不通 :/. - Carlo Wood
@CarloWood 你试过使用 static_assert(AreSame<TestGlobalNamespace, ::TestGlobalNamespace>::VALUE== 1);` 吗? - PiotrNycz
如果您在随机命名空间中尝试这样做,那么TestGlobalNamespace必须在该命名空间中定义(否则它当然会说它们是相同的)。最终我想出了一个宏,在全局命名空间之外使用时触发断言,但最终删除了它,因为我选择了另一条路线。 - Carlo Wood

1

您可以通过需要在第一个头文件之前包含在全局命名空间中的第二个头文件来实现此操作,虽然会稍微有些不便。

// stuff_guard.h - include from global namespace to guard stuff.h
#ifndef STUFF_GUARD_H
#define STUFF_GUARD_H
typedef char stuff_guard[1];
#endif

// stuff.h - must be included from non-global namespace, after stuff_guard.h
// No include guard - may be included multiple times, in different namespaces.

#ifndef STUFF_GUARD_H
#error stuff_guard.h must be included before stuff.h
#endif

typedef char stuff_guard[2];

static_assert(sizeof (stuff_guard) != sizeof (::stuff_guard), 
    "stuff.h must not be included from the global namespace");

// Put your stuff here

如果您违反这两个规则中的任何一个,它将提供相当友好的错误消息;如果您从非全局命名空间包含stuff_guard.h,则会提供一条略微不太友好的错误消息。

如果您使用旧编译器且无法使用static_assert,则可以使用BOOST_STATIC_ASSERT自己编写


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接