Инкапсуляция включения, чтобы отображались только выбранные классы

Вопрос: можно ли получить доступ к определенному классу из заголовка, скрывая при этом другие пространства имен / классы, определенные в этом заголовке?

Например, скажем, у меня есть файл заголовка - result.hpp:

// outcome.hpp
namespace sports { namespace outcome {
  class Injury {};
  class Success {};
}}

В другом файле заголовка - api.hpp - я хочу использовать sports::outcome::Injury без доступа sports::outcome::Success к файлам, которые включают api.hpp. Это возможно? Если да, то как я могу этого добиться?

P.S. В реальном коде api.hpp содержит шаблонные методы, которые будут вызывать методы класса Injury, поэтому прямого объявления недостаточно.


Мои попытки:

По своему незнанию я попытался добиться этого, поместив include во внутреннее пространство имен. Вот SSCCE:

// api.hpp
namespace sports { namespace api {
  namespace internal {
    #include "outcome.hpp"  // I'm trying to hide symbols within this header
    using sports::outcome::Injury;
  } 

  class Boxing {
    private:
      internal::Injury sustained;
  };
}

Я преждевременно праздновал, когда это сработало:

// This cpp file compiles file   \o/
#include "api.hpp"
int main(int argc, char *argv[]) {
  sports::api::Boxing b;  
  // sports::outcome not accessible
}

Как это не удается:

Все развалится, если контейнер из стандартной библиотеки используется в качестве члена класса в result.hpp. Например, используя эту версию:

// outcome.hpp
#include <vector>
namespace sports { namespace outcome {

  class Injury {
    private:
      std::vector x;
  };

// ...
}}

Компиляция завершается ошибкой из-за следующих ошибок:

In file included from /usr/include/c++/4.6/ext/new_allocator.h:34:0,
                 from /usr/include/c++/4.6/x86_64-linux-gnu/./bits/c++allocator.h:34,
                 from /usr/include/c++/4.6/bits/allocator.h:48,
                 from /usr/include/c++/4.6/vector:62,
                 from outcome.hpp:1,
                 from api.hpp:5,
                 from main.cpp:1:
/usr/include/c++/4.6/new:93:54: error: ‘void* sports::api::internal::operator new(sports::api::internal::std::size_t)’ may not be declared within a namespace
/usr/include/c++/4.6/new:94:56: error: ‘void* sports::api::internal::operator new [](sports::api::internal::std::size_t)’ may not be declared within a namespace
/usr/include/c++/4.6/new:95:35: error: ‘void sports::api::internal::operator delete(void*)’ may not be declared within a namespace
/usr/include/c++/4.6/new:96:37: error: ‘void sports::api::internal::operator delete [](void*)’ may not be declared within a namespace
/usr/include/c++/4.6/new:97:62: error: ‘void* sports::api::internal::operator new(sports::api::internal::std::size_t, const sports::api::internal::std::nothrow_t&)’ may not be declared within a namespace
/usr/include/c++/4.6/new:98:64: error: ‘void* sports::api::internal::operator new [](sports::api::internal::std::size_t, const sports::api::internal::std::nothrow_t&)’ may not be declared within a namespace
/usr/include/c++/4.6/new:99:58: error: ‘void sports::api::internal::operator delete(void*, const sports::api::internal::std::nothrow_t&)’ may not be declared within a namespace
/usr/include/c++/4.6/new:100:60: error: ‘void sports::api::internal::operator delete [](void*, const sports::api::internal::std::nothrow_t&)’ may not be declared within a namespace
/usr/include/c++/4.6/new:103:57: error: ‘void* sports::api::internal::operator new(sports::api::internal::std::size_t, void*)’ may not be declared within a namespace
/usr/include/c++/4.6/new:104:59: error: ‘void* sports::api::internal::operator new [](sports::api::internal::std::size_t, void*)’ may not be declared within a namespace
/usr/include/c++/4.6/new:107:52: error: ‘void sports::api::internal::operator delete(void*, void*)’ may not be declared within a namespace
/usr/include/c++/4.6/new:108:52: error: ‘void sports::api::internal::operator delete [](void*, void*)’ may not be declared within a namespace

Я явно делаю это неправильно. Мы будем очень благодарны за совет и хорошее выступление.


person Shawn Chin    schedule 14.11.2012    source источник
comment
Что вы имеете в виду, открывая пространство имен?   -  person juanchopanza    schedule 14.11.2012
comment
Используя приведенный выше пример, я не хочу, чтобы sports::outcome::* был доступен в файлах, содержащих api.hpp.   -  person Shawn Chin    schedule 14.11.2012


Ответы (2)


Вы можете импортировать имя из пространства имен в другое пространство имен с помощью объявления using.

namespace sports {
  using sports::outcome::Injury;
};

В вашем примере много чего не так. Особенно плохо размещено включение, и вам не хватает охранников.

// file1.h
#ifndef FILE1_h
#define FILE1_h

namespace sports { namespace outcome {
  class Injury;
}}
#endif

// api.h   
#ifndef API_H
#define API_H

#include <file1.h>

namespace sports {
  using sports::outcome::injury;
}

#endif

Файл, содержащий api.h, теперь имеет доступ к:

sports::injury x;

Файл, содержащий только file1.h, имеет доступ только к:

sports::outcome::injury x;
person pmr    schedule 14.11.2012
comment
Разве мне не нужно сначала включить файл заголовка, который определяет Injury, и не будет ли он включать все в этом файле заголовка? Простите за незнание, я все еще прихожу к пониманию C ++. - person Shawn Chin; 14.11.2012
comment
@ShawnChin Да, тебе нужно. Я добавил пример. Невозможно включить все в этот заголовок. Попробуйте разделить заголовки, сделать форвардные объявления и т. Д. - person pmr; 14.11.2012
comment
Я не учел включать охранников для краткости (они есть в самом коде). Неудачное включение было неубедительной попыткой скрыть то, что было включено - это определенно казалось неправильным, но я не мог понять, как еще этого добиться, отсюда и вопрос. - person Shawn Chin; 14.11.2012
comment
@ShawnChin Вы не можете сделать это просто так, как работает система включения C ++. К сожалению, у него нулевая инкапсуляция. - person pmr; 14.11.2012
comment
В этом есть смысл. Спасибо за терпение. - person Shawn Chin; 14.11.2012
comment
Хотя интересно, что мой запутанный подход почти сработал, если бы не необходимость использовать контейнеры из std. - person Shawn Chin; 14.11.2012

Если я правильно понимаю ваши намерения, тогда вам нужно что-то вроде «частного» или «внутреннего» пространства имен? ИМХО, это невозможно в C ++.

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

namespace sports
{
    class api;

    class outcome
    {
        friend class api;

        // Internal class definitions
        class Boxing
        {
        };
    };

    class api
    {
    public:
        typedef outcome::Boxing Boxing;
    }
}
person πάντα ῥεῖ    schedule 14.11.2012