Pull to refresh
102
Karma
21
Rating
Евгений @Izaron

Программист C++

Кодогенератор Waffle++ для C++

Можно считать, что сам C++ это тоже кодогенератор для ассемблера.

Кодогенератор Waffle++ для C++

Это особенность работы с шаблонами в C++. Пусть есть объявление такой шаблонной функции:

template<typename EnumType>
EnumType FromInt(int value);

Тогда, подставляя вместо EnumType разные типы данных (например FromInt<Color>(100500), мы получим разные функции - по одной функции на каждый EnumType. Шаблонная функция (и вообще шаблонный код) это просто заготовка реального кода.

Вывод компилятора с двумя функциями

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

В какой-то совсем внешней библиотеке эти определения находиться не смогут, потому что внешние библиотеки не имеют никакого понятия про типы данных в конкретной программе. Пусть внутри программы есть тип enum Foo, тогда программа должна сама заботиться о наличии определения функции FromInt<Foo>.

nocc — распределённый компилятор для гигантских проектов на С++

Плохо работает - все файлы просто один за другим "склеиваются" в один большой файл, поэтому локальные имена конфликтуют.

Если через некоторое время отказаться от данного подхода, то придется руками написать овер9000 новых include, которые раньше не писали, потому что их подключал какой-нибудь другой файл, который в "склейке" идет раньше.

В IT в 30. Как я стал solo Kaggle Grandmaster, устроился на работу, но так и не стал программистом

купить максимальное количество лотов за минимальные деньги

А это не задача о рюкзаке? Часто встречается в задачках по программированию - правда, в таких ограничениях, чтобы можно было найти оптимальное решение.

Holy C++

Не вопрос стандарта языка, аби хранят реализации, а в стандарте такого понятия нет

Ситуация из разряда "жопа есть, а слова нету". Почему нельзя удалить из стандарта ставший ненужным с C++11 метод push_back? (А точнее, переписать его код вместо создания нового метода emplace_back)

Потому что ABI сломается. Хотя, казалось бы, про ABI в стандарте ничего не написано.

Я бы, кстати, еще AST (Abstract Syntax Tree) в стандарт "C++ мечты" легализовал, чтобы люди могли писать тулзы для исходного кода без привязки к внутренностям компилятора.

Зато есть куча другого кода вне стандарта

95% людей устроило бы наличие любой (даже не самой быстрой) json-либы (как в питоне). Если бы была нужна какая-то мега-быстрая json-либа (в realtime-приложениях), то там уже могут быть варианты, но свои трейд-оффы есть везде.

Предложенное статье сокращает количество правил. Значительно

Предложенное в статье главным образом создает какой-то совершенно другой язык.

Достаточно быстрая компиляция, относительно возможностей языка.

Я имел в виду "класс" проблем, которые именно вызывают медленную компиляцию. Например, из-за некоторых свойств препроцессора тулза include-what-you-use в 10 раз толще, чем нужно быть, и все равно работает не совсем как следует. Значит, нужно порезать препроцессор. Просто не стал все это расписывать, иначе комментарий получился бы длиннее статьи.

Cmake

Многие считают это худшей системой сборки ever. Если приводить все аргументы, выйдет тоже немаленький текст, но я читал бомбежку на CMake (сам его редко использую) - меня впечатлило.

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

95% людей были бы согласны на любой нормальный спеллчекер в "C++ мечты". Да и что значит "не должны", "не могут". C#/Java/Python/Rust - у них все есть и они все могут.

Holy C++

Некоторые пункты отдают легким троллингом. Я бы еще предложил оставить в функциях не более 1 аргумента, а вызов функций с N аргументами имитировать в виде шаблонной магии.

Реальные проблемы в C++ выглядят не так. Вот так выглядят "классы" проблем, после фикса которых можно получить "C++ мечты":

  1. Вопрос о сломе ABI и всего веера положительных и отрицательных эффектов из-за этого.

  2. Бедность стандартной библиотеки по сравнению с буквально всеми другими языками (тут ABI ни при чем).

  3. Поехавший нейминг и правила, например овер9000 значений слова static при разных обстоятельствах; или что anonymous namespace наделяет все символы внутри себя internal linkage-ом (как до такого додумались?).

  4. Медленная компиляция по разным причинам.

  5. Отсутствие общепринятого менеджера пакетов/библиотек (тут могут быть разные мнения, но это тоже "класс" проблем)

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

  7. Отсутствие встроенных линтеров и чекеров (которых овер9000, но ими пользуются не только лишь все, а только самые продвинутые).

А "фиксы", которые удаляют ООП и т.д. - это не решение всех проблем C++, а описание какого-то нового языка.

RVO и NRVO в C++17

Есть много кейсов, где может применяться NRVO. В paper описано 20 кейсов, сейчас Clang делает это только в 13 кейсах (13/20). Мой патч добавил бы еще 4 кейса (было бы 17/20).

https://reviews.llvm.org/D119792 - тут я описал, что делаю.

Возражения там по делу, но просто стало влом тащить до конца, потому что КПД контрибьютинга в подобные опенсорсные проекты очень низок.

RVO и NRVO в C++17

Это хорошая тема! Антон Жилин писал paper (предложение в Стандарт) о гарантированном NRVO: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2025r2.html

Сейчас ни один компилятор не поддерживает все описанные в paper кейсы. Я отправлял патч в Clang, чтобы покрыть на 20% больше кейсов, чем поддерживается сейчас, но до конца не добил тему, потому что патчи медленно принимаются.

Гибкая индексация элементов в контейнере на С++ и при чём тут Boost.MultiIndex

К сожалению сам Boost.MultiIndex остался "черным ящиком" - неизвестно внутреннее строение контейнера.

Я придумал схему, по которой сами объекты хранятся в "стандартном" контейнере, а "индекс" это объект `std::multiset<const Person*, comp>`, то есть в индексе лежит указатель на них.

Тут реализация: https://godbolt.org/z/ne895x73f, но не придумал как быстро искать объект по ключу, потому что std::find_if линейный

Концепция умного указателя static_ptr<T> в C++

С этим я согласен, только замечания: не "программистский", а "вычисляторский"; не "жаргон", а "говор"; не "автор", а "создатель"; не "школа", а "училище". Слова заморского происхождения не пройдут!

Концепция умного указателя static_ptr<T> в C++

На стеке много объектов выделить не получится, он обычно маленький (8192 KiB). А как вы будете аккуратно аллоцировать объекты разных размеров? Тут свой головняк начнется, это не проще.

Для "маленьких объектов" есть memory pools (https://betterprogramming.pub/c-memory-pool-and-small-object-allocator-8f27671bd9ee)

В принципе static_ptr оторван от всяких кастомных аллокаторов, это перпендикулярно к нему идет. Можно считать, что static_ptr это такое представление std::unique_ptr<T>, где указатель T* t и, хм, объект *t находятся "рядом" by desing.

Концепция умного указателя static_ptr<T> в C++

Наверное картинки в статье вводят всех в заблуждение. Объекты sp::static_ptr<T> не живут только на стеке.

Например в std::vector<sp::static_ptr<T>> alloca/VLA ничем не помогут. Почему, например, такой вектор круче - описал тут https://habr.com/ru/post/665632/#comment_24343986

"Динамический буфер" - буфер все таки статический, хотя в compile-time проверяется что объекты туда залезут.

Концепция умного указателя static_ptr<T> в C++

Как быстро получить указатель на базовый класс? У меня вышло так:

using TEngine = std::variant<TSteamEngine, TRocketEngine, TEtherEngine>;
// ...
IEngine* GetEngine(TEngine* engine) {
    if (auto ptr = std::get_if<TSteamEngine>(engine)) return ptr;
    if (auto ptr = std::get_if<TRocketEngine>(engine)) return ptr;
    if (auto ptr = std::get_if<TEtherEngine>(engine)) return ptr;
    return nullptr;
}

std::variant из всех наследников выглядит как-то жутковато) Но идея похоже рабочая

P. S. Только бы еще оттуда удалить copy constructor и copy assignment operator...

Концепция умного указателя static_ptr<T> в C++

Хранимый объект допускает перемещение.

Кстати, std::vector<T> как раз требует, чтобы объект T был перемещаемым или хотя бы копируемым. А то не скомпилируется кусок кода отвечающий за перемещение объектов при переаллокации.

(Соответственно этого не требуется для std::list и подобных контейнеров)

Концепция умного указателя static_ptr<T> в C++

А в описанном случае с выделением памяти ..., сгодится и "глупый" си-шный указатель, разве нет?

А где будет находиться объект, куда указывает "глупый" указатель? В куче не может - цель уйти от кучи. На стеке может только в aligned_storage, а из этого вытекают разные вопросы, которые попытался решить в статье.

sp::static_ptr кстати решает еще одну специфическую проблему - теперь объект невозможно случайно скопировать (передать по значению, etc.)

не позволяет даже просто переиспользовать объект без полной передачи или вложения в другой объект

К сожалению не понял, что имеется в виду под "нельзя переиспользовать объект". Указываемый объект возможно использовать также, как в других умных указателях:

sp::static_ptr<TObj> p;
// ... в `p` лежит объект
TObj obj{std::move(*p)};

Концепция умного указателя static_ptr<T> в C++

Пусть есть виртуальный абстрактный класс IEngine и его наследники TSteamEngine, TRocketEngine, TEtherEngine.

Нужно завести контейнер из объектов, чей тип - какой-то наследник IEngine. Как вы это сделаете?

Стандартный подход: std::vector<std::unique_ptr<IEngine>>.

Этот подход значит, что в куче лежит память вектора для N объектов Engine*, каждый объект указывает еще куда-нибудь в кучу в рандомное место (и каждый раз при добавлении объекта происходит аллокация).

Подход с std::vector<sp::static_ptr<IEngine>> значит, что в куче лежит память вектора для N объектов размера static_ptr_traits<IEngine>::buffer_size, и больше ничего, это круче из-за локальности памяти.

Концепция умного указателя static_ptr<T> в C++

Действительно, идея "витает в воздухе", но здесь позволю себе не согласиться с вами 🙂 Какая мотивация у Антона:

  1. Нужно реализовать идиому PImpl, чтобы быстрее компилировалось (убрался инклюд) и т.д.

  2. "Стандартный подход" заключается в замене T на std::unique_ptr<T>, потому что там ок чтобы T был incomplete type (а у меня кстати не так, мне нужен T complete).

  3. Этот подход медленный из-за кучи, поэтому T заменяют на обертку над std::aligned_storage<sizeof(T), alignof(T)>, причем эти два числа надо посчитать руками.

И там совсем не про "динамический полиморфизм на стеке", тип жестко фиксирован. Из общего только использование aligned_storage...

Про вторую заметку: интересно, какие есть кейсы, где используются over-aligned типы? Я сам с таким еще не сталкивался.

Концепция умного указателя static_ptr<T> в C++

Устаревший он с C++23. Компилятор может скомпилировать с ворнингом что "это фича из более нового стандарта", но может и не скомпилировать. Это пока в тестовом формате.

Начиная с C++23 было бы так:

alignas(align) std::byte buf_[buffer_size];

Неклассические контейнеры в C++

Думаю что это сделано из соображений перфоманса.

С вашим дизайном было бы так: разыменовать (1) ссылку на вектор, посмотреть на .data(), разыменовать (2) сам объект в нужном адресе.

С текущим дизайном разыменование одно, так как сразу знаем нужный адрес.

Можно считать что итератор станет работать в 2 раза медленнее.

Неклассические контейнеры в C++

К сожалению, не пользовался библиотекой Bitmagic. Прочитав про нее, увидел что это очень мощная библиотека. Про устройство - последовательность битов там может иметь очень разные представления. Все зависит от сценариев использования, мне хватало функционала bitset и dynamic_bitset.

Единственно (на мой взгляд) минус в том, что библиотека Bitmagic является header-only, это негативно влияет на время компиляции.

Information

Rating
264-th
Location
Ян де нова о-ва
Registered
Activity