web-dev-qa-db-ja.com

ヘッダー内でC ++でネストされた名前空間を表現するより良い方法はありますか

私はC++からJava and C#に切り替えて、名前空間/パッケージの使用法がはるかに優れていると考えています(構造化されています)。その後、C++に戻り、ヘッダーファイル内で必要な構文は恐ろしいです。

namespace MyCompany
{
    namespace MyModule
    {
        namespace MyModulePart //e.g. Input
        {
            namespace MySubModulePart
            {
                namespace ...
                {
                    public class MyClass    

以下も私には奇妙に思えます(深いインデントを避けるため):

namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
     public class MyClass
     {

上記のことを表現する短い方法はありますか?私は次のようなものが欠けています

namespace MyCompany::MyModule::MyModulePart::...
{
   public class MyClass

更新

OK、Java/C#とC++での使用の概念が異なると言う人もいます。本当に?名前空間の目的は(動的な)クラスローディングだけではないと思います(これは非常に技術的な理由からです)。なぜ読みやすさと構造化のためにそれを使うべきではないのか、例えば「IntelliSense」を考えてみてください。

現在、名前空間とそこにあるものとの間にロジック/グルーはありません。 JavaそしてC#はこれをはるかに改善しています...なぜ<iostream>をインクルードし、名前空間stdを持っているのですか?]なぜ#includeが#include <std::io::stream><std/io/stream>のような "IntelliSense"フレンドリーな構文を使用しないのですか?デフォルトのライブラリに欠けている構造化は、Java/C#と比較したC++の弱点の1つです。

熱心な競合に対する一意性が1つのポイント(C#とJavaのポイントでもある)である場合、プロジェクト名または会社名をネームスペースとして使用することをお勧めします。

一方では、C++が最も柔軟であると言われています...しかし、誰もが「これをしないでください」と言いましたか? C++は多くのことを実行できるように思えますが、多くの場合、C#と比較して最も簡単なことでも恐ろしい構文を持っています。

更新2

ほとんどのユーザーは、2つのレベルよりも深いネストを作成するのはナンセンスだと言います。では、Win8開発でのWindows :: UI :: XamlおよびWindows :: UI :: Xaml :: Controls :: Primitives名前空間についてはどうでしょうか。 Microsoftの名前空間の使用は理にかなっていると思いますが、それはまさに2レベルよりも深いものです。大きなライブラリ/プロジェクトには、より深いネストが必要だと思います(ExtraLongClassNameBecauseEveryThingIsInTheSameNameSpaceのようなクラス名は嫌いです...そして、すべてをグローバルネームスペースに入れることもできます)。

更新3-結論

ほとんどの人は「やってはいけない」と言っていますが、...ブーストは、1つまたは2つのレベルよりも深いネストを持っています。はい、それはライブラリですが、再利用可能なコードが必要な場合は、自分のコードをライブラリのように扱い、他の誰かに提供します。また、ネームスペースを使用したディスカバリー目的で、より深いネストを使用します。

84
Beachwalker

C++ 17は、ネストされた名前空間定義を単純化する場合があります。

namespace A::B::C {
}

に等しい

namespace A { namespace B { namespace C {
} } }

Cppreferenceの名前空間ページの(8)を参照してください。
http://en.cppreference.com/w/cpp/language/namespace

108
W1M0R

本当に深いインデントを避けるために、私は通常このようにします:

namespace A { namespace B { namespace C
{
    class X
    {
        // ...
    };
}}}
26
Kurt Hutchinson

peterchen's answer を完全にサポートしていますが、質問の別の部分に対処する何かを追加したいです。

名前空間の宣言は、C++で私が実際に#defines。

#define MY_COMPANY_BEGIN  namespace MyCompany { // begin of the MyCompany namespace
#define MY_COMPANY_END    }                     // end of the MyCompany namespace
#define MY_LIBRARY_BEGIN  namespace MyLibrary { // begin of the MyLibrary namespace
#define MY_LIBRARY_END    }                     // end of the MyLibrary namespace

これにより、名前空間の閉じ中括弧の近くにコメントが不要になります(大きなソースファイルの一番下までスクロールして、どの括弧がどのスコープを閉じるかについてのコメントが欠落している中括弧を追加/削除/バランスしようとしましたか? 。)。

MY_COMPANY_BEGIN
MY_LIBRARY_BEGIN

class X { };

class Y { };

MY_LIBRARY_END
MY_COMPANY_END

すべての名前空間宣言を1行に配置したい場合は、少し(かなりlyい)プリプロセッサマジックを使用して同様に行うことができます。

// helper macros for variadic macro overloading
#define VA_HELPER_EXPAND(_X)                    _X  // workaround for Visual Studio
#define VA_COUNT_HELPER(_1, _2, _3, _4, _5, _6, _Count, ...) _Count
#define VA_COUNT(...)                           VA_HELPER_EXPAND(VA_COUNT_HELPER(__VA_ARGS__, 6, 5, 4, 3, 2, 1))
#define VA_SELECT_CAT(_Name, _Count, ...)       VA_HELPER_EXPAND(_Name##_Count(__VA_ARGS__))
#define VA_SELECT_HELPER(_Name, _Count, ...)    VA_SELECT_CAT(_Name, _Count, __VA_ARGS__)
#define VA_SELECT(_Name, ...)                   VA_SELECT_HELPER(_Name, VA_COUNT(__VA_ARGS__), __VA_ARGS__)

// overloads for NAMESPACE_BEGIN
#define NAMESPACE_BEGIN_HELPER1(_Ns1)             namespace _Ns1 {
#define NAMESPACE_BEGIN_HELPER2(_Ns1, _Ns2)       namespace _Ns1 { NAMESPACE_BEGIN_HELPER1(_Ns2)
#define NAMESPACE_BEGIN_HELPER3(_Ns1, _Ns2, _Ns3) namespace _Ns1 { NAMESPACE_BEGIN_HELPER2(_Ns2, _Ns3)

// overloads for NAMESPACE_END
#define NAMESPACE_END_HELPER1(_Ns1)               }
#define NAMESPACE_END_HELPER2(_Ns1, _Ns2)         } NAMESPACE_END_HELPER1(_Ns2)
#define NAMESPACE_END_HELPER3(_Ns1, _Ns2, _Ns3)   } NAMESPACE_END_HELPER2(_Ns2, _Ns3)

// final macros
#define NAMESPACE_BEGIN(_Namespace, ...)    VA_SELECT(NAMESPACE_BEGIN_HELPER, _Namespace, __VA_ARGS__)
#define NAMESPACE_END(_Namespace, ...)      VA_SELECT(NAMESPACE_END_HELPER,   _Namespace, __VA_ARGS__)

これで次のことができます。

NAMESPACE_BEGIN(Foo, Bar, Baz)

class X { };

NAMESPACE_END(Baz, Bar, Foo) // order doesn't matter, NAMESPACE_END(a, b, c) would work equally well

Foo::Bar::Baz::X x;

3レベルより深くネストする場合は、必要な数までヘルパーマクロを追加する必要があります。

13
Max Truxa

C++名前空間は、コンポーネントを分割したり、政治的分裂を表現したりするためではなく、インターフェイスをグループ化するために使用されます。

この標準は、Javaのような名前空間の使用を禁止するために、邪魔になりません。たとえば、namespace aliasesは、深くネストされた名前空間名または長い名前空間名を簡単に使用する方法を提供します。

namespace a {
namespace b {
namespace c {}
}
}

namespace nsc = a::b::c;

しかし、namespace nsc {}はエラーになります。名前空間はoriginal-namespace-nameを使用してのみ定義できるためです。基本的に、標準はそのようなライブラリのserに対しては物事を簡単にしますが、implementerにとっては難しいものです。これは、人々がそのようなことを書くことを思いとどまらせるが、もしそうすればその影響を緩和する。

関連するクラスと関数のセットによって定義されるインターフェースごとに1つの名前空間が必要です。内部またはオプションのサブインターフェースは、ネストされた名前空間に入る場合があります。しかし、2つ以上のレベルの深さは、非常に深刻な危険信号です。

アンダースコア文字と識別子プレフィックスの使用を検討してください。::演算子は必要ありません。

12
Potatoswatter

いいえ、そうしないでください。

ネームスペースの目的は、主にグローバルネームスペースの競合を解決することです。

二次的な目的は、シンボルのローカル略語です。例えば複雑なUpdateUIメソッドでは、using namespace WndUIを使用して短いシンボルを使用できます。

私は1.3MLocプロジェクトに参加しており、唯一の名前空間は次のとおりです。

  • インポートされた外部COMライブラリ(主に#import#include windows.hの間のヘッダーの競合を分離するため)
  • 特定の側面(UI、DBアクセスなど)のための「パブリックAPI」名前空間の1つのレベル
  • パブリックAPIの一部ではない「実装の詳細」名前空間(.cppの匿名名前空間、またはヘッダーのみのライブラリのModuleDetailHereBeTygers名前空間)
  • 列挙型は私の経験の最大の問題です。彼らは狂ったように汚染します。
  • まだ名前空間が多すぎると感じています

このプロジェクトでは、クラス名などで2文字または3文字の「リージョン」コードを使用します(例:DB::CNodeの代わりにCDBNode)。後者を好む場合、「パブリック」名前空間の第2レベルの余地がありますが、それ以上はありません。

クラス固有の列挙などは、これらのクラスのメンバーになることができます(これは必ずしも良いとは限らないことに同意しますが、あなたがすべきかどうかを言うのは難しいです)

バイナリとして配布されるサードパーティのライブラリに大きな問題があり、独自の名前空間を提供せず、1つに簡単に入れることができない場合を除いて、「会社」の名前空間もほとんど必要ありません。分布)。それでも、私の経験では、themを名前空間に強制する方がはるかに簡単です。


[編集]Stegiのフォローアップの質問に従って:

では、Win8開発のWindows :: UI :: XamlとWindows :: UI :: Xaml :: Controls :: Primitives名前空間についてはどうでしょうか? Microsoftの名前空間の使用は理にかなっていると思いますが、それはまさに2レベルよりも深いです

十分に明確でない場合は申し訳ありません。2つのレベルはハードリミットではなく、本質的に悪いレベルではありません。私の経験から、大規模なコードベースであっても、2つ以上を必要とすることはめったにないことを指摘したかっただけです。より深くまたはより浅くネストすることはトレードオフです。

現在、Microsoftのケースは間違いなく異なっています。おそらくはるかに大きなチームであり、allコードはライブラリです。

ここでは、Microsoftが.NET Libraryの成功を模倣していると思います。そこでは、名前空間が広範なライブラリの検出可能性に貢献しています。 (.NETには 約180 タイプがあります。)

さらに、名前空間に最適な(大きさの)シンボルがあると仮定します。たとえば、1は意味をなさない、100音は正しい、10000は明らかに多すぎる。


TL; DR:これはトレードオフであり、ハードナンバーはありません。安全にプレイし、どの方向にも無理をしないでください。 「それをしないでください」は単に「あなたはそれで問題を抱えている、私はそれで問題を抱えているだろう、そして私はあなたがそれを必要とする理由がわからない」から来る。

7
peterchen

Lzz (Lazy C++)ドキュメントからの引用:

Lzzは次のC++構造を認識します。

名前空間の定義

名前のない名前空間とすべての囲まれた宣言は、ソースファイルに出力されます。このルールは他のすべてをオーバーライドします。

名前付き名前空間の名前は修飾される場合があります。

   namespace A::B { typedef int I; }

と同等です:

   namespace A { namespace B { typedef int I; } }

もちろん、そのようなツールに依存するソースの品質は議論の余地があります...私はそれがより好奇心であり、C++によって引き起こされる構文病が多くの形をとることができることを示します(私も持っています...)

4
CapelliC

両方の標準(C++ 2003およびC++ 11)は、名前空間の名前が識別子であることを非常に明示しています。これは、明示的にネストされたヘッダーが必要であることを意味します。

名前空間の単純な名前のほかに修飾識別子を配置することは大したことではないという私の印象ですが、何らかの理由でこれは許可されていません。

2
Kirill Kobelev

次の構文を使用できます。

namespace MyCompany {
  namespace MyModule {
    namespace MyModulePart //e.g. Input {
      namespace MySubModulePart {
        namespace ... {
          class MyClass;
        }
      }
    }
  }
}

// Here is where the magic happens
class MyCompany::MyModule::MyModulePart::MySubModulePart::MyYouGetTheIdeaModule::MyClass {
    ...
};

この構文はC++ 98でも有効であり、C++ 17で 入れ子になった名前空間定義 で利用できるようになったことにほとんど注意してください。

ネストを解除してください!

出典:

2
smac89

はい、あなたはそれをする必要があります

namespace A{ 
namespace B{
namespace C{} 
} 
}

ただし、使用しないはずの方法で名前空間を使用しようとしています。 this の質問を確認してください。おそらく役に立つでしょう。

1
SingerOfTheFall

[編集:]
c ++ 17ネストされた名前空間は、標準言語機能としてサポートされているため( https://en.wikipedia.org/wiki/C%2B%2B17 )。現在、この機能はg ++ 8ではサポートされていませんが、clang ++ 6.0コンパイラで見つけることができます。


[結論:]
デフォルトのコンパイルコマンドとしてclang++6.0 -std=c++17を使用します。その後、すべてが正常に動作するはずです-そして、あなたはあなたのファイルでnamespace OuterNS::InnerNS1::InnerNS2 { ... }でコンパイルすることができます。


[元の回答:]
この質問は少し古いので、先に進んだと思います。しかし、まだ答えを探している他の人のために、私は次のアイデアを思いつきました:

Emacs buffers showing main file, namespace files, compilation command/result, and command-line execution.

(ここでEmacsの広告を作成できますか?)?)単にコードを投稿するよりも、画像を投稿する方がはるかに簡単で読みやすいです。すべてのコーナーケースの完全な回答を提供するつもりはありません。単にインスピレーションを与えるつもりでした。 (私は完全にC#をサポートしており、多くの場合、C++はいくつかのOOP機能を採用すべきだと感じています。

0
Alexander