【问题标题】:shielding #include within namespace { } block?在命名空间{}块中屏蔽#include?
【发布时间】:2011-02-21 13:47:58
【问题描述】:

编辑:我知道方法 1 本质上是无效的,并且可能会使用方法 2,但我正在寻找最好的 hack 或更好的解决方案来缓解猖獗的、可变的命名空间扩散。

我在一个命名空间中有多个类或方法定义,它们具有不同的依赖关系,并且希望尽可能使用最少的命名空间块或显式范围,但同时将 #include 指令与尽可能需要它们的定义进行分组。我从来没有看到任何迹象表明可以告诉任何预处理器从#include 内容中排除命名空间 {} 范围,但我在这里询问是否有可能进行类似的操作:(请参阅底部以了解为什么我想要一些死的东西简单)

// NOTE: apple.h, etc., contents are *NOT* intended to be in namespace Foo!

// would prefer something most this:
#pragma magic_namespace_backout(1) // FIXME: use actually existing directive
namespace Foo {

#include "apple.h"
B *A::blah(B const *x) { /* ... */ }

#include "banana.h"
int B::whatever(C const &var) { /* ... */ }

#include "blueberry.h"
void B::something() { /* ... */ }

} // namespace Foo

...

// over this:
#include "apple.h"
#include "banana.h"
#include "blueberry.h"

namespace Foo {
B *A::blah(B const *x) { /* ... */ }
int B::whatever(C const &var) { /* ... */ }
void B::something() { /* ... */ }
} // namespace Foo

...

// or over this:
#include "apple.h"
namespace Foo {
B *A::blah(B const *x) { /* ... */ }
} // namespace Foo

#include "banana.h"
namespace Foo {
int B::whatever(C const &var) { /* ... */ }
} // namespace Foo

#include "blueberry.h"
namespace Foo {
void B::something() { /* ... */ }
} // namespace Foo

我真正的问题是,我的项目可能需要对模块进行分支,但在同一程序中的分支中存在共存的组件。我有像 FooA 之类的类,我称之为 Foo::A,希望能够像 Foo::v1_2::A 那样痛苦地分支,其中一些程序可能需要 Foo::A 和 Foo ::v1_2::A。如果可能的话,我希望“Foo”或“Foo::v1_2”每个文件只显示一次,作为单个命名空间块。此外,我倾向于将#include 指令块定位在文件中需要它们的第一个定义的正上方。我最好的选择是什么,或者,我应该做什么而不是劫持命名空间?

【问题讨论】:

  • 我刚刚选择了选项 #2,并尝试使用包含的内容尽可能干净。

标签: c++ namespaces include


【解决方案1】:

只需将#include 视为将包含文件的内容复制并粘贴到#include 指令的位置。

这意味着,是的,包含文件中的所有内容都将在命名空间内。

【讨论】:

  • 谢谢,我已经接受了相当盲目的工具的预处理器。
  • 请注意,任何#defines 都不会在命名空间内。
  • 因为#defines 没有任何命名空间的概念
  • 前面有 # 的任何事情都发生在编译之前。诸如命名空间和编译期间的所有其他代码过程之类的事情。
【解决方案2】:

问:你能:
答:是的,你可以。 include 语句是在编译器看到它之前在预处理期间完成的。

问:这是个好主意吗。
答:可能不会。

如果 #include Apple.g 没有命名空间标签会发生什么。
现在你已经在全局命名空间和 foo 命名空间中声明了苹果。

您应该尽量避免您的代码的用户需要了解应该如何使用它的情况。如果您的文档总是说 #include foo 命名空间内的苹果头文件,那么用户将不会阅读并导致数小时的混乱。

【讨论】:

  • 更好的办法是让“apple_noNS.h”成为“原始”文件,而“apple.h”由namespace Foo { #include apple_noNS.h }组成,然后在命名空间之外的任何地方都包含apple.h。跨度>
  • @SF。这是一个可怕的想法。并在上面的陈述中明确说明了原因。
  • 在我的情况下,数据结构在(较小的)ANSI C 程序和(巨大的)C++ 程序之间共享。 C 中没有命名空间,但由于 C 程序的大小,其在全局命名空间中存在的众多条目的冲突和问题的风险得到了缓解。 OTOH 将其包含在 C++ 应用程序的全局命名空间中会导致一系列问题。我的代码的用户无论如何都需要了解这种依赖关系,否则如果他们用 C 部分做 C++ 的事情,事情就会浮出水面。这些文件必须被视为 API 类的私有字段。
  • @SF.:这没有任何改变。还有更好的方法可以做到这一点。如上所述(现在重复),这将导致问题。如果您想找到更好的方法,请提出问题,我们将为您提供正确的方法。
  • “你应该尽量避免你的代码的用户需要了解它应该如何使用的情况。”这可能是有史以来最好的指导方针:)
