2010-05-19 8 views
16

Edit: Ich weiß, dass Methode 1 im Wesentlichen ungültig ist und wahrscheinlich Methode 2 verwenden wird, aber ich bin auf der Suche nach dem besten Hack oder einer besseren Lösung zur Minderung der grassierenden, veränderlichen Namespace-Proliferation.Abschirmung #include in Namespace {} Block?

Ich habe mehrere Klassen- oder Methodendefinitionen in einem Namespace, die unterschiedliche Abhängigkeiten haben und die wenigsten Namespace-Blöcke oder explizite Abtastungen verwenden möchten, aber gleichzeitig #include-Direktiven mit den Definitionen gruppieren, die sie so gut wie möglich benötigen. Ich habe nie einen Hinweis darauf gesehen, dass einem Präprozessor gesagt werden könnte, dass er das Namespace {} von #include-Inhalten ausschließt, aber ich bin hier, um zu fragen, ob etwas Ähnliches möglich ist: (siehe unten, um zu erklären, warum ich etwas tot haben will) einfach)

// NOTE: apple.h, etc., contents are *NOT* intended to be in namespace Foo! 

// would prefer something most this: 
#pragma magic_namespace_backout(1) // FIXME: use actually existing directive 
namespace Foo { 

#include "apple.h" 
B *A::blah(B const *x) { /* ... */ } 

#include "banana.h" 
int B::whatever(C const &var) { /* ... */ } 

#include "blueberry.h" 
void B::something() { /* ... */ } 

} // namespace Foo 

...

// over this: 
#include "apple.h" 
#include "banana.h" 
#include "blueberry.h" 

namespace Foo { 
B *A::blah(B const *x) { /* ... */ } 
int B::whatever(C const &var) { /* ... */ } 
void B::something() { /* ... */ } 
} // namespace Foo 

...

// or over this: 
#include "apple.h" 
namespace Foo { 
B *A::blah(B const *x) { /* ... */ } 
} // namespace Foo 

#include "banana.h" 
namespace Foo { 
int B::whatever(C const &var) { /* ... */ } 
} // namespace Foo 

#include "blueberry.h" 
namespace Foo { 
void B::something() { /* ... */ } 
} // namespace Foo 

Mein wirkliches Problem ist, dass ich Projekte, wo ein Modul, aber c zu verzweigenden brauchen kann oexistierende Komponenten aus den Zweigen im selben Programm. Ich habe Klassen wie FooA usw., die ich Foo :: A genannt habe, in der Hoffnung, weniger schmerzhaft als Foo :: v1_2 :: A zu verzweigen, wo irgendein Programm sowohl ein Foo :: A als auch ein Foo benötigt :: v1_2 :: A. Ich möchte, dass "Foo" oder "Foo :: v1_2" nur einmal pro Datei angezeigt wird, wenn möglich als einzelner Namespace-Block. Darüber hinaus tendiere ich dazu, Blocks von # include Direktiven unmittelbar über der ersten Definition in der Datei zu finden, die sie benötigt. Was ist meine beste Wahl, oder alternativ, was sollte ich tun, anstatt die Namespaces zu entführen?

+0

Ich ging nur mit Option # 2 und versuchen, so sauber wie möglich mit den enthalten. – Jeff

Antwort

30

Denken Sie nur an # einschließlich Kopieren und Einfügen des Inhalts der enthaltenen Datei an die Position der # include-Anweisung.

Das bedeutet, ja, alles in der enthaltenen Datei wird im Namespace sein.

+1

Danke, ich habe den Präprozessor für das ziemlich blinde Tool akzeptiert, das es ist. – Jeff

+4

Beachten Sie, dass sich '# define' nicht im Namespace befinden. –

+1

Da '# define's kein Konzept für Namespace haben – Kapichu

13

Frage: Können Sie:
A: Ja, Sie können. Die Include-Anweisung wird während der Vorverarbeitung ausgeführt, bevor der Compiler sie überhaupt sieht.

Q: Ist es eine gute Idee.
A: Wahrscheinlich nicht.

Was passiert, wenn Sie Apple.g ohne Namespace-Tags #include.
Jetzt haben Sie Äpfel im globalen Namespace sowie den Foo-Namespace deklariert.

Sie sollten versuchen, Situationen zu vermeiden, in denen der Benutzer Ihres Codes verstehen muss, wie er verwendet werden soll. Wenn Ihre Dokumentation besagt, dass immer die Apfel-Header-Datei im foo-Namespace enthalten ist, ist dies das Bit, das der Benutzer nicht lesen wird und Stunden der Verwirrung verursacht.

+0

Die bessere Idee wäre, dass" apple_noNS.h "die" rohe "Datei ist, und" apple.h "bestehend aus' namespace Foo {#include apple_noNS.h } 'dann einschließlich apple.h überall außerhalb des Namensraums. –

+0

@SF. Das ist eine schreckliche Idee. Und explizit durch die obige Aussage abgedeckt, warum. –

+0

