que - ¿Hay una mejor manera de expresar espacios de nombres anidados en C++ dentro del encabezado?
namespace php (9)
Cambié de C ++ a Java y C # y creo que el uso de espacios de nombres / paquetes es mucho mejor allí (bien estructurado). Luego volví a C ++ y traté de usar espacios de nombres de la misma manera, pero la sintaxis requerida es horrible dentro del archivo de encabezado.
namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
public class MyClass
Lo siguiente me parece extraño también (para evitar la sangría profunda):
namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
public class MyClass
{
¿Hay una manera más corta de expresar lo anterior? Me falta algo así como
namespace MyCompany::MyModule::MyModulePart::...
{
public class MyClass
Actualizar
Ok, algunos dicen que el concepto de uso en Java / C # y C ++ es diferente. De Verdad? Creo que la carga de clases (dinámica) no es el único propósito de los espacios de nombres (esta es una perspectiva razonada muy técnica). ¿Por qué no debería usarlo para una legibilidad y estructuración, por ejemplo, pensar en "IntelliSense".
Actualmente, no hay lógica / pegamento entre un espacio de nombres y lo que puedes encontrar allí. Java y C # lo hacen mucho mejor ... ¿Por qué incluir <iostream>
y tener el espacio de nombres std
? Bien, si dices que la lógica debe basarse en el encabezado para incluir, ¿por qué el #include no usa una sintaxis amistosa de "IntelliSense" como #include <std::io::stream>
o <std/io/stream>
? Creo que la estructuración faltante en las libs predeterminadas es una debilidad de C ++ en comparación con Java / C #.
Si la singularidad de los conflictos ávidos es un punto (que es un punto de C # y también de Java), una buena idea es utilizar el nombre del proyecto o el nombre de la empresa como espacio de nombres, ¿no crees?
Por un lado, se dice que C ++ es el más flexible ... pero todos dijeron "no hagas esto". Me parece que C ++ puede hacer muchas cosas pero tiene una sintaxis horrible incluso para las cosas más fáciles en muchos casos en comparación con C #.
Actualización 2
La mayoría de los usuarios dicen que no tiene sentido crear un anidamiento más profundo que dos niveles. Ok, entonces ¿qué pasa con Windows :: UI :: Xaml y Windows :: UI :: Xaml :: Controls :: Primitives namespaces en el desarrollo de Win8? Creo que el uso de espacios de nombres de Microsoft tiene sentido y de hecho es más profundo que solo 2 niveles. Creo que las bibliotecas / proyectos más grandes necesitan un anidamiento más profundo (odio los nombres de clase como ExtraLongClassNameBecauseEveryThingIsInTheSameNameSpace ... y luego podrías poner todo en el espacio de nombres global).
Actualización 3 - Conclusión
La mayoría dice "no lo hagas", pero ... incluso el impulso tiene un anidamiento más profundo que uno o dos niveles. Sí, es una biblioteca pero: si desea un código reutilizable, trate su propio código como una biblioteca que le daría a otra persona. También uso un anidamiento más profundo para propósitos de descubrimiento usando espacios de nombres.
Ambos estándares (C ++ 2003 y C ++ 11) son muy explícitos de que el nombre del espacio de nombres es un identificador. Esto significa que se requieren encabezados anidados explícitos.
Mi impresión de que esto no es una gran cosa para permitir la colocación de un identificador calificado además de un nombre simple del espacio de nombres, pero por alguna razón esto no está permitido.
Apoyo totalmente la respuesta de peterchen pero quiero agregar algo que aborde otra parte de su pregunta.
Declarar espacios de nombres es uno de los casos más raros en C ++ donde me gusta el uso 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
Esto también elimina la necesidad de comentarios cerca del corchete de cierre del espacio de nombres (¿Alguna vez se bajó al final de un archivo fuente grande e intentó agregar / eliminar / equilibrar llaves a las que les faltaron comentarios sobre qué abrazadera cierra qué ámbito? No es divertido .).
MY_COMPANY_BEGIN
MY_LIBRARY_BEGIN
class X { };
class Y { };
MY_LIBRARY_END
MY_COMPANY_END
Si quieres poner todas las declaraciones de espacios de nombres en una sola línea, puedes hacer eso también con un poco de magia del preprocesador (bastante fea):
// 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__)
Ahora puedes hacer esto:
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;
Para anidar más de tres niveles, debe agregar macros auxiliares hasta el recuento deseado.
Aquí una cita de Lzz de Lzz (Lazy C ++):
Lzz reconoce los siguientes constructos de C ++:
definición de espacio de nombres
Un espacio de nombres sin nombre y todas las declaraciones encerradas se envían al archivo de origen. Esta regla anula todas las demás.
El nombre de un espacio de nombre nombrado puede ser calificado.
namespace A::B { typedef int I; }
es equivalente a:
namespace A { namespace B { typedef int I; } }
Por supuesto, la calidad de las fuentes que dependen de tales herramientas es discutible ... Diría que es más una curiosidad, que muestra que la sintaxis inducida por C ++ puede tomar muchas formas (yo también tengo la mía ...)
C ++ 17 podría simplificar la definición de espacios de nombres anidados:
namespace A::B::C {
}
es equivalente a
namespace A { namespace B { namespace C {
} } }
Ver (8) en la página del espacio de nombres en cppreference:
http://en.cppreference.com/w/cpp/language/namespace
Los espacios de nombres C ++ se usan para agrupar interfaces, no para dividir componentes o expresar divisiones políticas.
El estándar se sale de su camino para prohibir el uso de espacios de nombres similares a los de Java. Por ejemplo, los alias de espacio de nombres proporcionan una forma de usar fácilmente nombres de espacios de nombres anidados o largos.
namespace a {
namespace b {
namespace c {}
}
}
namespace nsc = a::b::c;
Pero namespace nsc {}
sería un error, porque un espacio de nombre solo se puede definir utilizando su nombre-espacio-nombre original . Esencialmente, el estándar facilita las cosas para el usuario de dicha biblioteca, pero es difícil para el implementador . Esto desalienta a las personas a escribir tales cosas, pero mitiga los efectos si lo hacen.
Debería tener un espacio de nombres por interfaz definido por un conjunto de clases y funciones relacionadas. Las subinterfaces internas u opcionales pueden entrar en espacios de nombres anidados. Pero más de dos niveles de profundidad deberían ser una bandera roja muy seria.
Considere usar caracteres de subrayado y prefijos de identificador donde el operador ::
no es necesario.
No, y por favor no hagas eso.
El objetivo de los espacios de nombres es principalmente resolver conflictos en el espacio de nombres global.
Un propósito secundario es la abreviatura local de símbolos; por ejemplo, un método UpdateUI
complejo puede usar un using namespace WndUI
para usar símbolos más cortos.
Estoy en un proyecto de 1.3MLoc, y los únicos espacios de nombres que tenemos son:
- bibliotecas COM externas importadas (principalmente para aislar conflictos de encabezado entre
#import
y#include windows.h
) - Un nivel de espacios de nombres de "API pública" para ciertos aspectos (UI, acceso a DB, etc.)
-
ModuleDetailHereBeTygers
nombres de "Detalles de implementación" que no son parte de la API pública (espacios de nombres anónimos en espacios de nombres .cpp oModuleDetailHereBeTygers
en libs de solo encabezado) - las enumeraciones son el mayor problema en mi experiencia. Ellos contaminan como locos.
- Sigo pensando que son demasiados espacios de nombres
En este proyecto, los nombres de clase, etc. utilizan un código de "región" de dos o tres letras (por ejemplo, CDBNode
lugar de DB::CNode
). Si prefiere este último, hay espacio para un segundo nivel de espacios de nombres "públicos", pero no más.
Los enunciados específicos de clase, etc. pueden ser miembros de esas clases (aunque estoy de acuerdo en que esto no siempre es bueno, y a veces es difícil decir si deberías)
Rara vez se necesita un espacio de nombres de "empresa", excepto si tiene grandes problemas con bibliotecas de terceros que se distribuyen como binarias, no proporcionan su propio espacio de nombres y no se pueden poner fácilmente en uno (por ejemplo, en un archivo binario distribución). Aún así, en mi experiencia forzarlos a un espacio de nombres es mucho más fácil de hacer.
[edit] Según la pregunta de seguimiento de Stegi:
Ok, entonces ¿qué pasa con Windows :: UI :: Xaml y Windows :: UI :: Xaml :: Controls :: Primitives namespaces en el desarrollo de Win8? Creo que el uso de espacios de nombres de Microsoft tiene sentido y de hecho es más profundo que solo 2 niveles
Lo siento si no fui lo suficientemente claro: dos niveles no es un límite difícil, y más no es intrínsecamente malo. Solo quería señalar que rara vez necesita más de dos, según mi experiencia, incluso en una gran base de código. Anidar más profundo o más superficial es una compensación.
Ahora, el caso de Microsoft es posiblemente diferente. Presumiblemente un equipo mucho más grande, y todo el código es biblioteca.
Supongo que Microsoft imita aquí el éxito de la Biblioteca .NET, donde los espacios de nombres contribuyen a la capacidad de descubrimiento de la extensa biblioteca. (.NET tiene alrededor de 18000 tipos)
Además asumiría que hay un símbolo óptimo (orden de magnitud de) símbolos en un espacio de nombres. decir, 1 no tiene sentido, 100 suena bien, 10000 es claramente demasiado.
TL; DR: Es una compensación, y no tenemos números duros. Juega seguro, no exageres en ninguna dirección. El "No hagas eso" viene simplemente del "Tienes problemas con eso, tengo problemas con eso, y no veo una razón por la que lo necesites".
Para evitar una sangría realmente profunda, generalmente lo hago de esta manera:
namespace A { namespace B { namespace C
{
class X
{
// ...
};
}}}
Puedes usar esta sintaxis:
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 {
...
};
Tenga en cuenta que esta sintaxis es válida incluso en C ++ 98 y es casi similar a lo que ahora está disponible en C ++ 17 con definiciones de espacio de nombres anidados .
Feliz desacato!
Fuentes:
Sí, tendrás que hacerlo como
namespace A{
namespace B{
namespace C{}
}
}
Sin embargo, está tratando de usar los espacios de nombres de una manera que no deberían usarse. Verifique this pregunta, tal vez lo encuentre útil.