Pull to refresh

Comments 30

А зачем вводить using namespace в библиотечный коды ( я просто выношу его сразу после include, когда подключаю стандартную библиотеку)? Это для ленивых программистов!?
using namespace можно использовать для того, чтобы при написании самого кода библиотеки явно не задавать пространства имен для классов, структур (и др.) из других библиотек. Обычно не рекомендуется вставлять using namespace в заголовочные файла по определенным причинам (подробнее можно узнать, например в книге «Стандарты программирования на С++. 101 правило и рекомендации» Г. Саттера и А.Александреску [глава 59]). Этот прием позволяет не беспокоиться об этом.
В пункте «Решение на основе «рабочих» пространств имен» мне ненравится то, что Вы пишете явно декларацию «внутренних» классов, в приведеном Вами случае это DetailClass. На мой взгляд нужно стремиться убирать с глаз долой от пользователя библиотеки любую информацию о внутренней структуре и реализации библиотеки. Я бы предложил бы использовать Forward-декларацию и убрать фактическую декларацию в DetailClassImpl.hpp.

Что-то вроде этого:

class DetailClass;

class SomeClass {
private:
DetailClass * pImpl;
};


В моем примере: DetailClass нужно обернуть в impl

Минусом В моем примере является необходимость динамического создания объекта.
Согласен, что декларация классов реализации не должна быть доступна пользователю. Убрать полностью на C++ эти зависимости нельзя, можно их только минимизировать.

Убрать необходимость в динамическом создании объекта можно за счет организации файлов в библиотеке. Но это уже тема следующей части цикла топиков по реализации библиотеки. Вкратце это можно сделать так:
// файл detail_class.hpp

namespace work_lib_namespace
{
    class detail_class
    {
    };
}


// файл some_class.hpp

#include "detail_class.hpp"

namespace work_lib_namespace
{
    class some_class
    {
    public:
        // интерфейс
    private:
        detail_class impl_;
    };
}

namespace lib_namespace
{
    using ::work_lib_namespace::some_class;
}
Тогда лучше так:
class SomeClass {
  private:
  class DetailClass;
  DetailClass * pImpl;
};


В файле реализации:
class SomeClass::DetailClass
{
 ..
};
Да, с Вами согласен, то что Вы написал куда правильней выглядит и именно в таком виде использую этот трюк. Просто при написании комментария чуток поторопился ;)
UFO just landed and posted this here
UFO just landed and posted this here
Автоматом вводить имена классов библиотеки в глобальное пространство имен, используя заголовочные файлы, считается не очень хорошим тоном. На мой взгляд, пусть файл mylib.h лучше использует конструкцию вида:
namespace mylib
{
    using namespace mylib_v1;
}

А пользователь библиотеки сам решит вводить ли ему имена библиотеки в глобальное (или свое) пространство имен через using namespace mylib;.
UFO just landed and posted this here
Мда, целая статья на тривиальный вопрос. Почему бы было не сделать как в бусте:
namespace my_lib {
    namespace detail {
        class impl {
             ...
        };
    }

    class my_class {
         detail::impl impl;
    };
}

Чем Ваш вариант отличается от решения №2, представленного в топике?
В принципе, ничем, за исключением того, что namespace detail — это для буста общепринятая концепция, а также тем, что все оборачивается под одним неймспейсом без разделения (namespace my_lib пишется только один раз). Предложение состоит в том, чтобы использовать уже имеющиеся стандартны и naming conventions, вместо изобретения своих.
Дело в том, что «рабочие» пространства имен — это стартовая точка для рассмотрения последующих вопросов, связанных с реализацией библиотеки.

