Pull to refresh

Comments 38

А можно ли было не удалять объявление функции?

И да, оказалось, что есть второй способ

Правда в заголовочном файле все-таки пришлось убрать ее объявление

Кругом обман :(

Сначала надо было определиться, нужна ли функция во внешнем интерфейсе, а уже потом решать технические вопросы скрытия.

А потом, когда проект ещё чуть подрастёт и заходят ускорить сборку на билд агентах, начнут использовать unity build и отгребут по полной. На некоторых оно может будет собираться, на других - нет. И отлаживать такое будет крайне неудобно.

Для си++ такие вещи решаются именованными, а не анонимными неймспейсами.

А можно подробнее? Как анонимный неймспейс для функции, используемой только в одной единице трансляции, помешает билду?

This is done by creating a (set of) unity sources which #include the original sources

Спасибо за информацию. Не сталкивался и постараюсь держаться подальше - слишком эффективное средство для стрельбы по ногам (особенно когда в кодовой базе полно legacy и third party).

Ну если писать на си++ с кучей глобальных функций и переменных в процедурном стиле на уровне джуна - то да. Но в более менее серьезных проектах, unity build - это распространённая практика.

Навскидку зависимости между third party/системными заголовками (в духе включать winsock2.h строго до windows.h), всяческие макросы влияющие на поведение заголовков типа _POSIX_C_SOURCE тоже приведут к интересным эффектам.

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

Если сразу использовать unity build, зная его специфику и явно прописывая исключения - да. Если уже есть десятки мегабайт кода, написанные в разное время разными командами - то использовать опасно. Хорошо ещё, если код просто иногда не билдится - такое можно быстро обнаружить и пофиксить, а вот если билдится всегда, но в зависимости от объединения файлов меняется семантика работы - тут похуже.

Ничего опасного, не придумывайте. Я год назад добавлял такую фичу в кросс-платформенный проект. Не скажу что сильно большой, примерно 5 лет командой из 6-8 человек делался. Со всеми код ревью, доработкой документации по кодстайлу и тестированием ушло 3 дня.

Никогда бы не подумал, что прочитаю такую простыню о такой наитупейшей вещи как static функции :)

Вот вас ждут еще открытия, когда вы для себя откроете inline :)

Когда деревья были выше, а трава зеленее то сначало читали толмуд Страуструпа, а только потом нажимали кнопки на клавиатуре. Сегодня же сначало пишут программу, а что то читать начинают когда вылезают проблемы. И это хорошо, интернет позволяет так делать, а раньше так бы не получилось. Но да, очень забавно наблюдать как элементарная проблема вызвала столько бурлений.

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

Но давайте признаем, что виноват на самом деле страус, изобретая с++ он почему то решил что модули это для слабоков

Страуструп был вынужден использовать имевшийся тогда Си, в котором никаких модулей не было. А почему был выбран Си тот же Страуструп многократно и подробно рассказывал, емнип, в "Дизайн и эволюция языка C++".

Да, и т.е. спустя сколько там 40 лет комитет таки запили модули и ничего не отвалилось. Мало того он и сам из хотел сделать, но типа и так пойдет. Си здесь вообще не причем.

Да, и т.е. спустя сколько там 40 лет комитет таки запили модули и ничего не отвалилось.

Об этом рано говорить, модули пока что используют не только лишь все.

Мало того он и сам из хотел сделать, но типа и так пойдет.

Он и auto изначально хотел использовать для автоматического вывода типов. Но "совместимость" с Си не позволила.

Си здесь вообще не причем.

Ну да, ну да. Взлетел бы C++ если бы он бы не помесью Симула и Си, а помесью Симула и Модула-2 (к примеру) -- большой вопрос. По факту же C++ смог несколько десятилетий жить за счет экосистемы Си (начиная от линкеров и форматов объектных файлов, заканчивая бесплатной интеграцией с Си-шным кодом). Как-то странно слышать, что "Си здесь вообще ни при чем".

Конечно не причем, наймспэйсы то были, да и вообще свой не совместимый с си манглин имён с самого начала. Модули в этом плане очень рядом конкретно Си никак немашал их запилить ещё тогда.

Конечно не причем, наймспэйсы то были

Изначально их не было. Они появились в C++ только в 1990-ом году, когда язык уже стал популярным за счет своей совместимости с Си.

