web-dev-qa-db-fra.com

Existe-t-il un meilleur moyen d’exprimer des espaces de nom 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 est bien meilleure ici (bien structurée). Ensuite, je suis revenu en C++ et j'ai essayé d'utiliser les espaces de noms de la même 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
     {

Y a-t-il un moyen plus court d'exprimer la chose ci-dessus? Il me manque quelque chose comme

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

Mise à jour

D'accord, certains disent que le concept d'utilisation en Java/C # et en 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 devrais-je pas l'utiliser pour une lisibilité et une structuration, par exemple "IntelliSense".

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

Si l'unicité des conflits évidents 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 la société comme espace de noms, vous ne le pensez pas?

D'un côté, on dit que le 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 que sa syntaxe est horrible, même pour les choses les plus faciles dans de nombreux cas par rapport au C #.

Mise à jour 2

La plupart des utilisateurs disent qu'il est insensé de créer une imbrication plus profonde que deux niveaux. Ok, qu’en est-il donc de 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 va bien au-delà de 2 niveaux. Je pense que les plus grandes bibliothèques/projets ont besoin d'une imbrication plus profonde (je déteste les noms de classes comme ExtraLongClassNameBecauseEveryThingIsInTheSameNameSpace ... alors vous pouvez tout mettre dans l'espace de noms global également.)

Mise à jour 3 - Conclusion

La plupart disent "ne le fais pas", 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 voudriez donner à quelqu'un d'autre. J'utilise également une imbrication plus profonde à des fins de découverte à l'aide d'espaces de nommage.

84
Beachwalker

C++ 17 pourrait simplifier la définition de l'espace de noms imbriqué:

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

108
W1M0R

Pour éviter une indentation vraiment profonde, je le fais habituellement de cette façon:

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

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

Déclarer les espaces de noms est l’un des très rares cas en C++ où j’aime bien utiliser #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 nom (avez-vous déjà défilé jusqu'au bas d'un fichier source volumineux et avez essayé d'ajouter/supprimer/équilibrer des accolades qui manquaient de commentaires sur l'accolade ferme quelle étendue? Non amusant .).

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 le faire aussi avec un peu de magie (assez moche) de 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__)

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

13
Max Truxa

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 fait tout son possible pour interdire l'utilisation d'espaces de nommage de type Java. Par exemple, alias d'espaces de noms permet d'utiliser facilement des noms d'espaces de noms longs et 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 nom ne peut être défini qu’à l’aide de son nom-espace-nom-original. Essentiellement, le standard facilite les choses pour tilisateur d'une telle bibliothèque, mais difficile pour implémenteur. Cela décourage les gens d'écrire de telles choses mais en 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 aller dans des espaces de noms imbriqués. Mais plus de deux niveaux de profondeur devraient constituer un drapeau rouge très sérieux.

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

12
Potatoswatter

Non, et s'il te plaît, ne fais 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 méthode complexe UpdateUI peut utiliser un 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:

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

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

Les enums spécifiques à une classe, etc. peuvent être membres de ces classes (bien que je convienne que ce n'est pas toujours bon et qu'il est parfois difficile de dire si vous devriez le faire)

Un espace de noms "société" est rarement nécessaire non plus, sauf si vous rencontrez de gros problèmes avec des bibliothèques tierces distribuées sous forme binaire, ne fournissant pas leur propre espace de noms et ne pouvant pas être facilement insérées dans un seul (par exemple, dans un fichier binaire). Distribution). Néanmoins, dans mon expérience, il est beaucoup plus facile de forcer eux dans un espace de noms.


[edit] Selon la question de suivi de Stegi:

Ok, qu’en est-il donc de Windows :: UI :: Xaml et de Windows :: UI :: Xaml :: Controls :: Primitives dans le développement Win8? Je pense que l'utilisation des espaces de noms par Microsoft est logique et va bien au-delà de 2 niveaux

Désolé si je n'ai pas été assez clair: deux niveaux ne sont pas une limite stricte et davantage n'est pas intrinsèquement mauvais. Je voulais juste souligner que vous avez rarement besoin de plus de deux, de mon expérience, même sur une base de code volumineuse. Nidification plus profonde ou plus peu profonde est un compromis.

Aujourd'hui, l'affaire Microsoft est sans doute différente. Vraisemblablement une équipe beaucoup plus grande 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 à la découvrabilité de la bibliothèque étendue. (.NET a environ 18 0 types.)

Je suppose en outre qu’il existe un ordre de grandeur optimal des symboles dans un espace de noms. disons: 1 n'a pas de sens, 100 sons, 10000 est clairement trop.


TL; DR: C'est un compromis, et nous n'avons pas de chiffres précis. Soyez prudent, n'en faites pas trop dans n'importe quelle direction. Le "Ne fais pas ça" vient simplement du "Tu as des problèmes avec ça, je pourrais avoir des problèmes avec ça, et je ne vois pas de raison pour laquelle tu en aurais besoin.".

7
peterchen

Voici une citation de Lzz (Lazy C++) docs:

Lzz reconnaît les constructions C++ suivantes:

définition de l'espace de noms

Un espace de nom non nommé 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épendent de tels outils est discutable ... Je dirais que c'est plus une curiosité, montrant que la maladie syntaxique induite par C++ peut prendre de nombreuses formes (j'ai aussi la mienne ...)

4
CapelliC

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

Mon impression que ce n’est pas grave de permettre de placer un identifiant qualifié en plus d’un simple nom de l’espace de nom, mais pour une raison quelconque, cela n’est pas autorisé.

2
Kirill Kobelev

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 valable même en C++ 98 et qu'elle est presque similaire à celle qui est maintenant disponible en C++ 17 avec définitions d'espace de nom imbriqué .

Bonne annulation!

Sources:

2
smac89

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 this question, peut-être que vous le trouverez utile.

1
SingerOfTheFall

[EDIT:]
Depuis c ++ 17, les espaces de noms imbriqués sont pris en charge en tant que fonctionnalité de langage standard ( https://en.wikipedia.org/wiki/C%2B%2B17 ). Pour l'instant, cette fonctionnalité n'est pas prise en charge dans g ++ 8, mais on peut la trouver dans le compilateur in clang ++ 6.0.


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


[RÉPONSE ORIGINALE:]
Comme cette question est un peu ancienne, je suppose que vous êtes passé à autre chose. Mais pour d'autres, qui cherchent encore une réponse, j'ai eu l'idée suivante:

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

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

0
Alexander