Как побороть зло пространства имен заголовочных файлов C ++?

С одним из моих проектов я отправлюсь в область C ++. В основном я иду
из фона Java и было интересно, как концепция пакетов Java
реализуется в мире C ++. Это привело меня к концепции пространств имен C ++.

Я до сих пор в порядке с пространствами имен, но когда дело доходит до заголовочных файлов
вещи становятся неэффективными в отношении полностью квалифицированного класса
имена, директивы использования и объявления использования.

Очень хорошее описание проблемы этот статья Херба Саттера.

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

Это почти недопустимо. Как заголовок C ++ обычно обеспечивает объявление
класса, максимальная читаемость имеет высший приоритет. Полностью отборочный каждый
Тип из другого пространства имен создает много визуального шума, наконец,
снижение читабельности заголовка до такой степени, что возникает вопрос
использовать ли пространства имен вообще.

Тем не менее, я хочу воспользоваться пространствами имен C ++ и поэтому немного подумать
вопрос: как побороть зло пространства имен заголовочных файлов C ++? После
некоторые исследования, которые, я думаю, typedefs могли бы быть правильным решением этой проблемы.

Ниже вы найдете пример программы на C ++, которая демонстрирует, как
нравится использовать публичные классы typedefs для импорта типов из других пространств имен.
Программа синтаксически верна и прекрасно компилируется на MinGW W64. Пока так
хорошо, но я не уверен, что этот подход удаляет ключевое слово using
из шапки, но приносит еще одну проблему, о которой я просто не знаю.
Просто что-то хитрое, как вещи, описанные Хербом Саттером.

Я прошу всех, кто хорошо разбирается в C ++,
рассмотрите код ниже и дайте мне знать, должно ли это работать или нет. Спасибо
за ваши мысли.

MyFirstClass.hpp

#ifndef MYFIRSTCLASS_HPP_
#define MYFIRSTCLASS_HPP_

namespace com {
namespace company {
namespace package1 {

class MyFirstClass
{
public:
MyFirstClass();
~MyFirstClass();

private:

};

} // namespace package1
} // namespace company
} // namespace com

#endif /* MYFIRSTCLASS_HPP_ */

MyFirstClass.cpp

#include "MyFirstClass.hpp"
using com::company::package1::MyFirstClass;

MyFirstClass::MyFirstClass()
{
}

MyFirstClass::~MyFirstClass()
{
}

MySecondClass.hpp

#ifndef MYSECONDCLASS_HPP_
#define MYSECONDCLASS_HPP_

#include <string>
#include "MyFirstClass.hpp"
namespace com {
namespace company {
namespace package2 {

/*
* Do not write using-declarations in header files according to
* Herb Sutter's Namespace Rule #2.
*
* using std::string; // bad
* using com::company::package1::MyFirstClass; // bad
*/

class MySecondClass{

public:
/*
* Public class-scoped typedefs instead of using-declarations in
* namespace package2. Consequently we can avoid fully qualified
* type names in the remainder of the class declaration. This
* yields maximum readability and shows cleanly the types imported
* from other namespaces.
*/
typedef std::string String;
typedef com::company::package1::MyFirstClass MyFirstClass;

MySecondClass();
~MySecondClass();

String getText() const; // no std::string required
void setText(String as_text); // no std::string required

void setMyFirstInstance(MyFirstClass anv_instance); // no com::company:: ...
MyFirstClass getMyFirstInstance() const; // no com::company:: ...

private:
String is_text; // no std::string required
MyFirstClass inv_myFirstInstance; // no com::company:: ...
};

} // namespace package2
} // namespace company
} // namespace com

#endif /* MYSECONDCLASS_HPP_ */

MySecondClass.cpp

#include "MySecondClass.hpp"
/*
* According to Herb Sutter's "A Good Long-Term Solution" it is fine
* to write using declarations in a translation unit, as long as they
* appear after all #includes.
*/
using com::company::package2::MySecondClass; // OK because in cpp file and
// no more #includes following
MySecondClass::MySecondClass()
{
}

MySecondClass::~MySecondClass()
{
}

/*
* As we have already imported all types through the class scoped typedefs
* in our header file, we are now able to simply reuse the typedef types
* in the translation unit as well. This pattern shortens all type names
* down to a maximum of "ClassName::TypedefTypeName" in the translation unit -
* e.g. below we can simply write "MySecondClass::String". At the same time the
* class declaration in the header file now governs all type imports from other
* namespaces which again enforces the DRY - Don't Repeat Yourself - principle.
*/

// Simply reuse typedefs from MySecondClass
MySecondClass::String MySecondClass::getText() const
{
return this->is_text;
}

// Simply reuse typedefs from MySecondClass
void MySecondClass::setText(String as_text)
{
this->is_text = as_text;
}

// Simply reuse typedefs from MySecondClass
void MySecondClass::setMyFirstInstance(MyFirstClass anv_instance)
{
this->inv_myFirstInstance = anv_instance;
}

// Simply reuse typedefs from MySecondClass
MySecondClass::MyFirstClass MySecondClass::getMyFirstInstance() const
{
return this->inv_myFirstInstance;
}

main.cpp

#include <cstdio>
#include "MySecondClass.hpp"
using com::company::package2::MySecondClass; // OK because in cpp file and
// no more #includes following
int main()
{
// Again MySecondClass provides all types which are imported from
// other namespaces and are part of its interface through public
// class scoped typedefs
MySecondClass *lpnv_mySecCls = new MySecondClass();

// Again simply reuse typedefs from MySecondClass
MySecondClass::String ls_text = "Hello World!";
MySecondClass::MyFirstClass *lpnv_myFirClsf =
new MySecondClass::MyFirstClass();

lpnv_mySecCls->setMyFirstInstance(*lpnv_myFirClsf);

lpnv_mySecCls->setText(ls_text);
printf("Greetings: %s\n", lpnv_mySecCls->getText().c_str());

lpnv_mySecCls->setText("Goodbye World!");
printf("Greetings: %s\n", lpnv_mySecCls->getText().c_str());

getchar();

delete lpnv_myFirClsf;
delete lpnv_mySecCls;

return 0;
}

6

Решение

Боль смягчается уменьшением сложности. Вы сгибаете C ++ в Java. (Это работает так же плохо, как пытаться в другом направлении.)

Некоторые намеки:

  • Удалите уровень пространства имен com. (Это просто java-изм, который вам не нужен)
  • Удалите пространство имен «company», возможно, замените его пространством имен «product» или «library» (т.е. boost, Qt, OSG и т. Д.). Просто выберите что-то уникальное с W.r.t. другие библиотеки, которые вы используете.
  • Вам не нужно полностью объявлять имена, которые находятся в том же пространстве имен, в котором вы находитесь (caveat emptor: template classe, см. Комментарий). Просто избегайте любых using namespace директивы в заголовках. (И используйте с осторожностью в файлах C ++, если вообще. Внутренние функции предпочтительнее.)
  • Рассмотрим псевдонимы пространства имен (в файлах функций / cpp), т.е. namespace bll = boost::lambda;, Это создает ярлыки, которые довольно аккуратны.
  • Кроме того, скрывая частные члены / типы, используя Pimpl шаблон, ваш заголовок имеет меньше типов, чтобы выставить.

П.С .: Благодаря @KillianDS несколько хороших советов в комментариях (которые были удалены, когда я отредактировал их в вопросе).

18

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]