Ответ 1
Просто подумайте о #including как о копировании и вставке содержимого включенного файла в позицию директивы #include.
Это означает, что да, все в включенном файле будет находиться внутри пространства имен.
Изменить: я знаю, что метод 1 по существу недействителен и, вероятно, будет использовать метод 2, но я ищу лучший хак или лучшее решение для смягчения разрастающейся, изменяемой распространения пространства имен.
У меня есть несколько определений классов или методов в одном пространстве имен, которые имеют разные зависимости, и хотели бы использовать наименьшее количество блоков пространства имен или явных выборок, но при группировании директив #include с определениями, которые требуют их как можно лучше. Я никогда не видел никаких указаний на то, что любому препроцессору может быть сказано исключить пространство имен {} из области содержимого #include, но я здесь, чтобы спросить, возможно ли что-то похожее на это: (см. Нижнюю часть для объяснения того, почему я хочу что-то мертвое простой)
// 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
Моя реальная проблема заключается в том, что у меня есть проекты, в которых модуль может нуждаться в разветвленности, но у него есть сосуществующие компоненты из ветвей в одной и той же программе. У меня есть классы, такие как FooA и т.д., Которые я назвал Foo:: A в надежде на то, что вы сможете менее болезненно отделяться от Foo:: v1_2:: A, где некоторым программам может понадобиться как Foo:: A, так и Foo:: v1_2:: A. Я бы хотел, чтобы "Foo" или "Foo:: v1_2" отображались только один раз для каждого файла, как только один блок пространства имен, если это возможно. Более того, я предпочитаю размещать блоки директив #include сразу над первым определением в файле, который их требует. Какой мой лучший выбор, или, альтернативно, что я должен делать вместо захвата пространств имен?
Просто подумайте о #including как о копировании и вставке содержимого включенного файла в позицию директивы #include.
Это означает, что да, все в включенном файле будет находиться внутри пространства имен.
В: Можете ли вы:
A: Да, вы можете. Оператор include выполняется во время предварительной обработки, прежде чем компилятор даже увидит его.
В: Это хорошая идея.
A: Наверное, нет.
Что произойдет, если вы # включите Apple.g без тегов пространства имен.
теперь у вас есть яблоки, объявленные в глобальном пространстве имен, а также в пространстве имен foo.
Вам следует попытаться избежать ситуаций, когда пользователь вашего кода должен понять, как его использовать. Если в вашей документации всегда указано # включить заголовочный файл Apple в пространстве имен foo, бит, который пользователь не будет читать, и вызвать частые путаницы.
Вы редактировали этот вопрос?
Первый блок в ваших примерах невозможен. Вы не можете найти пространство имен или что-то вроде того, что хотите, и не можете отключить пространство имен из файла, включенного в этот блок пространства имен. Это просто невозможно сделать.
Лично я предпочитаю первую из ваших альтернатив.
Изменить, ок... вот что-то, что вы могли бы сделать (может потребоваться очистка, непроверенная):
#define MY_NAMESPACE Foo
#define NAMESPACE_WRAP(X) namespace MY_NAMESPACE { X }
#include "apple.h"
NAMESPACE_WRAP((B * A::blah(B const * x) {...}))
Довольно уверен, что NAMESPACE_WRAP не будет работать для такого рода вещей, хотя вам, вероятно, придется поместить его в другой заголовок или ".ipp" или что-то еще:
#define NAMESPACE_WRAP(HEADER) \
namespace MY_NAMESPACE { \
#include HEADER \
}
Даже это может не сработать, и вам придется выйти за рамки моих знаний и посмотреть, как библиотека метапрограммирования ускорения препроцессора включает в себя макросы. Возможно, вы обнаружите, что эта библиотека заканчивается тем, что вам нужно.
Во всяком случае, это будет не так красиво, как вы хотите, и IMHO, как читаемый и прямой, как первая альтернатива, которую вы представили.
Научитесь любить третий пример, разделив его на три отдельных файла. Это действительно лучший способ.
Если вы действительно хотите включать файлы внутри других пространств имен, вы можете поместить }
в качестве первого символа файла include и namespace Whatever {
в конце. Но это было бы ужасно.
Вероятно, каждый модуль должен ссылаться на класс "Foo:: A", и вы можете установить определение макроса в начале модуля, которому нужна другая версия "A".
#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
Но это затрудняет понимание кода. Возможно, если вам нужно выбирать между реализациями объекта, вам лучше использовать функцию factory. Это сделает ваше намерение явным во всем коде.
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;
Метод 2 полностью.
Я всегда работаю с этими простыми правилами:
1.) Исходный код должен быть CLEAR, EASY-TO-THONSTAND и DUMMY-PROOF.
Хороший продукт не строится одним человеком. Простой, интуитивно понятный и простой в использовании форматирование сделает жизнь счастливее.
2.) Если в конечном продукте не будет разницы в производительности, придерживайтесь правила № 1
-Это не имеет смысла для разработчиков тратить брелки на то, что не приносит пользы конечным потребителям.
3.) Элегантный дизайн всегда будет работать нормально, поэтому правило №2 всегда верно.
-Изное правило применимо к Богу, возьмите зеркало и посмотрите на себя:)