Ваш вариант, а именно единственное описание namespace my_lib, теряет смысл, если библиотека содержит более одного файла. В таком случае для Вашего варианта дублирования имени namespace не избежать…
В контексте данной статьи это не очевидно. Посмотрим, что будет в последующих.
«дублирования имени namespace не избежать»
В дублировании имени namespace нет ничего страшного — компилятор воспринимает это, как продолжение namespace. Если подумать, эта фича — единственная причина, по которой namespace введены в язык.
Я прошу Вас, будьте внимательны. Автор вопроса указал разницу между предлагаемым им вариантом и решением 2, представленным в топике. Я этой разницы не заметил, указав на то место в описании варианта, при котором оба варианта становятся эквивалентными.
«Ваш вариант, а именно единственное описание namespace my_lib, теряет смысл, если библиотека содержит более одного файла. В таком случае для Вашего варианта дублирования имени namespace не избежать…»

Меня конкретно это утверждение заинтересовало. Не могли бы вы детальнее раскрыть свою мысль?
Автор вопроса при описании отличий своего варианта от решения 2 топика написал:
«что все оборачивается под одним неймспейсом без разделения (namespace my_lib пишется только один раз)».

Я возразил. Может быть, мысль была неудачно сформулирована.
И так, вы предлагаете набор гайдлайнов для кодописателей, которые должны облегчить разбирательство с интерфейсом недокументированных библиотек, так? Я бы советовал совсем другое — pimpl повсюду и разделение на public/private декларации на уровне заголовочных файлов. И еще namespace detail как в бусте для борьбы с intellisence.

А ваш третий способ мне не понравился. Сначала описываем все вперемешку с кишками (work_lib_namespace), а потом читаем отдельно список публичных сущностей (lib_namespace). Если хотим увидеть определение some_class, прыгаем черте куда.

Я наивно полагал что самодокументированность — это когда мы читаем код как документацию. Подряд, и не отвлекаясь на кишки. И не прыгая постоянно по файлу туда обратно.
Не туда написал ответ. Смотрите пост ниже.

И еще. Предлагаемый способ лишен одного недостатка (хотя для кого как), присущего всем остальным и, в частности, Вашему варианту. А именно: явное указание пространств имен при использовании классов других библиотек.

Помимо это, использование идиомы PImpl в стандартном для него виде, выливается в значительное количество кода, необходимого разработчику библиотеки для написания каждого такого класса.
Сам написал не одну библиотеку (и не маленькие). Идеи написания шли рядом со стандартными, особенно с теми, что применялись в boost-e. Устал от сложности языка, а точнее, от тех шаблонов, к которым мы привыкли.

Перечитайте, пожалуйста, топик. В нем ясно сказано, что организация файлов в нем не рассматривается. Это будет сделано позже — в следующих топиках. Подождите, немного: сами все увидите…

По поводу PImpl. Заставлять пользователей всегда использовать динамическое выделение объекта класса реализации, это, уж извините, не очень хорошо. Пусть разработчик библиотеки решает, что ему лучше…
>>Перечитайте, пожалуйста, топик. В нем ясно сказано,…
По после этих слов мне показалось что вы тратите свою энергию в-пустую пытаясь объяснить то что не требует пояснения. Мне очень хочется увидеть очередную Вашу статью, поэтому поберегите свою энергию на то что действительно нужно. ИМХО.
А как тогда делать бинарную совместимость если не через pimpl? Если нужно, чтобы экземпляры на стеке создавались и нормально копировались, то да здравствует шаблон cow. Если такое не требуется, то самый банальный pimpl сканает для обеспечения бинарной совместимости.
Если класс маленький и часто используемый, то и вовсе разделять на интерфейс и реализацию не стоит — только в производительности потеряем. Но тогда такой класс сразу нужно от начала и до конца делать и увековечивать в камне :)
Поясните, пожалуйста, что такое cow. Гугл молчит :(
Возможно под «cow» имелось в виду «Copy On Write» в вики отлично разжевано, что это значит.
Спасибо.

// Простите, случайно поставил минус (хотел «закрыть» окно комментариев)
Sign up to leave a comment.

Articles