Более того, шаблонов в C++ так же изначально не было. И выкручиваться с обобщенным программированием на C++ тогда приходилось за счет Си-шного препроцессора с define-ами.

Ну я историю с++ так детально не знаю, но период до шаблонов вообще можно не считать потому что это не с++, а фигня какая-то, которой пользовались 3 калеки.

Так историческая справка, забавно что первый раз про с++ я узнал из та-дам телевизора в 98 году, из новости что наконец то родили ёжика, с++98. Помню как показывали радующегося страуса. Я тогда из программирования мог только - да ничего толком.

Помню, как тогда гонялись за третьим изданием книги Страуса с описанием уже стандартных плюсов. При том что реально на них уже давно писали.

Ну я историю с++ так детально не знаю

Для вас это всего лишь история, для меня -- собственный опыт.

но период до шаблонов вообще можно не считать потому что это не с++, а фигня какая-то, которой пользовались 3 калеки.

Чуть больше, по некоторым оценкам количество программистов на C++ в 1991-ом году оценивалось где-то в 400K.

Ну и эта самая "фигня какая-то" была сильно круче тогдашних конкурентов (коих и не сказать, чтобы было много на конец 1980-х и начало 1990-х).

Забавно, что для некоторых C++ в то время не был отдельным языком программирования, а воспринимался просто как следующая версия Си, в который добавили пару-тройку новых ключевых слов. Тем более, что одним из "selling points" C++ всегда было то, что можно было взять уже имеющийся код на Си и с минимальными переделками начать использовать его как код на C++ (собственно перевод кодовой базы GCC на C++ как раз лишний тому пример). Даже без классов и перегрузки операторов. Просто ссылки вместо указателей.

Так что если бы C++ не выглядел в начале своей жизни именно как нашлепка над Си, то вряд ли он обрел бы такую популярность. Мог бы запросто повторить судьбу Eiffel-я, а то и Modula-3 (при том, что Eiffel не умер, живет себе и здравствует, просто мало кому нужен).

Так по рассуждать просто. Что такого была в плюсах без шаблонов чего не было в си? Как вы уже написали перегрузки (не только операторов). Перегрузка операторов штук полезная, но нишевая. Перегрузка в общем случае лично мне очень нравится, но вот все молодежные языки объявили ее грехом и забанили. Скажем честно разобраться в правилах перегрузки функций в плюсах могут чуть менее чем никто, только компилятор знает что там должно вызываться, но мне норм. Т.е. плохо что так сложно, но по факту это редко вызывает реальные проблемы.

Что ещё? Деструкторы в в частности и раи в общем. К деструкторам у меня лично есть претензии, но раи это сила да.

Ну и собственно классы. методы/функции классов + сокрытие данных в структурах/классах + наследование + конструкторы. Виртуальные функции и так были, но стало удобней конечно. Структуры стали первыми ограниченными пространствами имён, но это так сахарок, а вот сокрытие данных это хорошо, хотя в си всегда можно сделать тоже самое через непрозрачный указатель, да будет оверхед на кучу, но с другой стороны в том же расте внутри модуля все и так паблик, работает тоже хорошо и никаких френдов не надо. Наследование реализации - как оказалось тупиковая идея и проблемная в реализации, наследование интерфейсов можно и проще сделать. Конструкторы это отдельная боль это вообще НЕ функции (адреса нет) очень переусложнены.

Семантика копирования по умолчанию - явно проигрышная идея.

Наверное ещё эксепции были, я не знаю точно. Мне эксепции нравятся, но я понял что спустя сколько там 40 лет уже большая часть плюсистов НЕ понимает этот инструмент и это (а не пресловутая производительность) является главным мотивом их не использовать.

Ссылки появились только ради операторов и не доделанные они до сих пор. Что в них хорошо это то что они константность переносят на сам объект и всегда не нул.

Подытожим. Перегрузка удобно, но иногда отстреливает ноги; операторы хорошо, но второстепенно; раи очень хорошо; деструкторы не без проблем; инкапсуляция норм; наследование уж лучше бы его НЕ добавляли; эксепции хорошо, но люди не понимают; ссылки не доделанные; конструкторы переусложнены; семантика копирования по умолчанию плохая идея.

