1.83K subscribers
3.24K photos
127 videos
15 files
3.52K links
Блог со звёздочкой.

Много репостов, немножко программирования.

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
#prog #cpp

Для C++ существует такая вещь, как CppCoreGuidelines. Это набор рекомендаций и лучших практик, нацеленных на то, чтобы люди более эффективно (читай, менее ногострельно) использовали C++. Частью этих гайдлайнов является разработанный Гербом Саттером документ, описывающий формализацию времён жизни в C++ и позволяющий отлавливать использование висячих ссылок. Разумеется, от такого документа мало толку, если всё это проверять вручную, но есть lifetime profiler — форк clang, в котором реализован статический анализатор, работающий согласно документу выше. Его можно сбилдить самостоятельно и использовать локально только зачем, а можно открыть godbolt.org и использовать компилятор 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.

Так что, теперь всё хорошо как будто в C++ в принципе что-то может быть хорошо? К сожалению, нет. Так как string_view является семантически невладеющим указателем, он, как и любой невладеющий указатель, может стать висячим (dangling). Получить висячий string_view в C++ довольно просто, особенно если учесть, что string_view неявно конвертируется из string. Более того, присваивание ссылке может продлить время жизни временного объекта, но с string_view это не работает, потому что они не являются ссылками, поэтому можно в одно строке создать временную строку, присвоить её переменной типа string_view... И получить невалидный string_view, поскольку временная строка будет уничтожена в конце строки. Что ж, звучит как проблема, которую может отловить анализатор времён жизни!

Для начала напишем пару функций, оперирующих 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 может вернуть только второй аргумент или подстроку из него.
1
Что ж, попробуем использовать -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]
return shortest(n_str, str);
^~~~~~~~~~~~~~~~~~~~~~~~~~~

<source>:20:5: note: pointee 'n_str' left the scope here
return shortest(n_str, str);
^~~~~~~~~~~~~~~~~~~~~~~~~~~

И совершенно правильно, потому что тут может быть возвращён string_view, созданный из временной строки, которая уничтожается на выходе из функции. Что же касается второго примера... Тоже предупреждение. Причём то же самое :/ Хотя тут string_view от n_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 остались без изменений

Проверяем... Ноль предупреждений.

Заключения не будет.
#prog #rust и, пожалуй, #meme
Forwarded from мне не нравится реальность (вафель 🧇)
Улучшал код typed_phy и случайно наткнулся на такое прекрасное...

Силой святой копипасты у меня получилось деление на 0 😅
#meme

(thanks @trubad)
Forwarded from rusta::mann
#prog #cpp

В C++ в наследство от C достались операторы инкремента и декремента (++ и --). Как и любые операторы, их можно перегружать для своих типов. Но есть один нюанс: как различать префиксный и постфиксный инкременты? В C++ решение таково: префиксный оператор не принимает аргументов, а постфиксный принимает неиспользуемый параметр типа int.

Если это не костыль, то я даже не знаю, как это назвать.

stackoverflow.com/questions/15244094/c-overloading-for-both-pre-and-post-increment
Хочу основать конференцию "Переносица". Чтобы никогда её не организовывать и всё время писать объявления ""Переносица" переносится".
Forwarded from мне не нравится реальность (вафель 🧇)
Имплементируешь нестабильный трейт из стд для своего типа
@
Его изменили 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
1
#prog

Есть искусство. Есть современное искусство. А есть ну совсем современное искусство: сайт, который исчезнет, если на нём в течение суток не будут размещать ни одного сообщения.
👌1
#prog #cpp #article

Разбор устройства и процесса запуска простейшей программы на C++. В шоу выступают секции бинарника, скрипты компоновщика, запускатели программ и легаси, а также ещё много всего.

На самом деле, весьма познавательное чтиво.

oneraynyday.github.io/dev/2020/05/03/Analyzing-The-Simplest-C++-Program/