#prog #cpp
Для C++ существует такая вещь, как CppCoreGuidelines. Это набор рекомендаций и лучших практик, нацеленных на то, чтобы люди более эффективно (читай, менее ногострельно) использовали C++. Частью этих гайдлайнов является разработанный Гербом Саттером документ, описывающий формализацию времён жизни в C++ и позволяющий отлавливать использование висячих ссылок. Разумеется, от такого документа мало толку, если всё это проверять вручную, но есть lifetime profiler — форк clang, в котором реализован статический анализатор, работающий согласно документу выше. Его можно сбилдить самостоятельно и использовать локальнотолько зачем, а можно открыть godbolt.org и использовать компилятор
Начну немного издалека. В стандартной библиотеке C++ есть тип string (аналог в Rust — String). Это изменяемая строка из символов в неопределённой кодировке, хранящая данные в куче (как правило, ибо стандарт не запрещает использовать small string optimisation, когда маленькие строки хранятся на стеке, а не в куче). Самое главное — это владеющий тип: когда значение типа std::string выходит из области видимости, память, выделенная под данные строки, удаляется. В отличие от Rust, этот тип является неявно копируемым (что логично, учитывая, что этот тип появился сильно раньше move semantics): когда std::string передаётся аргументом в функцию, в куче выделяется память под копию строки и в неё копируются данные этой строки. Это может заметно снизить производительность. Для того, чтобы избежать ненужных аллокаций, стоит передавать строку по ссылке.
Так что же, есть производительный способ работать со строками без лишних трат? Не совсем. У std::string есть метод substr, который возвращает подстроку с указанных позиций, но проблема в том, что этот метод возвращает std::string, то есть выделяет память в куче. Как-то не зирокост. Можно, конечно, передавать ссылку на строку и индексы, выделяющие подстроку, но это неудобно. Для решения этой проблемы в разных библиотеках — в том числе, в boost — делали свои собственные типы. Они являлись невладеющими строками и хранили в себе лишь ссылку на данные исходной строки и позицию подстроки. Они дёшево копируются, и выделить подстроку при помощи такого типа также очень дёшево: достаточно поменять пару индексов. В стандарте C++17 подобный тип наконец-то добавили в стандартную библиотеку — это string_view, определённый в заголовочном файле
Так что, теперь всё хорошокак будто в C++ в принципе что-то может быть хорошо? К сожалению, нет. Так как string_view является семантически невладеющим указателем, он, как и любой невладеющий указатель, может стать висячим (dangling). Получить висячий string_view в C++ довольно просто, особенно если учесть, что string_view неявно конвертируется из string. Более того, присваивание ссылке может продлить время жизни временного объекта, но с string_view это не работает, потому что они не являются ссылками, поэтому можно в одно строке создать временную строку, присвоить её переменной типа string_view... И получить невалидный string_view, поскольку временная строка будет уничтожена в конце строки. Что ж, звучит как проблема, которую может отловить анализатор времён жизни!
Для начала напишем пару функций, оперирующих string_view:
Для C++ существует такая вещь, как CppCoreGuidelines. Это набор рекомендаций и лучших практик, нацеленных на то, чтобы люди более эффективно (читай, менее ногострельно) использовали C++. Частью этих гайдлайнов является разработанный Гербом Саттером документ, описывающий формализацию времён жизни в C++ и позволяющий отлавливать использование висячих ссылок. Разумеется, от такого документа мало толку, если всё это проверять вручную, но есть lifetime profiler — форк clang, в котором реализован статический анализатор, работающий согласно документу выше. Его можно сбилдить самостоятельно и использовать локально
x86-64 clang (experimental -Wlifetime)
. Как же этот анализатор работает на практике?Начну немного издалека. В стандартной библиотеке C++ есть тип string (аналог в Rust — String). Это изменяемая строка из символов в неопределённой кодировке, хранящая данные в куче (как правило, ибо стандарт не запрещает использовать small string optimisation, когда маленькие строки хранятся на стеке, а не в куче). Самое главное — это владеющий тип: когда значение типа std::string выходит из области видимости, память, выделенная под данные строки, удаляется. В отличие от Rust, этот тип является неявно копируемым (что логично, учитывая, что этот тип появился сильно раньше move semantics): когда std::string передаётся аргументом в функцию, в куче выделяется память под копию строки и в неё копируются данные этой строки. Это может заметно снизить производительность. Для того, чтобы избежать ненужных аллокаций, стоит передавать строку по ссылке.
Так что же, есть производительный способ работать со строками без лишних трат? Не совсем. У std::string есть метод substr, который возвращает подстроку с указанных позиций, но проблема в том, что этот метод возвращает std::string, то есть выделяет память в куче. Как-то не зирокост. Можно, конечно, передавать ссылку на строку и индексы, выделяющие подстроку, но это неудобно. Для решения этой проблемы в разных библиотеках — в том числе, в boost — делали свои собственные типы. Они являлись невладеющими строками и хранили в себе лишь ссылку на данные исходной строки и позицию подстроки. Они дёшево копируются, и выделить подстроку при помощи такого типа также очень дёшево: достаточно поменять пару индексов. В стандарте C++17 подобный тип наконец-то добавили в стандартную библиотеку — это string_view, определённый в заголовочном файле
<string_view>
. Аналогом этого типа в Rust выступает &str
.Так что, теперь всё хорошо
Для начала напишем пару функций, оперирующих string_view:
#include <string>
#include <string_view>
using sv = std::string_view;
sv shortest(sv a, sv b) {
return a.size() < b.size() ? a : b;
}
sv cut_prefix(sv prefix, sv str) {
//
не спрашивайте if (str.rfind(prefix, 0) == 0) {
return str.substr(prefix.size());
} else {
return str;
}
}
Эти функции имеют одинаковые сигнатуры, но между ними есть большое отличие: shortest может вернуть любой из двух аргументов, а вот cut_prefix может вернуть только второй аргумент или подстроку из него.GitHub
CppCoreGuidelines/CppCoreGuidelines.md at master · isocpp/CppCoreGuidelines
The C++ Core Guidelines are a set of tried-and-true guidelines, rules, and best practices about coding in C++ - isocpp/CppCoreGuidelines
❤1
Что ж, попробуем использовать
...Если посмотреть README к форку, можно увидеть ещё один флаг,
Ну, ладно, это я испорчен растом, на самом деле тот факт, что компилятор C++ ловит подобные ошибки безо всяких аннотаций со стороны пользователя, пусть и с ложноположительными результатами — это довольно круто. Но что, если мы захотим передавать функцию отдельным параметром? Давайте проверим:
Ну, хорошо, хорошо, это шаблоны, их действительно сложно анализировать, из-за них парсинг C++ является полной по Тьюрингу задачей. Давайте сделаем что-нибудь попроще, скажем, то, что было ещё в C: указатели на функции:
Заключения не будет.
-Wlifetime
на практике:sv example_should_err(int n, sv str) {На первый пример компилятор выдаёт предупреждение:
auto n_str = std::to_string(n);
return shortest(n_str, str);
}
sv example_should_ok(int n, sv str) {
auto n_str = std::to_string(n);
return cut_prefix(n_str, str);
}
<source>:20:5: warning: returning a dangling pointer [-Wlifetime]И совершенно правильно, потому что тут может быть возвращён string_view, созданный из временной строки, которая уничтожается на выходе из функции. Что же касается второго примера... Тоже предупреждение. Причём то же самое :/ Хотя тут string_view от n_str заведомо не покидает пределы функции.
return shortest(n_str, str);
^~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:20:5: note: pointee 'n_str' left the scope here
return shortest(n_str, str);
^~~~~~~~~~~~~~~~~~~~~~~~~~~
...Если посмотреть README к форку, можно увидеть ещё один флаг,
-Wlifetime-filter
. Цитирую: "Reduce the number of false-postives at the cost of additional false negatives.". Добавляем этот флаг и... Получаем тот же самый выхлоп.Ну, ладно, это я испорчен растом, на самом деле тот факт, что компилятор C++ ловит подобные ошибки безо всяких аннотаций со стороны пользователя, пусть и с ложноположительными результатами — это довольно круто. Но что, если мы захотим передавать функцию отдельным параметром? Давайте проверим:
template <typename F> sv example_hof(int n, sv str, F func) {Кладём в компилятор и... Получаем ноль предупреждений.
auto n_str = std::to_string(n);
return func(n_str, str);
}
sv example_hof_should_ok(int n, sv str) {
return example_hof(n, str, cut_prefix);
}
sv example_hof_should_err(int n, sv str) {
return example_hof(n, str, shortest);
}
Ну, хорошо, хорошо, это шаблоны, их действительно сложно анализировать, из-за них парсинг C++ является полной по Тьюрингу задачей. Давайте сделаем что-нибудь попроще, скажем, то, что было ещё в C: указатели на функции:
sv example_hof(int n, sv str, sv (*func)(sv, sv)) {Проверяем... Ноль предупреждений.
auto n_str = std::to_string(n);
return func(n_str, str);
}
// example_hof_should_ok и example_hof_should_err остались без изменений
Заключения не будет.
godbolt.org
Compiler Explorer - C++ (x86-64 clang (experimental -Wlifetime))
using sv = std::string_view;
sv shortest(sv a, sv b) {
return a.size() < b.size() ? a : b;
}
sv cut_prefix(sv prefix, sv str) {
if (str.rfind(prefix, 0) == 0) {
return str.substr(prefix.size());
} else {
return str;
}
}
…
sv shortest(sv a, sv b) {
return a.size() < b.size() ? a : b;
}
sv cut_prefix(sv prefix, sv str) {
if (str.rfind(prefix, 0) == 0) {
return str.substr(prefix.size());
} else {
return str;
}
}
…
Forwarded from мне не нравится реальность (вафель 🧇)
Улучшал код
Силой святой копипасты у меня получилось деление на 0 😅
typed_phy
и случайно наткнулся на такое прекрасное...Силой святой копипасты у меня получилось деление на 0 😅
#prog #article
Как сделать из notepad.exe экран для рендера трёхмерных изображений с FPS 30.
kylehalladay.com/blog/2020/05/20/Rendering-With-Notepad.html
Как сделать из notepad.exe экран для рендера трёхмерных изображений с FPS 30.
kylehalladay.com/blog/2020/05/20/Rendering-With-Notepad.html
Kylehalladay
Kyle Halladay - Ray Tracing In Notepad.exe At 30 FPS
I build shaders, renderers, games, and other stuff that's fun to stare at.
#prog #cpp
В C++ в наследство от C достались операторы инкремента и декремента (
Если это не костыль, то я даже не знаю, как это назвать.
stackoverflow.com/questions/15244094/c-overloading-for-both-pre-and-post-increment
В C++ в наследство от C достались операторы инкремента и декремента (
++
и --
). Как и любые операторы, их можно перегружать для своих типов. Но есть один нюанс: как различать префиксный и постфиксный инкременты? В C++ решение таково: префиксный оператор не принимает аргументов, а постфиксный принимает неиспользуемый параметр типа int.Если это не костыль, то я даже не знаю, как это назвать.
stackoverflow.com/questions/15244094/c-overloading-for-both-pre-and-post-increment
Stack Overflow
C++: overloading ++ for both pre and post increment
Can we overload operator++ for pre-increment and post-increment? i.e. calling SampleObject++ and ++SampleObject results correctly.
class CSample {
public:
int m_iValue; // just to directly
class CSample {
public:
int m_iValue; // just to directly
Хочу основать конференцию "Переносица". Чтобы никогда её не организовывать и всё время писать объявления ""Переносица" переносится".
Forwarded from мне не нравится реальность (вафель 🧇)
Имплементируешь нестабильный трейт из стд для своего типа
@
Его изменили 11 дней назад
@
У тебя старый найтли компилятор и ты имплементировал старую версию
@
В CI НОВЫЙ КОМПИЛЯТОР И CI ВАЛИТСЯ
@
Его изменили 11 дней назад
@
У тебя старый найтли компилятор и ты имплементировал старую версию
@
В CI НОВЫЙ КОМПИЛЯТОР И CI ВАЛИТСЯ
Если бы Рик Эстли писал бы Never gonna give you up в наши дни, то в ней, вероятно, была бы строчка "Never gonna send dickpic"
#prog #rust #article
Офигенная статья о заблуждениях касательно лайфтаймов. Очень хорошее подспорье новичкам. Как пишет автор в своём первом посте:
While learning Rust I was struggling with certain concepts so I tried to organize my thoughts by writing them down in markdown files and well... I soon had several markdown files. I thought "Maybe I should start a blog?" but then I realized "I probably shouldn't, my posts will never be as good as anything written on Rust by Niko Matsakis, Saoirse Shipwreckt, Huon Wilson, David Tolnay, Alexis Beingessner, Daniel Keep, Carl Fredrik Samson, Aleksey Kladov, Amos Wegner, Pascal Hertleif, Dimitri Sabadie, Daniel Henry-Mantilla, Steve Klabnik, Jake Goulding, or Carol Nichols". That list isn't even exhaustive, there's easily dozens of people I left out! There's lots of super smart folk who are already writing about Rust. However, I have one unique advantage that none of those people have: I'm dumb. Super smart people tend to write super smart articles that only other super smart people understand. There's a gap in the market for us dummies, and it's that gap I intend to fill with my writing.
Советую приглядывать за этим блогом, это явно что-то многообещающее.
github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md
Офигенная статья о заблуждениях касательно лайфтаймов. Очень хорошее подспорье новичкам. Как пишет автор в своём первом посте:
While learning Rust I was struggling with certain concepts so I tried to organize my thoughts by writing them down in markdown files and well... I soon had several markdown files. I thought "Maybe I should start a blog?" but then I realized "I probably shouldn't, my posts will never be as good as anything written on Rust by Niko Matsakis, Saoirse Shipwreckt, Huon Wilson, David Tolnay, Alexis Beingessner, Daniel Keep, Carl Fredrik Samson, Aleksey Kladov, Amos Wegner, Pascal Hertleif, Dimitri Sabadie, Daniel Henry-Mantilla, Steve Klabnik, Jake Goulding, or Carol Nichols". That list isn't even exhaustive, there's easily dozens of people I left out! There's lots of super smart folk who are already writing about Rust. However, I have one unique advantage that none of those people have: I'm dumb. Super smart people tend to write super smart articles that only other super smart people understand. There's a gap in the market for us dummies, and it's that gap I intend to fill with my writing.
Советую приглядывать за этим блогом, это явно что-то многообещающее.
github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md
GitHub
rust-blog/posts/common-rust-lifetime-misconceptions.md at master · pretzelhammer/rust-blog
Educational blog posts for Rust beginners. Contribute to pretzelhammer/rust-blog development by creating an account on GitHub.
❤1
#prog
Есть искусство. Есть современное искусство. А есть ну совсем современное искусство: сайт, который исчезнет, если на нём в течение суток не будут размещать ни одного сообщения.
Есть искусство. Есть современное искусство. А есть ну совсем современное искусство: сайт, который исчезнет, если на нём в течение суток не будут размещать ни одного сообщения.
👌1
Блог*
#prog Есть искусство. Есть современное искусство. А есть ну совсем современное искусство: сайт, который исчезнет, если на нём в течение суток не будут размещать ни одного сообщения.
И, кстати, он существует уже больше месяца
Хабр
Создан сайт, который самоуничтожится, если пользователи не будут публиковать там сообщения в течение суток
Программист и веб-дизайнер из Канады (aka FemmeAndroid ) создала сайт с названием This Website Will Self-Destruct, который должен самоуничтожиться, если пользователи в течение 24 часов (86 400 секунд)...
👍2
#prog #cpp #article
Разбор устройства и процесса запуска простейшей программы на C++. В шоу выступают секции бинарника, скрипты компоновщика, запускатели программ и легаси, а также ещё много всего.
На самом деле, весьма познавательное чтиво.
oneraynyday.github.io/dev/2020/05/03/Analyzing-The-Simplest-C++-Program/
Разбор устройства и процесса запуска простейшей программы на C++. В шоу выступают секции бинарника, скрипты компоновщика, запускатели программ и легаси, а также ещё много всего.
На самом деле, весьма познавательное чтиво.
oneraynyday.github.io/dev/2020/05/03/Analyzing-The-Simplest-C++-Program/