【解决方案3】:

您是否编辑过这个问题?

您的示例中的第一个块是不可能的。你不能像你想要的那样从一个命名空间或任何东西上去,也不能从该命名空间块中包含的文件中禁用命名空间。根本不可能那样做。

我个人更喜欢你的第一个选择。

编辑,好的...这是您可以做的事情(可能需要清理,未经测试):


#define MY_NAMESPACE Foo
#define NAMESPACE_WRAP(X) namespace MY_NAMESPACE { X }

#include "apple.h"
NAMESPACE_WRAP((B * A::blah(B const * x) {...}))

很确定 NAMESPACE_WRAP 不适用于那种事情,所以你可能需要将它放在不同的标题或“.ipp”或其他任何东西中并执行此操作:


#define NAMESPACE_WRAP(HEADER) \
namespace MY_NAMESPACE { \
#include HEADER \
}

即使这样也可能行不通,您必须超出我的知识范围,看看 boost 预处理器元编程库是如何包含宏的。您实际上可能会发现该库最终使您想要的更容易。

无论如何,它不会像你想要的那样漂亮,恕我直言,它不会像你提出的第一个替代方案那样可读和直接。

【讨论】:

  • 是的,我进行了编辑,因为我的目标不是是/否的答案,而是与第一个示例最相似的解决方案。我试图表明第一种方法不可行,至少是我的默认方法,但我愿意使用非标准的预处理器指令等来 - 使其正确。
【解决方案4】:

学会喜欢第三个例子,把它分成三个单独的文件。这确实是最明确的方法。

如果您真的想在其他命名空间中包含文件,您可以将} 作为包含文件的第一个字符,并将namespace Whatever { 放在末尾。但这会很糟糕。

【讨论】:

    【解决方案5】:

    可能每个模块都应该引用“Foo::A”类,您可以将宏定义放在需要其他版本“A”的模块的开头。

    #include "apple.h"
    #include "apple1_2.h"
    
    //this module uses Version 1.2 of "Apple" class
    #define Apple v1_2::Apple
    namespace Foo {
    B *A::blah(B const *x) 
    {
        Foo::Apple apple; //apple is of type Foo::v1_2::Apple
        /* ... */ 
    } 
    int B::whatever(C const &var) { /* ... */ }
    void B::something() { /* ... */ }
    } // namespace Foo
    #undef Apple
    

    但这会使代码更难理解。也许,如果您需要在对象的实现之间进行选择,您最好使用工厂函数。这将使您的意图在整个代码中变得明确。

    AppleBaseClass* createApple(int version)
    {
        if(version == 0)
            return new Foo::Apple;
        else if(version == 1)
            return new Foo::v1_2::Apple;
    }
    //usage
    AppleBaseClass* apple = createApple(apple_version);
    
    //compile-time equivalent
    //metafunction CreateApple
    template<int version> struct CreateApple {};
    template<>
    struct CreateApple<0> 
    {
        typedef Foo::Apple ret;
    };
    template<>
    struct CreateApple<1> 
    {
        typedef Foo::v1_2::Apple ret;
    };
    //usage
    CreateApple<apple_version>::ret apple;
    

    【讨论】:

      【解决方案6】:

      方法2一路走来。
      我总是遵循这些简单的规则:

      1.) 源代码应该清晰、易于理解和防伪。
      - 一个好的产品不是一个人做的。简单、直观和易于遵循的格式将使每个人的生活更快乐。

      2.) 如果最终产品没有性能差异,请遵守规则 #1
      - 开发人员将脑细胞花在不利于最终客户的事情上是没有意义的。

      3.) 优雅的设计总是能正常工作,所以规则 #2 始终适用。
      -同样的规则也适用于上帝,拿镜子看看自己:)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-10-03
        • 1970-01-01
        • 2014-06-07
        • 2010-11-27
        • 1970-01-01
        • 2018-07-07
        • 1970-01-01
        • 2018-12-03
        相关资源
        最近更新 更多