Przerzuciłem się z C ++ na Javę i C # i uważam, że użycie przestrzeni nazw / pakietów jest tam znacznie lepsze (dobrze zorganizowane). Potem wróciłem do C ++ i próbowałem użyć przestrzeni nazw w ten sam sposób, ale wymagana składnia jest okropna w pliku nagłówkowym.
namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
public class MyClass
Poniższe również wydaje mi się dziwne (aby uniknąć głębokiego wcięcia):
namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
public class MyClass
{
Czy istnieje krótszy sposób wyrażenia powyższej rzeczy? Brakuje mi czegoś takiego
namespace MyCompany::MyModule::MyModulePart::...
{
public class MyClass
Aktualizacja
Ok, niektórzy twierdzą, że koncepcja użycia w Javie / C # i C ++ jest inna. Naprawdę? Myślę, że (dynamiczne) ładowanie klas nie jest jedynym celem przestrzeni nazw (jest to bardzo techniczna perspektywa). Dlaczego nie miałbym go używać ze względu na czytelność i strukturę, np. Pomyśl o „IntelliSense”.
Obecnie nie ma logiki / spoiwa między przestrzenią nazw a tym, co można tam znaleźć. Java i C # robią to znacznie lepiej ... Po co dołączać <iostream>
i mieć przestrzeń nazw std
? Ok, jeśli powiesz, że logika powinna opierać się na nagłówku, dlaczego #include nie używa przyjaznej składni „IntelliSense”, takiej jak #include <std::io::stream>
lub <std/io/stream>
? Myślę, że brakująca strukturalizacja w domyślnych bibliotekach jest jedną ze słabości C ++ w porównaniu z Javą / C #.
Jeśli wyjątkowość dla zajadłych konfliktów jest jednym punktem (który jest także punktem C # i Java), dobrym pomysłem jest użycie nazwy projektu lub nazwy firmy jako przestrzeni nazw, nie sądzisz?
Z jednej strony mówi się, że C ++ jest najbardziej elastyczny ... ale wszyscy mówili „nie rób tego”? Wydaje mi się, że C ++ może robić wiele rzeczy, ale ma okropną składnię, nawet w przypadku najłatwiejszych rzeczy w wielu przypadkach w porównaniu z C #.
Zaktualizuj 2
Większość użytkowników twierdzi, że tworzenie zagnieżdżenia głębszego niż dwa poziomy jest nonsensem. Ok, a co z przestrzeniami nazw Windows :: UI :: Xaml i Windows :: UI :: Xaml :: Controls :: Primitives w programowaniu pod Win8? Myślę, że użycie przestrzeni nazw przez Microsoft ma sens i jest rzeczywiście głębsze niż tylko 2 poziomy. Myślę, że większe biblioteki / projekty wymagają głębszego zagnieżdżenia (nienawidzę nazw klas, takich jak ExtraLongClassNameBecauseEveryThingIsInTheSameNameSpace ... wtedy też możesz umieścić wszystko w globalnej przestrzeni nazw).
Aktualizacja 3 - Zakończenie
Większość mówi „nie rób tego”, ale ... nawet wzmocnienie ma głębsze zagnieżdżenie niż jeden lub dwa poziomy. Tak, jest to biblioteka, ale: Jeśli chcesz kod wielokrotnego użytku - traktuj swój własny kod jak bibliotekę, którą dałbyś komuś innemu. Używam też głębszego zagnieżdżenia do celów wykrywania przy użyciu przestrzeni nazw.
źródło
namespace
słowa kluczowego?Odpowiedzi:
C ++ 17 może uprościć zagnieżdżoną definicję przestrzeni nazw:
namespace A::B::C { }
jest równa
namespace A { namespace B { namespace C { } } }
Zobacz (8) na stronie przestrzeni nazw w cppreference:
http://en.cppreference.com/w/cpp/language/namespace
źródło
/std:c++latest
/std:c++latest
w programie Visual Studio 2015, a także korzystać z funkcji Boost, możesz napotkać bardzo mistyczne błędy kompilatora po dołączeniu niektórych nagłówków Boost. Napotkałem ten problem, jak opisano w tym pytaniu StackOverflowAby uniknąć naprawdę głębokich wcięć, zwykle robię to w ten sposób:
namespace A { namespace B { namespace C { class X { // ... }; }}}
źródło
clang-format
nie może tego sformatować, ponieważ pokazujesz clang.llvm.org/docs/ClangFormatStyleOptions.html (NamespaceIndentation)W pełni popieram odpowiedź Peterchena, ale chcę dodać coś, co dotyczy innej części twojego pytania.
Deklarowanie przestrzeni nazw jest jednym z bardzo rzadkich przypadków w C ++, w których lubię używać
#define
s.#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
Eliminuje to również potrzebę umieszczania komentarzy w pobliżu zamykającego nawiasu klamrowego przestrzeni nazw (czy kiedykolwiek przewinąłeś w dół do samego końca dużego pliku źródłowego i próbowałeś dodać / usunąć / zrównoważyć nawiasy klamrowe, w których brakowało komentarzy na temat tego, który nawias zamyka który zakres? .).
MY_COMPANY_BEGIN MY_LIBRARY_BEGIN class X { }; class Y { }; MY_LIBRARY_END MY_COMPANY_END
Jeśli chcesz umieścić wszystkie deklaracje przestrzeni nazw w jednej linii, możesz to również zrobić z odrobiną (dość brzydkiej) magii preprocesora:
// 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__)
Teraz możesz to zrobić:
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;
W przypadku zagnieżdżenia głębszego niż trzy poziomy należy dodać makra pomocnicze do żądanej liczby.
źródło
#define
Chociaż nie lubię tego, jestem pod wrażeniem tej magii preprocesora ... tylko jeśli nie będę musiał dodawać dodatkowych makr pomocniczych do głębszego zagnieżdżania ... ,Przestrzenie nazw C ++ służą do grupowania interfejsów, a nie do dzielenia komponentów lub wyrażania podziałów politycznych.
Standard robi wszystko, co w jego mocy, aby zabraniać używania przestrzeni nazw w stylu Java. Na przykład aliasy przestrzeni nazw umożliwiają łatwe używanie głęboko zagnieżdżonych lub długich nazw przestrzeni nazw.
namespace a { namespace b { namespace c {} } } namespace nsc = a::b::c;
Ale
namespace nsc {}
wtedy byłby to błąd, ponieważ przestrzeń nazw może być zdefiniowana tylko przy użyciu jej oryginalnej nazwy przestrzeni nazw . Zasadniczo standard ułatwia życie użytkownikowi takiej biblioteki, ale utrudnia wdrażanie . To zniechęca ludzi do pisania takich rzeczy, ale łagodzi skutki, jeśli to zrobią.Powinieneś mieć jedną przestrzeń nazw na interfejs zdefiniowaną przez zestaw powiązanych klas i funkcji. Wewnętrzne lub opcjonalne interfejsy podrzędne mogą trafiać do zagnieżdżonych przestrzeni nazw. Ale głębokość więcej niż dwa poziomy powinna być bardzo poważną czerwoną flagą.
Rozważ użycie znaków podkreślenia i przedrostków identyfikatorów, jeśli
::
operator nie jest potrzebny.źródło
company::division
nadcompany_division
?Nie, i proszę, nie rób tego.
Celem przestrzeni nazw jest przede wszystkim rozwiązywanie konfliktów w globalnej przestrzeni nazw.
Dodatkowym celem jest lokalny skrót symboli; np. w złożonej
UpdateUI
metodzie możnausing namespace WndUI
użyć krótszych symboli.Pracuję nad projektem 1.3 MLoc i jedyne przestrzenie nazw, które mamy, to:
#import
i#include windows.h
)ModuleDetailHereBeTygers
przestrzenie nazw w bibliotekach tylko z nagłówkiem)W tym projekcie nazwy klas itp. Używają dwu- lub trzyliterowego kodu regionu (np.
CDBNode
ZamiastDB::CNode
). Jeśli wolisz to drugie, jest miejsce na drugi poziom „publicznych” przestrzeni nazw, ale nic więcej.Wyliczenia specyficzne dla klasy itp. Mogą być członkami tych klas (chociaż zgadzam się, że nie zawsze jest to dobre i czasami trudno jest powiedzieć, czy powinieneś)
Rzadko też jest potrzebna przestrzeń nazw „firmowa”, chyba że masz duże problemy z bibliotekami innych firm, które są rozprowadzane jako binarne, nie dostarczają własnej przestrzeni nazw i nie można ich łatwo umieścić w jednej (np. W pliku binarnym dystrybucja). Jednak z mojego doświadczenia wynika , że zmuszanie ich do przestrzeni nazw jest znacznie łatwiejsze do wykonania.
[edytuj] Zgodnie z pytaniem uzupełniającym Stegi:
Przepraszam, jeśli nie byłam wystarczająco jasna: dwa poziomy nie są sztywnym limitem, a więcej nie jest wewnętrznie złe. Chciałem tylko zaznaczyć, że rzadko potrzebujesz więcej niż dwóch, z mojego doświadczenia, nawet w przypadku dużej bazy kodu. Zagnieżdżanie głębsze lub płytsze to kompromis.
Teraz sprawa Microsoftu jest prawdopodobnie inna. Przypuszczalnie dużo większy zespół, a cały kod to biblioteka.
Zakładam, że Microsoft naśladuje tutaj sukces biblioteki .NET, w której przestrzenie nazw przyczyniają się do wykrywalności obszernej biblioteki. (.NET ma około 18000 typów).
Zakładałbym ponadto, że w przestrzeni nazw istnieje optymalny (rząd wielkości) symboli. powiedzmy, 1 nie ma sensu, 100 dźwięków dobrze, 10000 to zdecydowanie za dużo.
TL; DR: To kompromis i nie mamy twardych liczb. Graj ostrożnie, nie przesadzaj w żadnym kierunku. „Nie rób tego” pochodzi po prostu z „Masz z tym problemy, miałbym z tym problemy i nie widzę powodu, dla którego byś tego potrzebował”.
źródło
Constants
, a następnie tworzyć zagnieżdżone przestrzenie nazw z odpowiednimi nazwami, aby kategoryzować stałe; w razie potrzeby używam dalszych przestrzeni nazw, aby zapobiec kolizji nazw. TaConstants
przestrzeń nazw jest sama w sobie zawarta w przestrzeni nazw typu catch-all dla kodu systemowego programu, o nazwie takiej jakSysData
. To tworzy pełną nazwę zawierającą trzy lub cztery przestrzenie nazw (takich jakSysData::Constants::ErrorMessages
,SysData::Constants::Ailments::Bitflags
lubSysData::Defaults::Engine::TextSystem
).using
dyrektywy w celu wprowadzenia odpowiednich nazw, minimalizując możliwość wystąpienia konfliktów nazw. Uważam, że poprawia czytelność i pomaga dokumentować zależności dowolnego bloku kodu. Oprócz stałych staram się, jeśli to możliwe, trzymać go w dwóch przestrzeniach nazw (takich jakSysData::Exceptions
iSysData::Classes
).Oto cytat z dokumentacji Lzz (Lazy C ++):
Oczywiście jakość źródeł zależnych od takich narzędzi jest dyskusyjna ... Powiedziałbym, że to raczej ciekawostka, pokazująca, że choroba składni wywołana przez C ++ może przybierać różne formy (ja też mam swoją ...)
źródło
Oba standardy (C ++ 2003 i C ++ 11) jasno mówią, że nazwa przestrzeni nazw jest identyfikatorem. Oznacza to, że wymagane są jawne zagnieżdżone nagłówki.
Mam wrażenie, że nie jest to wielka sprawa, aby umożliwić umieszczenie kwalifikowanego identyfikatora oprócz prostej nazwy przestrzeni nazw, ale z jakiegoś powodu jest to niedozwolone.
źródło
Ten artykuł dość dobrze porusza temat: Namespace Paper
Co w zasadzie sprowadza się do tego. Im dłuższe przestrzenie nazw, tym większa szansa, że ludzie będą używać
using namespace
dyrektywy.Patrząc na poniższy kod, możesz zobaczyć przykład, w którym to cię zrani:
namespace abc { namespace testing { class myClass {}; }} namespace def { namespace testing { class defClass { }; }} using namespace abc; //using namespace def; int main(int, char**) { testing::myClass classInit{}; }
Ten kod skompiluje się dobrze, jednak jeśli odkomentujesz wiersz,
//using namespace def;
wówczas przestrzeń nazw „testing” stanie się niejednoznaczna i wystąpią kolizje nazw. Oznacza to, że baza kodu może zmienić się ze stabilnej na niestabilną, włączając bibliotekę innej firmy.W C #, nawet jeśli miałbyś używać
using abc;
iusing def;
kompilator jest w stanie to rozpoznaćtesting::myClass
lub po prostumyClass
znajduje się tylko wabc::testing
przestrzeni nazw, ale C ++ nie rozpozna tego i zostanie wykryty jako kolizja.źródło
Tak, będziesz musiał to zrobić jak
namespace A{ namespace B{ namespace C{} } }
Jednak próbujesz używać przestrzeni nazw w sposób, w jaki nie powinny być używane. Sprawdź to pytanie, może uznasz to za przydatne.
źródło
Możesz użyć tej składni:
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 { ... };
Zwróć uwagę, że ta składnia jest poprawna nawet w C ++ 98 i jest prawie podobna do tego, co jest teraz dostępne w C ++ 17 z zagnieżdżonymi definicjami przestrzeni nazw .
Miłego unnesting!
Źródła:
źródło
[EDYCJA:]
Ponieważ zagnieżdżone przestrzenie nazw c ++ 17 są obsługiwane jako standardowa funkcja języka ( https://en.wikipedia.org/wiki/C%2B%2B17 ). Obecnie ta funkcja nie jest obsługiwana w g ++ 8, ale można ją znaleźć w kompilatorze w clang ++ 6.0.
[WNIOSEK:]
Użyj
clang++6.0 -std=c++17
jako domyślnego polecenia kompilacji. Wtedy wszystko powinno działać dobrze - i będziesz mógł skompilować sięnamespace OuterNS::InnerNS1::InnerNS2 { ... }
w swoich plikach.[ORYGINALNA ODPOWIEDŹ:]
Ponieważ to pytanie jest trochę stare, zakładam, że poszedłeś dalej. Ale dla innych, którzy wciąż szukają odpowiedzi, wpadłem na następujący pomysł:
(Czy mogę zrobić reklamę dla Emacsa tutaj :)?) Umieszczanie obrazu jest o wiele łatwiejsze i bardziej czytelne niż po prostu kod pocztowy. Nie mam zamiaru przedstawiać wyczerpującej odpowiedzi na wszystkie narożne sprawy, po prostu chciałem dać trochę inspiracji. (Całkowicie popieram C # i uważam, że w wielu przypadkach C ++ powinien przyjąć pewne funkcje OOP, ponieważ C # jest popularny głównie ze względu na porównywalną łatwość użycia).
źródło