In meiner Situation ist die Datenstruktur zwischen einem (kleineren) ANSI C-Programm und einem (großen) C++ - Programm geteilt. Es gibt keine Namespaces in C, aber aufgrund der Größe des C-Programms werden die Risiken von Konflikten und Problemen seiner zahlreichen Einträge im globalen Namespace gemildert.OTOH, das es in den globalen Namespace der C++ - Anwendung einbindet, würde eine Reihe von Problemen verursachen. Der Benutzer meines Codes muss diese Abhängigkeit sowieso verstehen oder Dinge werden sich ins Gesicht blasen, wenn sie C++ Dinge mit den C-Teilen machen. Diese Dateien müssen als private Felder einer API-Klasse betrachtet werden. –

2

Haben Sie diese Frage bearbeitet?

Der erste Block in Ihren Beispielen ist nicht möglich. Sie können weder innerhalb eines Namespaces einen Namespace nach oben setzen, noch können Sie den Namespace innerhalb einer Datei in diesem Namespaceblock deaktivieren. Es kann einfach nicht so gemacht werden.

Persönlich bevorzuge ich die erste Ihrer Alternativen.

Bearbeiten, ok ...hier ist etwas, das Sie (müssen Bereinigung, ungetestet) tun könnte:


#define MY_NAMESPACE Foo 
#define NAMESPACE_WRAP(X) namespace MY_NAMESPACE { X } 

#include "apple.h" 
NAMESPACE_WRAP((B * A::blah(B const * x) {...})) 

Ziemlich sicher NAMESPACE_WRAP funktioniert nicht für diese Art der Sache, aber so werden Sie wahrscheinlich es in einem anderen Header setzen müssen oder“.IPP "oder was auch immer, und dies tut:


#define NAMESPACE_WRAP(HEADER) \ 
namespace MY_NAMESPACE { \ 
#include HEADER \ 
} 

Auch das nicht funktionieren kann, und Sie werden über mein Wissen gehen und schauen, wie die Boost-Präprozessor metaprogramming Bibliothek hat sein Makros enthalten. Sie können tatsächlich feststellen, dass diese Bibliothek am Ende macht, was Sie wollen, einfacher.

Auf jeden Fall wird es nicht so schön sein, wie Sie wollen, und, IMHO, so lesbar und geradlinig wie die erste Alternative, die Sie vorgestellt haben.

+0

Ja, ich habe bearbeitet, da mein Ziel nicht so sehr eine Ja/Nein-Antwort ist, sondern eine Lösung, die dem ersten Beispiel am ähnlichsten ist. Ich habe versucht anzugeben, dass die erste Methode nicht praktikabel ist, zumindest meine Standardvorgabe, aber ich wäre bereit, nicht-standardisierte Präprozessordirektiven usw. zu verwenden, um - richtig zu sein. – Jeff

2

Lernen Sie das dritte Beispiel zu lieben, indem Sie es in drei separate Dateien aufteilen. Das wäre wirklich der klarste Weg.

Wenn Sie wirklich Dateien in andere Namespaces einschließen möchten, können Sie } als erstes Zeichen der Include-Datei und namespace Whatever { am Ende setzen. Aber das wäre schrecklich.

0

Wahrscheinlich sollte sich jedes Modul auf die Klasse "Foo :: A" beziehen, und Sie könnten Makrodefinition am Anfang des Moduls platzieren, das eine andere Version von "A" benötigt.

#include "apple.h" 
#include "apple1_2.h" 

//this module uses Version 1.2 of "Apple" class 
#define Apple v1_2::Apple 
namespace Foo { 
B *A::blah(B const *x) 
{ 
    Foo::Apple apple; //apple is of type Foo::v1_2::Apple 
    /* ... */ 
} 
int B::whatever(C const &var) { /* ... */ } 
void B::something() { /* ... */ } 
} // namespace Foo 
#undef Apple 

Aber das macht Code schwieriger zu verstehen. Wenn Sie zwischen Implementierungen eines Objekts wählen müssen, verwenden Sie besser eine Factory-Funktion. Das würde Ihre Absicht im gesamten Code explizit machen.

AppleBaseClass* createApple(int version) 
{ 
    if(version == 0) 
     return new Foo::Apple; 
    else if(version == 1) 
     return new Foo::v1_2::Apple; 
} 
//usage 
AppleBaseClass* apple = createApple(apple_version); 

//compile-time equivalent 
//metafunction CreateApple 
template<int version> struct CreateApple {}; 
template<> 
struct CreateApple<0> 
{ 
    typedef Foo::Apple ret; 
}; 
template<> 
struct CreateApple<1> 
{ 
    typedef Foo::v1_2::Apple ret; 
}; 
//usage 
CreateApple<apple_version>::ret apple; 
0

Methode 2 den ganzen Weg.
ich arbeite immer mit diesen einfachen Regeln:

1.) Quellcode sollte klar, leicht zu verstehen und Dummy-PROOF sein.
-Ein gutes Produkt wird nicht von einer einzigen Person gebaut. Die einfache, intuitive und leicht zu befolgende Formatierung wird das Leben aller erfreuen.

2.) Wenn es keinen Leistungsunterschied im Endprodukt gibt, halten Sie sich an Regel # 1
-Es macht keinen Sinn für Entwickler zu verbringen Braincells auf etwas, das Endkunden nicht profitiert.

3.) Elegantes Design wird immer gut funktionieren, so Regel # 2 immer wahr.
-Same Regel gilt für Gott, nehmen Sie einen Spiegel und schauen auf sich selbst :)