В свете переписывания библиотечного кода на кроссплатформу, принято решения использовать snake_case, как у стандартной библиотеки С++. С этим проблем нет. На 99,9% код использует шаблоны и статический полиморфизм. От сюда duck typing и никаких приседаний с именованием не требуется. Но в некоторых местах всё же необходима динамический полиморфизм и там присутствуют интерфейсы типа IBase и т.д.
Хотел коллег спросить, кто как именует чистые интерфейсные классы, в случае если везде используется snake_case ?
По сути вопроса не подскажу, так как всегда используется другая нотация. Просто по мелочи попридираюсь
V>На всякий случай, используемая терминология:
V>sname_case
V>
Я так понимаю, имелся в виду snake_case, а то у тебя везде по тексту какой-то непонятный зверь sname_case
V>В свете переписывания библиотечного кода на кроссплатформу, принято решения использовать sname_case, как у стандартной библиотеки С++. С этим проблем нет. На 99,9% код использует шаблоны и статический полиморфизм. От сюда duck typing и никаких приседаний с именованием не требуется. Но в некоторых местах всё же необходима динамический полиморфизм и там присутствуют интерфейсы типа IBase и т.д. V>Хотел коллег спросить, кто как именует чистые интерфейсные классы, в случае если везде используется sname_case ?
Не использую snake_case, вернее, ограниченно использую в тех частях, которые типа косят под стандартную библиотеку. В остальном коде — PascalCase, функции и мемберы — camelCase. Позволяет визуально разделять C++ сущности, свои сущности, и третьесторонние сущности
V>Первый случай — не очевидно, что это файл интерфейса или класс интерфейса. V>Второй случай — не привычно.
Ничего, привыкнешь
V>Еще есть какие-нибудь варианты?
IPascalCase
Не вижу проблемы в миксе форматирования. Интерфейсы — всегда PascalCase с префиксом I, реализации — snake_case, например
Здравствуйте, Videoman, Вы писали:
V>Хотел коллег спросить, кто как именует чистые интерфейсные классы, в случае если везде используется snake_case ?
Иногда бывает необходимость выделить корень какой-то иерархии классов и тогда хочется, чтобы в названии было что-то, что бы говорило, что это уже корень, отсюда все и растет. В таких случаях название начинается с abstract_, вроде abstract_message_mbox, abstract_message_sink, abstract_connection_pool.
Иногда используется суффикс _iface, вроде notificator_iface, controller_iface, consumer_iface. Правильнее, наверное, было бы использовать _interface, но слишком уж длинные названия получаются. При этом _iface не обязательно означает абстрактный класс, иногда это вполне себе обычный класс, просто дающий какой-то другой способ доступа к объекту. Что-то вроде:
// Публично-доступная часть.namespace impl {
class private_consumer_iface;
}
class consumer {
friend class impl::private_consumer_iface;
...
public:
void a();
void b();
...
};
// Приватная часть, детали реализации.
// Скорее всего, другая единица трансляции.namespace impl {
class private_consumer_iface {
public:
void c(consumer & target);
void d(consumer & target);
...
};
}
Но вообще жизненный опыт показывает, что в каком-то специальном именовании абстрактных классов смысла нет. Т.к. вначале кажется что abstract_ или _iface в названии как-то помогают, а через N лет оказывается, что то, что было чистым _iface со временем обросло и какими-то невиртуальными вспомогательными методами. А какой-нибудь abstract_ раньше был самым корнем иерархии, но со временем он сам стал производен от каких-то дополнительных классов.
Здравствуйте, so5team, Вы писали:
S>Но вообще жизненный опыт показывает, что в каком-то специальном именовании абстрактных классов смысла нет. Т.к. вначале кажется что abstract_ или _iface в названии как-то помогают, а через N лет оказывается, что то, что было чистым _iface со временем обросло и какими-то невиртуальными вспомогательными методами. А какой-нибудь abstract_ раньше был самым корнем иерархии, но со временем он сам стал производен от каких-то дополнительных классов.
интерфейсы желательно наследовать виртуально, поэтому такой префикс все-же нужен.
Здравствуйте, so5team, Вы писали:
NB>>интерфейсы желательно наследовать виртуально
S>Никогда так не делал, и даже сходу не припомню случая, когда это было бы нужно. S>Но, наверное, если без сурового множественного наследования никуда, то такое правило может иметь право на жизнь.
проектирование на интерфейсах как раз предполагает суровое множественное наследование.
а поскольку у интерфейсов часто бывает общая база, вот и выходит что без него никуда.
Здравствуйте, so5team, Вы писали:
NB>>а поскольку у интерфейсов часто бывает общая база
S>Вот с такими вещами практически не сталкивался. Разве что по молодости и сейчас уже не вспомню.
например тот же IRefCountedBase -- типа у нас все объекты с подсчетом ссылок, чтобы можно было свободно использовать ref_ptr<ISome>.
еще какие-нибудь общие вещи может захотеться по типу комовского IUnknown.
Здравствуйте, night beast, Вы писали:
S>>Вот с такими вещами практически не сталкивался. Разве что по молодости и сейчас уже не вспомню.
NB>например тот же IRefCountedBase -- типа у нас все объекты с подсчетом ссылок, чтобы можно было свободно использовать ref_ptr<ISome>.
Сценарий понятен, но для моей практики очень уж экзотичен.
V>В свете переписывания библиотечного кода на кроссплатформу, принято решения использовать snake_case,
Использовать для чего? Следует разделять именование типов и именование существующих в рантайме сущностей:
объектов, переменных функций.
На мой взгляд для первого (типов) можно использовать CamelCase и snake_case для второго (функций, объектов).
И camelCase для объектов и CamelCase для типов. Смысл в том, что удобно видеть где тип, а где объект/функция.
V>Еще есть какие-нибудь варианты?
Оставить CamelCase для типов, не заниматься идиотскими переименовываниями и не сношать мозг.
Здравствуйте, night beast, Вы писали:
NB>интерфейсы желательно наследовать виртуально, поэтому такой префикс все-же нужен.
Не-не-не! Я ни за что не агитирую, каждый пусть делает как хочет, но в свое время я наелся с виртуальным наследование — во !!! У меня правило-ограничение, никогда не наследовать множественно реализацию. Только абстрактные интерфейсы, без реализации, и только агрегирование. Типа как в COM, где за 10 лет никогда не использовал никакого виртуального наследования и не жаловался.
Здравствуйте, Videoman, Вы писали:
NB>>интерфейсы желательно наследовать виртуально, поэтому такой префикс все-же нужен. V>Не-не-не! Я ни за что не агитирую, каждый пусть делает как хочет, но в свое время я наелся с виртуальным наследование — во !!! У меня правило-ограничение, никогда не наследовать множественно реализацию. Только абстрактные интерфейсы, без реализации, и только агрегирование. Типа как в COM, где за 10 лет никогда не использовал никакого виртуального наследования и не жаловался.
так даимонд проблема не связана с тем, абстрактный интерфейс или нет.
у тебя один интерфейс наследует два других с общей базой -- вот уже и конфликт.
Я раньше придерживался правила, что суффикс Base в названии класса означает интерфейс. Например, PersonBase, VehicleBase.
Альтернативный вариант начинать с I (IPerson, IVehicle) был еще раньше. Но тогда была популярна венгерская нотация и в целом такое было принято в COM. И в кроссплатформенном мире такое сильно неприятно, да и лично мне визуально не нравится.
В итоге я пришел к тому, что называю классы без всяких префиксов (Person, Vehicle). Т.к. само название класса само по себе должно давать понимание, что это за класс. И второе, пользователю класса должно быть пофигу, абстрактный он, или нет. В крайнем случае, он это поймет из контекста или из документации. Ну и помимо прочего, IDE давно уже с легкостью показывают и иерархию, и метки для абстракных/перегруженнных/наследуемых методов.
Здравствуйте, night beast, Вы писали:
NB>так даимонд проблема не связана с тем, абстрактный интерфейс или нет. NB>у тебя один интерфейс наследует два других с общей базой -- вот уже и конфликт.
А в чём конфликт?
Здравствуйте, night beast, Вы писали:
NB>так даимонд проблема не связана с тем, абстрактный интерфейс или нет. NB>у тебя один интерфейс наследует два других с общей базой -- вот уже и конфликт.
А мне кажется связана. Виртуальное наследование это же про данные. Нет данных — нет виртуальной базы. Чистым интерфейсам не только виртуальное наследование не нужно, но и таблица виртуальных функций — всё хранится в классе.
Вот, пример:
Здравствуйте, Videoman, Вы писали:
NB>>так даимонд проблема не связана с тем, абстрактный интерфейс или нет. NB>>у тебя один интерфейс наследует два других с общей базой -- вот уже и конфликт.
V>А мне кажется связана. Виртуальное наследование это же про данные. Нет данных — нет виртуальной базы. Чистым интерфейсам не только виртуальное наследование не нужно, но и таблица виртуальных функций — всё хранится в классе.
V>Какая разница через какой интерфейс вызов, если данные всё равно расшарены
ты не работаешь напрямую с объектами, ты работаешь с интерфейсами:
struct IBase {
virtual ~IBase() {}
};
struct IA : IBase {
};
struct IB : IBase {
};
struct IC : IA, IB {
};
int main()
{
IC * c = new IC();
IBase* x = static_cast<IBase*>(c); // опаньки
}
Здравствуйте, DiPaolo, Вы писали:
DP>...
DP>В итоге я пришел к тому, что называю классы без всяких префиксов (Person, Vehicle). Т.к. само название класса само по себе должно давать понимание, что это за класс. И второе, пользователю класса должно быть пофигу, абстрактный он, или нет. В крайнем случае, он это поймет из контекста или из документации. Ну и помимо прочего, IDE давно уже с легкостью показывают и иерархию, и метки для абстракных/перегруженнных/наследуемых методов.
В том-то и проблема, что не хочется конфликтов и головняка как отличить Person от интерфейса с таким же названием. Хочется типа такого:
namespace i
{
struct person;
}
class person: public i::person
{
// ..
};
Здравствуйте, Videoman, Вы писали:
V>Тут да, неоднозначность получается. Тогда действительно можно указать virtual, показав, что мне всё равно что компилятор будет звать:
не все равно.
будет один IBase. это усложняет работу с поиском нужного метода, появляются доп смещения.
в общем, насколько понимаю, такие вызовы дороже обходятся (но это не точно)
ну и сама компиляция усложняется. деталей не помню но при виртуальном наследовании где-то идет комбинаторный взрыв и память расходуется в больших объемах.
по крайней мере раньше что-то вроде:
быстро ложило компилятор.
V>Только не понятно, что изменится в самом классе в таком случае, кроме подавления ошибки компилятора, при обращении к такому методу. Интересно...
все усложняется, поэтому плохо что в плюсах нет полноценных интерфейсов.
Здравствуйте, night beast, Вы писали:
NB>не все равно. NB>будет один IBase. это усложняет работу с поиском нужного метода, появляются доп смещения. NB>...
Здравый смысл по прежнему отказывается это понимать. Откуда там смещения, если нет данных? По сути речь идет о статической структуре таблиц виртуальных функций, причем только одного объекта — object. Или вы хотите сказать, что указатель на таблицу виртуальных функций и будет являться не явными данными всех классов ? Почему компилятор это не оптимизирует, если мне по сути без разницы через что вызывать?
Здравствуйте, Videoman, Вы писали:
NB>>не все равно. NB>>будет один IBase. это усложняет работу с поиском нужного метода, появляются доп смещения. NB>>...
V>Здравый смысл по прежнему отказывается это понимать. Откуда там смещения, если нет данных? По сути речь идет о статической структуре таблиц виртуальных функций, причем только одного объекта — object. Или вы хотите сказать, что указатель на таблицу виртуальных функций и будет являться не явными данными всех классов ?
да. как только ты добавляешь в класс виртуальную функцию у объекта (экземпляра класса) появляется указатель на таблицу виртуальных функций.
виртуальное наследование к этому добавляет доп. сложностей (сейчас, к сожалению, уже не найду статью с описанием внутренней реализации)
V> Почему компилятор это не оптимизирует, если мне по сути без разницы через что вызывать?
потому что компилятор во время компиляции не знает, какой метод будет вызван
V>В том-то и проблема, что не хочется конфликтов и головняка как отличить Person от интерфейса с таким же названием. Хочется типа такого:
Я придерживаюсь мнения, что это должно быть понятно из названия. Более конкретно (я так понимаю вы работаете с видео):
Stream — очевидно, что это абстрактный класс. VideoStream — тоже абстрактный класс. AvcStream — конкретный класс для реализации H.264/AVC. То есть по контексту и физической сути класса должно быть понятно, что есть что. ИМХО, всякие IStream тут излишни, т.к. понятно, что stream (поток) — это абстрактный поток.
Здравствуйте, Videoman, Вы писали:
V>В том-то и проблема, что не хочется конфликтов и головняка как отличить Person от интерфейса с таким же названием. Хочется типа такого:
Если у тебя person это и интерфейс, и конкретный класс, занчит у тебя что-то не правильно с твоими абстракциями.
Здравствуйте, Kernan, Вы писали:
K>Если у тебя person это и интерфейс, и конкретный класс, значит у тебя что-то не правильно с твоими абстракциями.
Это можно знать только задним числом, разобравшись в иерархии. Я привел пример просто как иллюстрацию, что в общем виде, взглянув на такое имя без контекста, не понятно, что это, реализация или интерфейс. Мой вопрос, кто как это обозначает или не обозначает, просто для обмена опытом.
Есть еще одна специфика, у себя я практически не использую интерфейсы в чистом виде. У меня не COM в общем понимании. Обычно интерфейсы появляются там, где без динамического полиморфизма ну уж совсем не обойтись, что бы в функцию можно было подсунуть любой внешний сторонний класс при необходимости. Типа такого:
namespace media
{
class sample : public i_sample
{
// ..
};
}
У меня самого в библиотеке класс почти всегда один и по сути это default implementationи и она всегда устраивает, а интерфейс может понадобиться для внешнего кода. Поэтому называть все классы default_something, просто из-за наличия интерфейса мне бы не хотелось.
Потом, выделять интерфейс в имени это не я придумал, так много где делают, например Microsoft и Google те же. Проблема в том, что у них другой стиль именования и он мне не подходит.
Здравствуйте, Videoman, Вы писали:
V>Еще есть какие-нибудь варианты?
Одна из самых понятных и универсальных нотаций, это простая С нотация основаная на snake case, в которой для обозначения типа используется суффикс _t:
clock_t
date_t
uuid_t
Подобный подход можно всретить даже в самом стандарте C и его стандартных библиотеках. Если продолжить эту идею, то для интерфейсов можно было бы использовать суффикc _i:
base_i
Второй очень понятный вариант — это классический pascal сase:
IBase
Наверное вот это и всё. Это два самых жизнеспособных варианта, только потому, что всё сразу понятно даже для абсолютно посторонних людей.
Не вижу никаких преимуществ аббревиатуры-префикса перед полными словами: window_base или WindowBase >> IWindow. Использование нормальных слов ближе к духу современного C++.
В целом проще всего следовать какому-то хорошо определенному стилю: std/boost или Qt.