Existe-t-il un meilleur moyen d'exprimer les espaces de noms imbriqués en C ++ dans l'en-tête


97

Je suis passé de C ++ à Java et C # et je pense que l'utilisation des espaces de noms / packages est bien meilleure ici (bien structurée). Ensuite, je suis revenu au C ++ et j'ai essayé d'utiliser les espaces de noms de la même manière, mais la syntaxe requise est horrible dans le fichier d'en-tête.

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

Ce qui suit me semble également étrange (pour éviter le retrait profond):

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

Existe-t-il un moyen plus court d'exprimer ce qui précède? Il me manque quelque chose comme

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

Mettre à jour

Ok, certains disent que le concept d'utilisation en Java / C # et C ++ est différent. Vraiment? Je pense que le chargement de classe (dynamique) n'est pas le seul but des espaces de noms (c'est une perspective raisonnée très technique). Pourquoi ne pas l'utiliser pour une lisibilité et une structuration, par exemple pensez à "IntelliSense".

Actuellement, il n'y a pas de logique / colle entre un espace de noms et ce que vous pouvez y trouver. Java et C # font cela beaucoup mieux ... Pourquoi inclure <iostream>et avoir un espace de noms std? Ok, si vous dites que la logique doit reposer sur l'en-tête à inclure, pourquoi le #include n'utilise-t-il pas une syntaxe conviviale "IntelliSense" comme #include <std::io::stream>ou<std/io/stream> ? Je pense que la structuration manquante dans les bibliothèques par défaut est une faiblesse de C ++ par rapport à Java / C #.

Si l'unicité des conflits avides est un point (qui est également un point de C # et Java), une bonne idée est d'utiliser le nom du projet ou le nom de l'entreprise comme espace de noms, ne pensez-vous pas?

D'une part, on dit que C ++ est le plus flexible ... mais tout le monde a dit "ne fais pas ça"? Il me semble que le C ++ peut faire beaucoup de choses mais a une syntaxe horrible même pour les choses les plus simples dans de nombreux cas par rapport au C #.

Mise à jour 2

La plupart des utilisateurs disent qu'il est absurde de créer une imbrication plus profonde que deux niveaux. Ok, alors qu'en est-il des espaces de noms Windows :: UI :: Xaml et Windows :: UI :: Xaml :: Controls :: Primitives dans le développement Win8? Je pense que l'utilisation des espaces de noms par Microsoft a du sens et qu'elle est en effet plus profonde que 2 niveaux. Je pense que les bibliothèques / projets plus importants ont besoin d'une imbrication plus profonde (je déteste les noms de classe comme ExtraLongClassNameBecauseEveryThingIsInTheSameNameSpace ... alors vous pouvez tout mettre dans l'espace de noms global aussi.)

Mise à jour 3 - Conclusion

La plupart disent "ne le faites pas", mais ... même le boost a une imbrication plus profonde qu'un ou deux niveaux. Oui, c'est une bibliothèque mais: Si vous voulez du code réutilisable - traitez votre propre code comme une bibliothèque que vous donneriez à quelqu'un d'autre. J'utilise également une imbrication plus profonde à des fins de découverte à l'aide d'espaces de noms.


3
S'agit-il d'un abus de namespacemot-clé?
Nawaz

4
les espaces de noms et les systèmes de modules c # / java n'ont pas le même objectif, vous ne devriez donc pas essayer de les utiliser de la même manière. et non, il n'y a pas de syntaxe plus simple, simplement parce que cela n'a aucun sens de fournir une syntaxe pour rendre les choses plus faciles à faire, ce qui n'est pas censé faire.
PlasmaHH

@PlasmaHH ... donc la faiblesse est la structuration manquante de la bibliothèque std de C ++? (voir mon exemple simple dans la mise à jour)
Beachwalker

@Stegi: Si vous pouvez donner des arguments solides pour expliquer pourquoi il manque et quels avantages solides nous tirerions d'une telle structuration, nous pourrions parler de faiblesses potentielles. Jusque-là, j'appellerais javas l'imbrication sans fin des paquets déroutante au mieux.
PlasmaHH

