Экранирование #include в пространстве имен {} block?

Изменить: я знаю, что метод 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 сразу над первым определением в файле, который их требует. Какой мой лучший выбор, или, альтернативно, что я должен делать вместо захвата пространств имен?

Ответы

Ответ 1

Просто подумайте о #including как о копировании и вставке содержимого включенного файла в позицию директивы #include.

Это означает, что да, все в включенном файле будет находиться внутри пространства имен.

Ответ 2

В: Можете ли вы:
A: Да, вы можете. Оператор include выполняется во время предварительной обработки, прежде чем компилятор даже увидит его.

В: Это хорошая идея.
A: Наверное, нет.

Что произойдет, если вы # включите Apple.g без тегов пространства имен.
теперь у вас есть яблоки, объявленные в глобальном пространстве имен, а также в пространстве имен foo.

Вам следует попытаться избежать ситуаций, когда пользователь вашего кода должен понять, как его использовать. Если в вашей документации всегда указано # включить заголовочный файл Apple в пространстве имен foo, бит, который пользователь не будет читать, и вызвать частые путаницы.

Ответ 3

Вы редактировали этот вопрос?

Первый блок в ваших примерах невозможен. Вы не можете найти пространство имен или что-то вроде того, что хотите, и не можете отключить пространство имен из файла, включенного в этот блок пространства имен. Это просто невозможно сделать.

Лично я предпочитаю первую из ваших альтернатив.

Изменить, ок... вот что-то, что вы могли бы сделать (может потребоваться очистка, непроверенная):


#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, как читаемый и прямой, как первая альтернатива, которую вы представили.

Ответ 4

Научитесь любить третий пример, разделив его на три отдельных файла. Это действительно лучший способ.

Если вы действительно хотите включать файлы внутри других пространств имен, вы можете поместить } в качестве первого символа файла include и namespace Whatever { в конце. Но это было бы ужасно.

Ответ 5

Вероятно, каждый модуль должен ссылаться на класс "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;

Ответ 6

Метод 2 полностью.
Я всегда работаю с этими простыми правилами:

1.) Исходный код должен быть CLEAR, EASY-TO-THONSTAND и DUMMY-PROOF.
Хороший продукт не строится одним человеком. Простой, интуитивно понятный и простой в использовании форматирование сделает жизнь счастливее.

2.) Если в конечном продукте не будет разницы в производительности, придерживайтесь правила № 1
-Это не имеет смысла для разработчиков тратить брелки на то, что не приносит пользы конечным потребителям.

3.) Элегантный дизайн всегда будет работать нормально, поэтому правило №2 всегда верно.
-Изное правило применимо к Богу, возьмите зеркало и посмотрите на себя:)