Pull to refresh
82
0.1
Евгений Охотников @eao197

Велосипедостроитель, программист-камикадзе

Send message

"Доктор, когда я делаю так, у меня тут болит"

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

Но вообще, конечно, лучше продумать архитектурку так

Ну дык понятно, что лучше быть здоровым и богатым, чем... ;)

Да.

Это может быть тупо невозможно, т.к. каждая функция может работать в своем треде и вообще ничего не знать про другую.

Ну, если он не в курсе, то он и всякие неоднозначные оптимизации применять не будет.

Так даже в таком тривиальном случае:

// Где-то в другом TU.
void create(std::span<std::byte> mem) { new(mem.data()) Demo{...}; }

// В моем TU.
void use(std::span<std::byte> mem) {
  Demo * d = std::launder(reinterpret_cast<Demo*>(mem.data());
  ...
}

компилятору нечего оптимизировать, т.к. в моем TU внутри use никаких других объектов Demo или указателей на них нет и не было. Так что здесь, имхо, start_lifetime_as была бы даже логичнее.

Внутренней бухгалтерией компилятора.

Жаль, что она как-то явно не описана :(

Если по указателю находится объект, чей лайфтайм с точки зрения компилятора уже начался, то нужно использовать std::launder

Допустим, у нас есть буфер, в который мы загрузили значение из файла/пайпа. После чего передали указатель на этот буфер в две разные функции. Первая в C++23 должна использовать start_lifetime_as. Тем самым она начинает время жизни объекта.

Что должна делать вторая? Вызывать launder, т.к. первая уже лайфтайм для объекта начала? Или можно еще раз вызвать start_lifetime_as.

Плюс тому, launder/start_lifetime_as ограничены скоупом, в котором мы хотим использовать указатель на что-то. Компилятор понятия не имеет что происходит в других TU, в которых значение по указателю формируется. ХЗ был ли там уже начат лайфтайм или нет.

Если для более раннего стандарта, то там UB.

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

std::start_lifetime_as нужен для создания объекта с состоянием уже хранящимся в байтах, где он будет размещен.

Насколько я знаю, start_lifetime_as не создает объект, а говорит компилятору о том, что появился новый объект о котором компилятор ранее не знал (т.е. он не был создан через new, как локальная переменная на стеке или каким-то другим известным компилятором способом). Т.е. программист говорит компилятору: вот в этой области памяти сейчас есть объект, пожалуйста, поверь, что это так.

В этой связи мне не понятно, чем область памяти, заполненная, например, чтением байт из файла или из пайпа, принципиально отличается от области памяти, в которой объект был ранее сконструирован через placement new.

У вас как раз launder нужен после того, как вы создали объекты через placement new, но типизированные указатели на них нигде не сохранили.

Я понимаю роль launder вот в таком сценарии: https://wandbox.org/permlink/wh0QVJSGhu41P0LH
И, насколько помню, именно для этого launder и создавался.

Но вот другой сценарий: https://wandbox.org/permlink/wTISrxXoKMlw0PUv
(к сожалению, не удалось протестировать с start_lifetime_as, похоже, эту фичу пока еще в компилятор не завезли).

Здесь make_and_use_object для конструирования объекта вызывает разные функторы. И вот после возврата из функтора что должно применяться -- launder или start_lifetime_as? Ведь каждый из функторов заполняет переданный буфер по-разному?

И для launder здесь, вроде как, места нет, т.к. не было ранее объектов Data, на которые у меня могли бы быть указатели, и эти объекты бы пересоздавались.

В общем, я к тому, что после введения в язык start_lifetime_as стало не очень понятно в каком случае требуется launder, а в каком start_lifetime_as.

Да, есть такое дело.
Но ведь в C++23 завозят std::start_lifetime_as и, поскольку в статье речь идет о C++26, может быть std::start_lifetime_as уместнее, чем std::launder?

PS. На самом деле я сам не знаю, что правильно. До C++23 вроде как std::launder необходим хотя бы для очистки совести. На вот начиная с C++23...

А точно ли здесь нужно использование std::launder?
Вроде бы объект типа T инстанциируется корректно, затем его тип не меняется, а кастинг из std::byte* в T* полностью легален.

Именно про это я и говорю

Мой ответ вам -- это был сарказм на 146%. И речь шла про то, что в программировании еще никому не удавалось не совершать ошибок.

Неужели и вы про это?

умные указатели - это заметание ошибок дизайна под ковер

Вы сейчас серьезно?

Так очевидно же, что писать на C++ без утечек памяти, use after free и double free просто: достаточно не совершать ошибок. Делов-то.

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

Во-вторых, умные указатели появились отнюдь не вместе с C++11. Я сам их начал использовать на повседневной основе во второй половине 1990-х (при этом не был пионером, а подсмотрел в чужих разработках, так что умные указатели в мире C++ уже лет тридцать как, если не больше). Ну а самый простой из них под видом std::auto_ptr был прямо в С++98.

ИМХО, этот момент имеет смысл отразить в статье. Потому что выравнивание -- это штука, про которые не все помнят, а кто-то и не знает. Скопируют ваш подход и наступят на грабли где-нибудь.

Ясно-понятно, вопросов больше не имею.

Кем и как этот размер подбирается?

При правильном проектировании сырых указателей вполне достаточно.

Это утверждение относится к игровым движкам или к программированию на C++ вообще?

Нет ли здесь:

// 4. Создаем inplace объект за счетчиком
_type* object = new (memory + counterSize) _type(std::forward<_args>(args)...);

проблем с выравниванием для типа _type?
Скажем, sizeof(counterSize) у вас 4, а выравнивание для _type -- 8.

возраст отдельных кусков кода движка

Звучит так, что "долгоживущий код" -- это редкость.

я не беру сторонние либы

А зачем их брать? Или вы их тоже ревьювите?

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

https://en.cppreference.com/w/cpp/links/libs -- этот список никакой не "официальный"
Не знаю как сейчас, а несколько лет назад на cppreference можно было зарегистрироваться и внести изменения в wiki-страничку со списком библиотек. Так что там список того, что успели и не поленились добавить на эту wiki-страничку. Попасть в список awesome-cpp чуть сложнее, там (емнип) PR отправлялся и этот PR ждал пока его примут.

А насколько в игрострое распространены случаи, когда однажды написанная кодовая база затем эксплуатируется и развивается на протяжении 10 и более лет?

Назначенная инциализация мастхев, но насколько знаю она из мира Си и не совсем стандарт до С++23(?)

Стандарт с C++20, но там есть свои особенности.

почему все так носятся с рефлексией?

Потому что ее нет, вот и кажется, что это манна небесная. А как появится, как распробуем, так и начнем мечтать еще о чем-то недоступном в ожидании чуда :)

Information

Rating
3,589-th
Location
Гомель, Гомельская обл., Беларусь
Registered
Activity