128 votes

Existe-t-il une meilleure façon d'exprimer les espaces de noms imbriqués en C++ dans l'en-tête ?

Je suis passé de C++ à Java et C# et je pense que l'utilisation des espaces de noms/packages y est bien meilleure (bien structurée). Je suis ensuite 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 l'indentation profonde) :

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

Existe-t-il une manière plus courte d'exprimer ce qui précède ? Il me manque quelque chose comme

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

Mise à jour

Ok, certains disent que le concept d'utilisation en Java/C# et C++ est différent. Vraiment ? Je pense que le chargement (dynamique) des classes n'est pas la seule raison d'être des espaces de noms (il s'agit d'un point de vue très technique et raisonné). Pourquoi ne pas l'utiliser pour la lisibilité et la structuration, par exemple en pensant à "IntelliSense".

Actuellement, il n'y a pas de logique ou de colle entre un espace de noms et ce que l'on peut y trouver. Java et C# font cela beaucoup mieux... Pourquoi inclure <iostream> et ayant un espace de noms std ? Ok, si vous dites que la logique doit s'appuyer sur l'en-tête à inclure, pourquoi le #include n'utilise-t-il pas une syntaxe "IntelliSense" comme #include <std::io::stream> ou <std/io/stream> ? Je pense que l'absence de structuration dans les librairies par défaut est une des faiblesses du C++ par rapport à Java/C#.

Si l'unicité pour éviter les conflits est un point (qui est aussi 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'un côté, on dit que le C++ est le plus flexible... mais tout le monde a dit "ne faites 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 affirment 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 est logique et qu'elle est en effet plus profonde que 2 niveaux. Je pense que les plus grandes bibliothèques / projets ont besoin d'une imbrication plus profonde (je déteste les noms de classe comme ExtraLongClassNameBecauseEveryTthingIsInTheSameNameSpace... alors vous pourriez tout mettre dans l'espace de noms global, aussi...).

Mise à jour 3 - Conclusion

La plupart des gens disent qu'il ne faut pas le faire, mais... même 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 en utilisant des espaces de noms.

176voto

W1M0 Points 312

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

namespace A::B::C {
}

est équivalent à

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

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

34voto

Kurt Hutchinson Points 850

Pour éviter une mise en retrait trop profonde, je procède généralement de la manière suivante :

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

17voto

Max Truxa Points 1510

Je soutiens pleinement Réponse de peterchen mais je voudrais ajouter quelque chose qui répond à une autre partie de votre question.

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

Cela supprime également le besoin de commentaires à proximité de l'accolade fermante de l'espace de noms (avez-vous déjà fait défiler le texte jusqu'au bas d'un gros fichier source et essayé d'ajouter/supprimer/équilibrer des accolades pour lesquelles il manquait des commentaires sur l'accolade fermant l'espace de noms ? Ce n'est pas drôle).

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'espace de noms sur une seule ligne, vous pouvez également le faire avec un peu de magie (assez laide) 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 le faire :

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 de plus de trois niveaux, vous devrez ajouter des macros d'aide jusqu'au nombre souhaité.

13voto

Potatoswatter Points 70305

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

La norme s'efforce d'interdire l'utilisation des espaces de noms à la manière de Java. Par exemple, alias de l'espace 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 namespace nsc {} serait alors une erreur, car un espace de noms ne peut être défini qu'à l'aide de sa fonction espace-nom original . Pour l'essentiel, la norme facilite la tâche des utilisateur d'une telle bibliothèque, mais difficile pour les exécutant . Cela décourage les gens d'écrire de telles choses, mais en atténue les effets s'ils le font.

Vous devriez avoir un espace de noms par interface définie par un ensemble de classes et de fonctions apparentées. Les sous-interfaces internes ou optionnelles peuvent être placées dans des espaces de noms imbriqués. Mais plus de deux niveaux de profondeur devraient constituer un signal d'alarme très sérieux.

Envisagez d'utiliser des caractères de soulignement et des préfixes d'identification lorsque l'élément :: n'est pas nécessaire.

6voto

peterchen Points 21792

Non, et s'il vous plaît, ne faites pas cela.

L'objectif 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, un UpdateUI peut utiliser une méthode using namespace WndUI pour utiliser des symboles plus courts.

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

  • a importé des bibliothèques COM externes (principalement pour isoler les conflits d'en-tête entre les bibliothèques COM et les bibliothèques COM). #import y #include windows.h )
  • Un niveau d'espaces de noms "API publique" pour certains aspects (interface utilisateur, accès à la base de données, etc.)
  • les espaces de noms "Implementation Detail" qui ne font pas partie de l'API publique (espaces de noms anonymes dans les fichiers .cpp ou ModuleDetailHereBeTygers les espaces de noms dans librairies d'en-tête uniquement)
  • les enums sont le plus gros problème selon mon expérience. Ils polluent comme des fous.
  • Je continue à penser qu'il y a beaucoup trop d'espaces de noms.

Dans ce projet, les noms de classes, etc. utilisent un code de "région" à deux ou trois lettres (par ex. CDBNode au lieu de DB::CNode ). Si vous préférez cette dernière solution, il y a de la place pour un deuxième niveau d'espaces de noms "publics", mais pas plus.

Les enums spécifiques aux classes, etc. peuvent être membres de ces classes (bien que je reconnaisse que ce n'est pas toujours une bonne chose, et qu'il est parfois difficile de dire s'il faut le faire).

Un espace de noms "entreprise" est rarement nécessaire, sauf si vous rencontrez de gros problèmes avec des bibliothèques tierces distribuées sous forme binaire, qui ne fournissent pas leur propre espace de noms et qui ne peuvent pas être facilement placées dans un espace de noms (par exemple, dans une distribution binaire). Néanmoins, d'après mon expérience, forcer les dans un espace de noms est beaucoup plus facile à réaliser.


[modifier] Pour répondre à la question complémentaire de Stegi :

Ok, 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 est logique et qu'elle est en fait plus profonde que 2 niveaux seulement

Désolé de ne pas avoir été assez clair : Deux niveaux n'est pas une limite absolue, et plus n'est pas intrinsèquement mauvais. Je voulais juste souligner que vous avez rarement besoin plus de deux, d'après mon expérience, même sur une base de code importante. L'imbrication plus profonde ou plus superficielle est un compromis.

Le cas de Microsoft est sans doute différent. L'équipe est vraisemblablement beaucoup plus nombreuse et tous 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 à l'amélioration de la qualité de l'information. découvrabilité de la vaste bibliothèque. (.NET a environ 18000 types.)

Je suppose en outre qu'il existe un nombre optimal (ordre de grandeur) de symboles dans un espace de noms. Par exemple, 1 n'a pas de sens, 100 sonne bien, 10000 est clairement trop.


TL;DR : Il s'agit d'un compromis, et nous ne disposons pas de chiffres précis. Jouez la carte de la sécurité, n'exagérez dans aucune direction. Le "Ne faites pas ça" vient simplement du "Vous avez des problèmes avec ça, j'aurais des problèmes avec ça, et je ne vois pas pourquoi vous en auriez besoin".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X