экранирование #include в блоке пространства имен {}? - PullRequest
16 голосов
/ 20 мая 2010

Редактировать: я знаю, что метод 1 по сути недопустим и, вероятно, будет использовать метод 2, но я ищу лучшее решение или лучшее решение для уменьшения безудержного, изменчивого распространения пространства имен.

У меня есть несколько определений классов или методов в одном пространстве имен, которые имеют разные зависимости, и я хотел бы использовать как можно меньше блоков пространства имен или явных областей видимости, но при этом группируя директивы #include с определениями, которые требуют их как можно лучше. Я никогда не видел никаких признаков того, что какой-либо препроцессор мог бы сказать, чтобы исключить область имен {} scoping из содержимого #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 непосредственно над первым определением в файле, который требует их. Какой мой лучший выбор, или, в качестве альтернативы, что я должен делать вместо того, чтобы захватывать пространства имен?

Ответы [ 6 ]

37 голосов
/ 20 мая 2010

Просто представьте, что #include - это копирование и вставка содержимого включенного файла в положение директивы #include.

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

14 голосов
/ 20 мая 2010

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

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

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

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

2 голосов
/ 20 мая 2010

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

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

2 голосов
/ 20 мая 2010

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

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

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

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


#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 \
}

Даже это может не сработать, и вам придется выйти за рамки моих знаний и посмотреть, как библиотека метапрограммирования boost препроцессора включает в себя макросы. Вы можете обнаружить, что эта библиотека в итоге делает то, что вы хотите, проще.

В любом случае, он не будет таким красивым, как вы хотите, и, ИМХО, не таким читабельным и понятным, как первая представленная вами альтернатива.

0 голосов
/ 20 мая 2010

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

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

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

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

0 голосов
/ 20 мая 2010

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

Но это усложняет понимание кода. Возможно, если вам нужно выбирать между реализациями объекта, вам лучше использовать фабричную функцию. Это сделало бы ваше намерение явным во всем коде.

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;
...