3
@PlasmaHH Intellisense et autres aides pour / après l'inclusion d'en-tête (package). Les grands projets au sein d'une même entreprise peuvent avoir besoin de plus d'une imbrication (par exemple, vw :: golflib :: io) pour une déclaration claire de ce qu'un espace de noms contient à quelle "portée". Eh bien, vous pouvez simplement utiliser vw :: mais si l'espace de noms est destiné à être utilisé pour éviter les conflits, pourquoi sont-ils si horribles à déclarer? Cela aboutit à un point où personne ne l'utilise ou n'utilise simplement un espace de noms avec une profondeur de un (comme souvent suggéré).
Beachwalker le

Réponses:


130

C ++ 17 pourrait simplifier la définition d'espaces de noms imbriqués:

namespace A::B::C {
}

est équivalent à

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

Voir (8) sur la page d'espace de noms sur cppreference:
http://en.cppreference.com/w/cpp/language/namespace


5
... activé par le commutateur du compilateur/std:c++latest
sunny moon

3
Notez que si vous utilisez /std:c++latestdans Visual Studio 2015 et que vous utilisez également Boost, vous pouvez rencontrer des erreurs de compilation très mystiques lorsque vous incluez des en-têtes Boost. J'ai rencontré ce problème comme décrit dans cette question
StackOverflow

1
Il fonctionne tel quel, espace de noms A :: B :: C. J'ai testé avec g ++ - 6.0
ervinbosenbacher


17

Je soutiens pleinement la réponse de Peterchen, mais je souhaite ajouter quelque chose qui répond à une autre partie de votre question.

La déclaration d'espaces de noms est l'un des très rares cas en C ++ où j'aime vraiment l'utilisation de #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

Cela supprime également le besoin de commentaires près de l'accolade fermante de l'espace de noms (avez-vous déjà fait défiler vers le bas d'un gros fichier source et essayé d'ajouter / supprimer / équilibrer les accolades qui manquaient de commentaires sur quelle accolade ferme quelle portée? .).

MY_COMPANY_BEGIN
MY_LIBRARY_BEGIN

class X { };

class Y { };

MY_LIBRARY_END
MY_COMPANY_END

Si vous voulez mettre toutes les déclarations d'espaces de noms sur une seule ligne, vous pouvez également le faire avec un peu de magie (assez moche) du préprocesseur:

// 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__)

Vous pouvez maintenant faire ceci:

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;

Pour une imbrication à plus de trois niveaux, vous devrez ajouter des macros d'assistance jusqu'au nombre souhaité.


Autant que je n'aime pas, #defineje suis assez impressionné par cette magie du préprocesseur ... seulement si je n'ai pas à ajouter de macros d'assistance supplémentaires pour une imbrication plus profonde ... eh bien, je ne vais pas l'utiliser de toute façon, donc .. .
galdin

12

Les espaces de noms C ++ sont utilisés pour regrouper les interfaces, non pour diviser les composants ou exprimer la division politique.

La norme fait tout son possible pour interdire l'utilisation d'espaces de noms de type Java. Par exemple, les alias d'espaces de noms permettent d'utiliser facilement des noms d'espaces de noms longs ou profondément imbriqués.

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

namespace nsc = a::b::c;

Mais ce namespace nsc {}serait alors une erreur, car un espace de noms ne peut être défini qu'en utilisant son nom d'espace de noms d'origine . Essentiellement, la norme rend les choses faciles pour l' utilisateur d'une telle bibliothèque mais difficile pour l' implémenteur . Cela décourage les gens d'écrire de telles choses mais atténue les effets s'ils le font.

Vous devez avoir un espace de noms par interface défini par un ensemble de classes et de fonctions associées. Des sous-interfaces internes ou facultatives peuvent entrer dans des espaces de noms imbriqués. Mais plus de deux niveaux de profondeur devraient être un signal d'alarme très grave.

Pensez à utiliser des caractères de soulignement et des préfixes d'identifiant lorsque l' ::opérateur n'est pas nécessaire.


17
Ok, alors qu'en est-il des espaces de noms Windows :: UI :: Xaml et Windows :: UI :: Xaml :: Controls :: Primitives dans le développement Win8? Je pense que l'utilisation des espaces de noms par Microsoft a du sens et qu'elle est en effet plus profonde que 2 niveaux.
Beachwalker