Т.е. безусловно хорошая и новая фича по сравнению с си только раи.

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

Простите за оффтоп, но можно привести примеры когда нужно и когда не нужно писать static inline к строке типа
static inline constexpr uint8_t i= 1;
Я унаследовал кодовую базу C и перевожу ее на C++. Соответственно заменяю #define на static inline constexpr. Нужно ли тут писать static inline?

Короткий ответ - inline constexpr

Почему?

constexpr означает const. Конст для переменных объявленных в глобальном скоупе (а именно там обычно дефайнят константы в си) подразумевает static. Поэтому constexpr и static constexpr для переменных в глобальном скоупе это синонимы.

НО статик означает интернал линкэдж, это значит что в каждом цпп файле где инклудится хидер с глобальным констэкспром МОЖЕТ (но не обязан) иметь отдельный сторэдж под эту переменную, что может привести к росту размера бинарника. Если у такой переменной берется адрес, то тогда ТОЧНО будет выделен отдельный сторэдж в каждом цппшнике где берется адрес.

Зачем нужен инлайн? Инлайн означает экстернал линкэдж, это значит что ВСЕ переменные обязаны иметь ровно один сторэдж в независимости от того сколько цппшников берет адрес. Это может не позволить расти размеру бинарника.

На что только ни пойдут разрабы, лишь бы названия нормальные не придумывать. Компилятор-то можно настроить как угодно. Но языки программирования ведь придуманы прежде всего для людей, чтобы они понимали, что происходит. И сами же дают такие названия из разряда value для переменной

Правда в заголовочном файле все-таки пришлось убрать ее объявление

Как то между делом упомянули, хотя изначально проблема в основном в этом - разные функции с одной сигнатурой в публичном интерфейсе двух компонент.

Тема важная и нужная, но пример подкачал. Самое главное, когда один раз так стрельнешь, уже ни разу не забудешь обернуть в неймспейс)

Спасибо. Да, примеры в рассказах много значат, наверное можно было бы придумать лучше. В процессе написания они несколько раз переписывались, пока не остановился на некотором компромисе.
Мне сильно подходит способ изучения языка через решение проблемы. Так более детально можно погрузиться в конкретную область, а таких областей в C++ предостаточно. Чуть выше @Playa привел ссылку на прекрасный материал по спецификаторам, спасибо большое. Она мне не раз попадалась на глаза. Только она написана как справочник с большим объемом информации. За одно прочтение думаю мало кто осилит все переварить и сразу использовать в работе. Можно к ней обращаться когда уже немного в теме и набил шишек на собственных ошибках.
И да, эта статья изначально задумывалась как развлекательная с элементами теории и практики. Для себя хотелось бы на Хабре видеть больше материала в таком стиле.

А просто использовать namespace слишком сложно? Или согласовать имена функций?

Или согласовать имена функций?

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

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

Однажды мне потребовалось включить проект 2 библиотеки на Си, написанные одним человеком и делающие однотипные вещи, но для разных стандартов.

Естественно, тот человек называл интерфейсные функции одинаково.

Как С++ тот код не скомпилировался, чтобы обернуть в неймспейс.

Пришлось загружать их динамически и работать с ними по-очереди.

Вроде же можно включать библиотеки из С в отдельный неймспейс, не?

extern "C" namespace abc {
  #include <clib>
}

Я не знаком с таким синтаксисом, но с виду он не решает проблему линковки дублирующихся символов.

Эта штука работает мудрёно. Когда я писал свои бинды на winapi это помогло мне избавиться от дубликатов. Наружу оно торчало как обычный symbol, а внутри код было как winapi::linkage::symbol и это работало.

ЗЫ, развлекался с модулями и смотрел как они работают

Пришлось загружать их динамически и работать с ними по-очереди.

Ну, можно было бы и работать вместе. Через тот же dlsym (или его виндовый аналог GetProcAddress) можно найти нужный символ в каждой отдельной библиотеке.

В случае GCС / Clang можно было бы изобрести костыль c помощью -Wl,--version-script, если позарез нужна статическая линковка. С MSVC пришлось бы пострадать)

Наверное, да. Потому что через dlsym и делалось. Но, в тз требования одновременно работы этих библиотек не было)

Sign up to leave a comment.

Articles