헤더 내에서 C ++로 중첩 된 네임 스페이스를 표현하는 더 좋은 방법이 있습니까? (동적) 클래스 로딩이 네임

저는 C ++에서 Java 및 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

최신 정보

좋아, 일부는 Java / C # 및 C ++의 사용 개념이 다르다고 말합니다. 정말? 나는 (동적) 클래스 로딩이 네임 스페이스의 유일한 목적이 아니라고 생각합니다 (이것은 매우 기술적 인 관점입니다). 예를 들어 “IntelliSense”와 같이 가독성 및 구조화를 위해 사용하면 안됩니다.

현재 네임 스페이스와 거기에서 찾을 수있는 것 사이에는 논리 / 접착제가 없습니다. Java와 C #은 이보다 훨씬 낫습니다. 왜 <iostream>네임 스페이스를 포함 하고 갖는가 std? 좋습니다. 논리가 포함 할 헤더에 의존해야한다고 말하면 #include가 #include <std::io::stream>또는 같은 “IntelliSense”친숙한 구문을 사용하지 않는 이유는 <std/io/stream>무엇입니까? 기본 라이브러리에서 누락 된 구조화는 Java / C #에 비해 C ++의 약점 중 하나라고 생각합니다.

열렬한 충돌에 대한 고유성이 1 포인트 (C # 및 Java의 포인트이기도 함) 인 경우 프로젝트 이름이나 회사 이름을 네임 스페이스로 사용하는 것이 좋습니다. 그렇게 생각하지 않습니까?

한편으로는 C ++가 가장 유연하다고합니다.하지만 모두가 “이러지 마세요”라고 말했습니까? 나에게 C ++는 많은 일을 할 수 있지만 C #에 비해 많은 경우 가장 쉬운 일에도 끔찍한 구문을 가지고 있습니다.

업데이트 2

대부분의 사용자는 두 수준보다 더 깊은 중첩을 만드는 것이 말도 안된다고 말합니다. 그렇다면 Win8 개발에서 Windows :: UI :: Xaml 및 Windows :: UI :: Xaml :: Controls :: Primitives 네임 스페이스는 어떻습니까? 나는 마이크로 소프트의 네임 스페이스 사용이 합리적이라고 생각하며 실제로 2 단계보다 더 깊다. 더 큰 라이브러리 / 프로젝트에는 더 깊은 중첩이 필요하다고 생각합니다 (ExtraLongClassNameBecauseEveryThingIsInTheSameNameSpace와 같은 클래스 이름을 싫어합니다 … 그러면 모든 것을 전역 네임 스페이스에 넣을 수도 있습니다.)

업데이트 3-결론

대부분은 “하지 마세요”라고 말하지만 … 부스트조차도 한두 단계보다 더 깊은 중첩을 가지고 있습니다. 예, 라이브러리이지만 재사용 가능한 코드를 원한다면-자신의 코드를 다른 사람에게 줄 라이브러리처럼 취급하십시오. 또한 네임 스페이스를 사용하여 검색 목적으로 더 깊은 중첩을 사용합니다.



답변

C ++ 17은 중첩 된 네임 스페이스 정의를 단순화 할 수 있습니다.

namespace A::B::C {
}

다음과 같다

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

cppreference의 네임 스페이스 페이지에서 (8) 참조 :
http://en.cppreference.com/w/cpp/language/namespace


답변

깊은 들여 쓰기를 피하기 위해 일반적으로 다음과 같이합니다.

namespace A { namespace B { namespace C
{
    class X
    {
        // ...
    };
}}}


답변

나는 peterchen의 대답을 전적으로지지 하지만 질문의 다른 부분을 다루는 것을 추가하고 싶습니다.

네임 스페이스 선언은 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

모든 네임 스페이스 선언을 한 줄에 넣으려면 약간의 (매우 추악한) 전 처리기 마법으로도 그렇게 할 수 있습니다.

// 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 단계보다 깊게 중첩하려면 원하는 개수까지 도우미 매크로를 추가해야합니다.


답변

C ++ 네임 스페이스는 구성 요소를 나누거나 정치적 구분을 표현하는 것이 아니라 인터페이스를 그룹화하는 데 사용됩니다.

이 표준은 자바와 유사한 네임 스페이스 사용을 금지하는 방식으로 진행됩니다. 예를 들어 네임 스페이스 별칭 은 깊이 중첩되거나 긴 네임 스페이스 이름을 쉽게 사용할 수있는 방법을 제공합니다.

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

namespace nsc = a::b::c;

그러나 namespace nsc {}네임 스페이스는 original-namespace-name을 사용해서 만 정의 될 수 있기 때문에 오류가됩니다 . 기본적으로 표준은 그러한 라이브러리 의 사용자 에게는 일을 쉽게 하지만 구현 자 에게는 어렵습니다 . 이것은 사람들이 그런 글을 쓰는 것을 막지 만 글을 쓰면 그 효과를 완화시킵니다.

관련 클래스 및 함수 집합에 의해 정의 된 인터페이스 당 하나의 네임 스페이스가 있어야합니다. 내부 또는 선택적 하위 인터페이스는 중첩 된 네임 스페이스에 들어갈 수 있습니다. 그러나 2 개 이상의 수준은 매우 심각한 위험 신호 여야합니다.

::연산자가 필요하지 않은 경우 밑줄 문자와 식별자 접두사를 사용하는 것이 좋습니다.


답변

아뇨, 그러지 마세요.

네임 스페이스의 목적은 주로 전역 네임 스페이스의 충돌을 해결하는 것입니다.

두 번째 목적은 기호의 지역 약어입니다. 예를 들어 복잡한 UpdateUI방법은를 사용하여 using namespace WndUI더 짧은 기호를 사용할 수 있습니다 .

저는 1.3MLoc 프로젝트에 있으며 우리가 가진 유일한 네임 스페이스는 다음과 같습니다.

  • 가져온 외부 COM 라이브러리 (주로 #import및 간의 헤더 충돌을 격리하기 위해
    #include windows.h)
  • 특정 측면 (UI, DB 액세스 등)에 대한 한 수준의 “공용 API”네임 스페이스
  • 공용 API의 일부가 아닌 “구현 세부 정보”네임 스페이스 (.cpp의 익명 네임 스페이스 또는 ModuleDetailHereBeTygers헤더 전용 라이브러리의 네임 스페이스)
  • 열거 형은 내 경험에서 가장 큰 문제입니다. 그들은 미친 듯이 오염시킵니다.
  • 나는 아직도 그것이 완전히 너무 많은 네임 스페이스라고 느낀다.

이 프로젝트에서 클래스 이름 등은 2 자 또는 3 자 “지역”코드 (예 : CDBNode대신 DB::CNode)를 사용합니다. 후자를 선호하는 경우 두 번째 수준의 “공용”네임 스페이스를위한 공간이 있지만 더 이상은 없습니다.

클래스 별 열거 형 등은 해당 클래스의 구성원이 될 수 있습니다 (항상 좋은 것은 아니라는 데 동의하며 때로는 그래야하는지 여부를 말하기가 어렵습니다).

바이너리로 배포되고 자체 네임 스페이스를 제공하지 않고 쉽게 하나에 넣을 수없는 타사 라이브러리에 큰 문제가있는 경우를 제외하고는 “회사”네임 스페이스가 거의 필요하지 않습니다 (예 : 바이너리 분포). 그래도 내 경험상 그것들 을 네임 스페이스로 강제 하는 것은 훨씬 쉽습니다.


[편집] Stegi의 후속 질문으로 당 :

그렇다면 Win8 개발에서 Windows :: UI :: Xaml 및 Windows :: UI :: Xaml :: Controls :: Primitives 네임 스페이스는 어떻습니까? Microsoft의 네임 스페이스 사용이 합리적이라고 생각하며 실제로 2 단계보다 더 깊습니다.

충분히 명확하지 않은 경우 죄송합니다. 두 수준은 엄격한 제한이 아니며 더 많은 수준은 본질적으로 나쁘지 않습니다. 제 경험상, 대규모 코드 기반에서도 두 개 이상은 거의 필요 하지 않다는 점을 지적하고 싶었습니다 . 더 깊거나 더 얕은 중첩은 트레이드 오프입니다.

이제 Microsoft 사례는 틀림없이 다릅니다. 아마도 훨씬 더 큰 팀이며 모든 코드는 라이브러리입니다.

Microsoft가 여기에서 네임 스페이스 가 광범위한 라이브러리 의 검색 가능성 에 기여하는 .NET 라이브러리의 성공을 모방하고 있다고 가정 합니다. (.NET에는 약 18000 개의 유형이 있습니다.)

또한 네임 스페이스에 최적 (크기 순서) 기호가 있다고 가정합니다. 1은 말이 안 돼요, 100은 맞아요, 10000은 분명히 많이 요.


TL; DR : 이것은 트레이드 오프이며, 우리는 정확한 숫자가 없습니다. 안전하게 플레이하고 어떤 방향으로도 무리하지 마십시오. “그렇게하지 마라”는 “당신은 그것에 문제가 있고, 나는 그것에 문제가있을 것이고, 당신이 그것을 필요로하는 이유를 알지 못합니다.”에서 온 것입니다.


답변

다음은 Lzz (Lazy C ++) 문서 의 인용문입니다 .

Lzz는 다음 C ++ 구문을 인식합니다.

네임 스페이스 정의

이름이 지정되지 않은 네임 스페이스와 포함 된 모든 선언이 소스 파일에 출력됩니다. 이 규칙은 다른 모든 규칙을 재정의합니다.

명명 된 네임 스페이스의 이름을 정규화 할 수 있습니다.

   namespace A::B { typedef int I; }

다음과 같습니다.

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

물론 이러한 도구에 의존하는 소스의 품질은 논란의 여지가 있습니다. C ++에 의해 유발 된 구문 질환이 다양한 형태를 취할 수 있다는 것을 보여주는 호기심이 더 많습니다 (저도 저도 있습니다 …)


답변

두 표준 (C ++ 2003 및 C ++ 11)은 네임 스페이스의 이름이 식별자라는 매우 명시 적입니다. 이는 명시 적 중첩 헤더가 필요함을 의미합니다.

네임 스페이스의 단순한 이름 외에 정규화 된 식별자를 배치하는 것은 큰 문제가 아니라는 느낌이 들지만 어떤 이유로 든 허용되지 않습니다.