2
Utiliser moins de 2 niveaux est un drapeau rouge et utiliser 3 ou 4 est parfaitement bien. Essayer d'obtenir une hiérarchie d'espaces de noms plate quand cela n'a pas de sens va à l'encontre de l'objectif même des espaces de noms - éviter les conflits de noms. Je suis d'accord que vous devriez avoir un niveau pour une interface et un autre pour les sous-interfaces et les internes. Mais autour de cela, vous avez besoin d'au moins un niveau supplémentaire pour l'espace de noms de l'entreprise (pour les petites et moyennes entreprises) ou deux pour l'entreprise et la division (pour les grandes entreprises). Sinon, les espaces de noms de votre interface entreront en conflit avec ceux d'autres interfaces portant le même nom développées ailleurs
Kaiserludi

@Kaiserludi Quel est l'avantage technique de company::divisionover company_division?
Potatoswatter

@Potatoswatter Dans la société :: anotherDivsion, vous pouvez simplement utiliser la «division» plus courte. pour faire référence à company :: division même dans les en-têtes où vous devriez fortement éviter de polluer les espaces de noms de niveau supérieur en utilisant 'using namespace'. En dehors de l'espace de noms de l'entreprise, vous pouvez toujours faire une 'utilisation de la société d'espace de noms; lorsque les noms de division n'entrent pas en collision avec d'autres espaces de noms dans votre portée, mais lorsque les noms d'interface à l'intérieur de certains espaces de noms de division entrent en collision, de sorte que vous ne pouvez pas faire 'using namespace company_division;'.
Kaiserludi

3
@Potatoswatter Le fait est que vous l'obtenez pratiquement gratuitement (company :: division n'est pas plus long que company_division) et que vous n'avez pas à définir d'abord un alias d'espace de noms supplémentaire pour l'utiliser.
Kaiserludi

6

Non, et ne faites pas ça.

Le but des espaces de noms est principalement de résoudre les conflits dans l'espace de noms global.

Un objectif secondaire est l'abréviation locale des symboles; par exemple, une UpdateUIméthode complexe peut utiliser un using namespace WndUIpour utiliser des symboles plus courts.

Je suis sur un projet 1.3MLoc, et les seuls espaces de noms dont nous disposons sont:

  • bibliothèques COM externes importées (principalement pour isoler les conflits d'en-tête entre #importet #include windows.h)
  • Un niveau d'espaces de noms «API publics» pour certains aspects (UI, accès à la base de données, etc.)
  • Espaces de noms «Détails d'implémentation» qui ne font pas partie de l'API publique (espaces de noms anonymes dans .cpp, ou ModuleDetailHereBeTygersespaces de noms dans les bibliothèques d'en-tête uniquement)
  • les énumérations sont le plus gros problème de mon expérience. Ils polluent comme des fous.
  • J'ai toujours l'impression que c'est trop d'espaces de noms

Dans ce projet, les noms de classe, etc. utilisent un code «région» à deux ou trois lettres (par exemple CDBNodeau lieu de DB::CNode). Si vous préférez ce dernier, il y a de la place pour un deuxième niveau d'espaces de noms "publics", mais pas plus.

Les énumérations spécifiques à une classe, etc. peuvent être membres de ces classes (même si je suis d'accord que ce n'est pas toujours bon, et il est parfois difficile de dire si vous devriez)

Il y a rarement besoin d'un espace de noms "entreprise" non plus, sauf si vous rencontrez de gros problèmes avec des bibliothèques tierces qui sont distribuées en tant que binaire, ne fournissent pas leur propre espace de noms et ne peuvent pas être facilement mises en un (par exemple, dans un binaire Distribution). Pourtant, dans mon expérience forçant les dans un espace de noms est beaucoup plus facile à faire.


[modifier] Selon la question de suivi de Stegi:

Ok, alors qu'en est-il des espaces de noms Windows :: UI :: Xaml et Windows :: UI :: Xaml :: Controls :: Primitives dans le développement Win8? Je pense que l'utilisation des espaces de noms par Microsoft a du sens et qu'elle est en effet plus profonde que 2 niveaux

Désolé si je n'étais pas assez clair: deux niveaux ne sont pas une limite stricte, et plus n'est pas intrinsèquement mauvais. Je voulais juste souligner que vous en avez rarement besoin de plus de deux, d'après mon expérience, même sur une grande base de code. Une nidification plus profonde ou moins profonde est un compromis.

Maintenant, le cas de Microsoft est sans doute différent. Vraisemblablement une équipe beaucoup plus grande, et tout le code est une bibliothèque.

Je suppose que Microsoft imite ici le succès de la bibliothèque .NET, où les espaces de noms contribuent à la découvrabilité de la bibliothèque étendue. (.NET a environ 18000 types.)

Je suppose en outre qu'il existe un (ordre de grandeur de) symboles optimal dans un espace de noms. dire, 1 n'a pas de sens, 100 sons corrects, 10000 c'est clairement trop.


TL; DR: C'est un compromis, et nous n'avons pas de chiffres précis. Jouez prudemment, n'en faites pas trop dans aucune direction. Le "Ne fais pas ça" vient simplement du "Tu as des problèmes avec ça, j'aurais des problèmes avec ça, et je ne vois pas pourquoi tu en aurais besoin.".


8
Ok, alors qu'en est-il des espaces de noms Windows :: UI :: Xaml et Windows :: UI :: Xaml :: Controls :: Primitives dans le développement Win8? Je pense que l'utilisation des espaces de noms par Microsoft a du sens et qu'elle est en effet plus profonde que 2 niveaux.
Beachwalker

2
Si j'ai besoin de constantes d'accès global, j'aime les mettre dans un espace de noms avec un nom comme Constants, puis créer des espaces de noms imbriqués avec des noms appropriés pour catégoriser les constantes; si nécessaire, j'utilise ensuite d'autres espaces de noms pour éviter les collisions de noms. Cet Constantsespace de noms est lui-même contenu dans un espace de noms fourre-tout pour le code système du programme, avec un nom tel que SysData. Cela crée un nom complet contenant trois ou quatre espaces de noms ( par exemple SysData::Constants::ErrorMessages, SysData::Constants::Ailments::Bitflagsou SysData::Defaults::Engine::TextSystem).
Justin Time - Réintègre Monica le

1
Lorsque les constantes sont requises dans le code réel, toute fonction qui en a besoin utilise une usingdirective pour apporter les noms appropriés, minimisant ainsi la possibilité de conflits de noms. Je trouve que cela améliore la lisibilité et aide à documenter les dépendances de tout bloc de code donné. En dehors des constantes, j'ai tendance à essayer de le conserver dans deux espaces de noms si possible (tels que SysData::Exceptionset SysData::Classes).
Justin Time - Réintègre Monica le

2
Dans l'ensemble, je dirais que dans les cas généraux, il est préférable d'utiliser un nombre minimal d'espaces de noms imbriqués, mais si vous avez besoin d'objets globaux pour une raison quelconque (qu'ils soient constants ou mutables, de préférence le premier), plusieurs espaces de noms imbriqués doivent être utilisés pour les séparer en catégories appropriées, à la fois pour documenter leur utilisation et minimiser les éventuelles collisions de noms.
Justin Time - Réintègre Monica le

2
-1 pour "veuillez ne pas faire cela" sans raisons objectives (malgré les clarifications ultérieures). Le langage prend en charge les espaces de noms imbriqués et un projet peut avoir de bonnes raisons de les utiliser. Une discussion sur d'éventuelles raisons de ce genre et de tout inconvénient concret et objectif à le faire annulerait mon vote défavorable.
TypeIA

4

Voici une citation de la documentation Lzz (Lazy C ++):

Lzz reconnaît les constructions C ++ suivantes:

définition de l'espace de noms

Un espace de noms sans nom et toutes les déclarations incluses sont générés dans le fichier source. Cette règle remplace toutes les autres.

Le nom d'un espace de noms nommé peut être qualifié.

   namespace A::B { typedef int I; }

est équivalent à:

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

Bien sûr la qualité des sources qui dépend de tels outils est discutable ... Je dirais que c'est plus une curiosité, montrer que la maladie de syntaxe induite par C ++ peut prendre plusieurs formes (j'ai la mienne aussi ...)


2

Les deux normes (C ++ 2003 et C ++ 11) sont très explicites que le nom de l'espace de noms est un identifiant. Cela signifie que des en-têtes imbriqués explicites sont requis.

J'ai l'impression que ce n'est pas un gros problème de permettre de placer un identifiant qualifié en plus d'un simple nom de l'espace de noms, mais pour une raison quelconque, cela n'est pas autorisé.


1

Vous pouvez utiliser cette syntaxe:

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 {
    ...
};

Notez que cette syntaxe est valide même en C ++ 98 et qu'elle est presque similaire à ce qui est maintenant disponible en C ++ 17 avec des définitions d'espaces de noms imbriqués .

Heureux sans nidification!

Sources:


C'est la syntaxe mentionnée dans la question où une meilleure solution est recherchée à la place. Maintenant, avec C ++ 17 est une alternative valide disponible comme indiqué par la réponse acceptée. Désolé, je suis contre le fait de ne pas avoir lu la question et la réponse.
Beachwalker

@Beachwalker ne nous attrapons pas sur la syntaxe. La déclaration d'espace de noms ci-dessus pourrait aussi bien être la même que dans la réponse acceptée. Le point principal que je voulais souligner par cette réponse est ce que l'OP avait dit qu'il avait manqué, et ce que j'ai fait en dessous de ce désordre d'espace de noms. Pour autant que je puisse voir, tout le monde semble s'être concentré sur la déclaration de tout dans l'espace de noms, alors que ma réponse vous sort du désordre imbriqué et je suis sûr qu'OP aurait apprécié cette syntaxe si quelqu'un l'avait mentionné il y a 4 ans lorsque cette question a été d'abord demandé.
smac89

1

Cet article couvre assez bien le sujet: Papier d'espace de noms

Ce qui résume fondamentalement cela. Plus vos espaces de noms sont longs, plus il y a de chances que les gens utilisent leusing namespace directive.

Donc, en regardant le code suivant, vous pouvez voir un exemple où cela vous blessera:

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{};
}

Ce code se compilera bien, cependant, si vous décommentez la ligne, //using namespace def;l'espace de noms "testing" deviendra ambigu et vous aurez des conflits de noms. Cela signifie que votre base de code peut passer de stable à instable en incluant une bibliothèque tierce.

En C #, même si vous deviez utiliser using abc;et que using def;le compilateur est capable de reconnaître cela testing::myClassou même myClassest seulement dans l' abc::testingespace de noms, mais C ++ ne le reconnaîtra pas et il est détecté comme une collision.


0

Oui, vous devrez le faire comme

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

Cependant, vous essayez d'utiliser les espaces de noms d'une manière qu'ils ne sont pas censés être utilisés. Vérifiez cette question, vous la trouverez peut-être utile.


-1

[EDIT:]
Étant donné que les espaces de noms imbriqués c ++ 17 sont pris en charge en tant que fonctionnalité de langage standard ( https://en.wikipedia.org/wiki/C%2B%2B17 ). À partir de maintenant, cette fonctionnalité n'est pas prise en charge dans g ++ 8, mais elle peut être trouvée dans le compilateur in clang ++ 6.0.


[CONCLUSION:]
Utilisez clang++6.0 -std=c++17comme commande de compilation par défaut. Ensuite, tout devrait bien fonctionner - et vous pourrez compiler avec namespace OuterNS::InnerNS1::InnerNS2 { ... }dans vos fichiers.


[RÉPONSE ORIGINALE:]
Puisque cette question est un peu ancienne, je suppose que vous avez évolué. Mais pour d'autres, qui cherchent encore une réponse, j'ai eu l'idée suivante:

Tampons Emacs affichant le fichier principal, les fichiers d'espace de noms, la commande / résultat de compilation et l'exécution en ligne de commande.

(Puis-je faire une publicité pour Emacs ici :)?) Publier une image est beaucoup plus facile et plus lisible que de simplement poster du code. Je n'ai pas l'intention de fournir une réponse complète de tous les cas d'angle, je voulais simplement donner de l'inspiration. (Je supporte totalement C # et pense que dans de nombreux cas, C ++ devrait adopter certaines fonctionnalités de la POO, car C # est populaire principalement en raison de sa facilité d'utilisation comparée).


Mise à jour: Puisque C ++ 17 a un espace de noms imbriqué ( nuonsoft.com/blog/2017/08/01/c17-nested-namespaces ), il semblerait que ma réponse ne soit plus pertinente, sauf si vous utilisez des versions plus anciennes de C ++ .
ワ イ き ん ぐ

1
Même si j'aime Emacs, publier une image n'est pas idéal. Il échappe à la recherche / indexation de texte et rend également votre réponse difficile d'accès pour les visiteurs malvoyants.
Heinrich soutient Monica le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.