Как можно оформить в C++ длинный строковый литерал и разбить его на нескольксо строк в коде?
Допустим, мы хотим разбить длинную строку в коде на несколько линий, но так чтобы переносы строк и отступы из самого кода не повлияли на результат.
Один из проcтейших способов - просто написать строки в кавычках друг за другом:
Такой вариант склеит строки без переносов
Однако, есть и другой способ: можно использовать кавычки только в начале и конце всей строки, вставив символ
как самый последний (!) на каждой строке:
При этом начинать новую строку следует с самого начала, иначе любой отступ в коде
попадет и в саму строку. Неудобно, правда?
А как же raw string literals? Те самые, что заключаются в
Да, они замечательно подходят, чтоб не экранировать разные символы, вроде кавычек и обратных слешей:
Но что будет, если взять нашу цитату, перенесенную по строкам в коде:
Оно вставит переносы строк
Мой победитель: просто строки в двойных кавычках на каждой строке. Просто, привычно, не вставляет ненужных символов и позволяет форматировать код.
Допустим, мы хотим разбить длинную строку в коде на несколько линий, но так чтобы переносы строк и отступы из самого кода не повлияли на результат.
Один из проcтейших способов - просто написать строки в кавычках друг за другом:
std::string str1 =
"Если ты, следуя правому разуму, будешь старательно,"
" ревностно и любовно относиться к делу, которым ты в"
" данный момент занят...";
std::cout << str1 << std::endl; // напечатает всё в одну строку
Такой вариант склеит строки без переносов
\n, но тут важно самостоятельно не пропустить пробелы в местах разрывов.Однако, есть и другой способ: можно использовать кавычки только в начале и конце всей строки, вставив символ
\как самый последний (!) на каждой строке:
std::string str1 =
"Если ты, следуя правому разуму, будешь старательно, \
ревностно и любовно относиться к делу, которым ты в \
данный момент занят...";
std::cout << str1 << std::endl; // напечатает всё в одну строку
При этом начинать новую строку следует с самого начала, иначе любой отступ в коде
попадет и в саму строку. Неудобно, правда?
А как же raw string literals? Те самые, что заключаются в
R"( и )" (или другие разделители)...Да, они замечательно подходят, чтоб не экранировать разные символы, вроде кавычек и обратных слешей:
std::string json = R"(
{
"path": "C:\Program Files\",
}
)";
Но что будет, если взять нашу цитату, перенесенную по строкам в коде:
std::string str1 =
R"(Если ты, следуя правому разуму, будешь старательно,
ревностно и любовно относиться к делу, которым ты в
данный момент занят...)";
Оно вставит переносы строк
\n и отступы из кода в результат.Мой победитель: просто строки в двойных кавычках на каждой строке. Просто, привычно, не вставляет ненужных символов и позволяет форматировать код.
👍6❤2🔥2
Многим известно как работает dynamic_cast в C++. Зная, что за указателем на базовый класс стоит объект производного, мы можем скастовать базовый до производного и вызвать его метод. Пример:
Все, что нужно для корректной работы dynamic_cast - это наличие хотя бы одного виртуального метода. И тогда мы можем в рантайме проверить результат кастования на nullptr. Если это nullptr - то мы не угадали и указатель смотрел на объект базового класса.
Обычно такой подход рассматривается как результат неудачного проектирования, но иногда все же приходится так делать. К тому же проверка указателя на nullptr довольно обычная операция.
Но что делать, если мы имеем не указатель, а ссылку на базовый класс? Оказывается, что dynamic_cast работает как ожидается и со ссылками - можно преобразовать ссылку на базовый класс в ссылку на производный. Однако, что делать, если преобразование в рантайме невозможно? Ссылка, в отличие от указателя не может указывать на nullptr. В случае ошибки преобразования dynamic_cast выбросит исключение std::bad_cast. Пример:
Однако, этот пример показывает плохой стиль программирования, потому что использует исключения для потока выполнения.
struct Base {
virtual ~Base() = default;
};
struct Derived : public Base {
void doDerivedJob() {
std::cout << "derived" << std::endl;
}
};
void cast(Base* baseP) {
Derived* d = dynamic_cast<Derived*>(baseP);
if (d) {
d->doDerivedJob();
} else {
std::cout << "ptr: unable to cast" << std::endl;
}
}
int main(int argc, char* argv<::>)
{
Base b;
Derived d;
cast(&b); // ptr: unable to cast
cast(&d); // derived
return 0;
}Все, что нужно для корректной работы dynamic_cast - это наличие хотя бы одного виртуального метода. И тогда мы можем в рантайме проверить результат кастования на nullptr. Если это nullptr - то мы не угадали и указатель смотрел на объект базового класса.
Обычно такой подход рассматривается как результат неудачного проектирования, но иногда все же приходится так делать. К тому же проверка указателя на nullptr довольно обычная операция.
Но что делать, если мы имеем не указатель, а ссылку на базовый класс? Оказывается, что dynamic_cast работает как ожидается и со ссылками - можно преобразовать ссылку на базовый класс в ссылку на производный. Однако, что делать, если преобразование в рантайме невозможно? Ссылка, в отличие от указателя не может указывать на nullptr. В случае ошибки преобразования dynamic_cast выбросит исключение std::bad_cast. Пример:
void cast(Base & baseRef) {
try {
Derived& d = dynamic_cast<Derived&>(baseRef);
d.doDerivedJob();
} catch (const std::bad_cast& ex) {
std::cout << "ref: unable to cast: " << ex.what() << std::endl;
// baseRef.doWork();
}
}
int main(int argc, char* argv<::>)
{
Base b;
Derived d;
cast(b); // ref: unable to cast: Bad dynamic_cast
cast(d); // derived
return 0;
}Однако, этот пример показывает плохой стиль программирования, потому что использует исключения для потока выполнения.
❤4
Как подсчитать число выставленных битов (единиц) в целом беззнаковом?
Давайте возьмем C++ 20, где в <bit> появилась функция std::popcount, которая делает, то что нам нужно:
Скучно? На первый взгляд да, но в C++ всегда можно закопаться в детали =)
Во-первых, что за странное название? Почему не какой-нибудь
Оказывается, что popcount это сокращение от “population count”.
Вроде немного понятнее, но лучше не стало)
Чтож, почитаем хабр: https://habr.com/ru/articles/467083/
Кроме пространного списка задач, где для решения используется эта функция, из статьи мы узнаём
что во многих процессорах есть специальная инструкция (н-р
данное вычисление очень быстро (за такт?).
Кроме того, в GCC и CLANG есть встроенные функции, которые также вычисляют popcount очень быстро и что-то мне подсказывает, что они могут вызывать эту единственную инструкцию (а может и нет).
В GCC эта функция наз-ся
Как видим, в дебаге GCC вставит встроенную
Если же добавить -O3, то разницы не будет никакой.
Замеры делать не буду)
Давайте возьмем C++ 20, где в <bit> появилась функция std::popcount, которая делает, то что нам нужно:
#include <iostream>
#include <bit>
#include <cstdint>
int main() {
uint8_t u1 = 0b00011101;
unsigned u2 = 123; // 0b1111011
std::cout << std::popcount(u1) << " " <<
std::popcount(u2) << std::endl; // 4 6
return 0;
}
Скучно? На первый взгляд да, но в C++ всегда можно закопаться в детали =)
Во-первых, что за странное название? Почему не какой-нибудь
bit_set_count?Оказывается, что popcount это сокращение от “population count”.
Вроде немного понятнее, но лучше не стало)
Чтож, почитаем хабр: https://habr.com/ru/articles/467083/
Кроме пространного списка задач, где для решения используется эта функция, из статьи мы узнаём
что во многих процессорах есть специальная инструкция (н-р
POPCNT на X86), которая выполняетданное вычисление очень быстро (за такт?).
Кроме того, в GCC и CLANG есть встроенные функции, которые также вычисляют popcount очень быстро и что-то мне подсказывает, что они могут вызывать эту единственную инструкцию (а может и нет).
В GCC эта функция наз-ся
__builtin_popcount. Пример можно посмотреть тут: https://godbolt.org/z/84ETrhfnKКак видим, в дебаге GCC вставит встроенную
__popcountdi2 функцию, а для std::popcount будет другая.Если же добавить -O3, то разницы не будет никакой.
Замеры делать не буду)
Хабр
Как используется странная инструкция popcount в современных процессорах
Это псевдорасшифровка моей презентации на !!Con 2019 . В большинстве используемых сегодня процессорных архитектур есть инструкция под названием popcount , сокращённо от 'population count'. Она делает...
👍5❤1
Давайте посмотрим на такой пример заголовочного файла (.h) c использованием указателя на incomplete тип (forward declaration):
Не будем вдаваться в корректность, посмотрим для начала на компилируемость.
Такой код, конечно же соберется, ведь компилятор знает размер указателя в байтах (размер одинаков как для определенного, так и неопределенного типа).
Если мы раскомментируем строку с shared_ptr, то код тоже скомпилируется (по крайней мере у меня собрался). Не будем говорить, про корректность его работы, но напомним, что вцелом удаление неопределенного типа - UB.
А вот если мы раскомментируем строку с unique_ptr, то код перестанет собираться.
Многие используют паттерн pimpl и потому знают, что реализацию деструктора нужно сделать в других исходниках (например .cpp), где forward declaration тип определен и проблема этим решается.
Но почему компилятор не ругается на shared_ptr?
Дело в том, что unique_ptr и shared_ptr требуют полного определения типа в разных местах. Детали сложны (и о них я не знаю), но первому нужен статический deleter, тогда как второму - динамический.
Вывод: в нашей копилке костылей еще одно неправильное решение - заменить unique_ptr на shared_ptr 😊.
#include <memory>
class ForwardDClass;
class WithSmartPtr
{
public:
WithSmartPtr() = default;
~WithSmartPtr() = default;
private:
ForwardDClass* rawPtr_;
//std::shared_ptr<ForwardDClass> sPtr_;
//std::unique_ptr<ForwardDClass> uPtr_;
};
int main(int argc, char* argv<::>)
{
WithSmartPtr w;
return 0;
}
Не будем вдаваться в корректность, посмотрим для начала на компилируемость.
Такой код, конечно же соберется, ведь компилятор знает размер указателя в байтах (размер одинаков как для определенного, так и неопределенного типа).
Если мы раскомментируем строку с shared_ptr, то код тоже скомпилируется (по крайней мере у меня собрался). Не будем говорить, про корректность его работы, но напомним, что вцелом удаление неопределенного типа - UB.
А вот если мы раскомментируем строку с unique_ptr, то код перестанет собираться.
Многие используют паттерн pimpl и потому знают, что реализацию деструктора нужно сделать в других исходниках (например .cpp), где forward declaration тип определен и проблема этим решается.
Но почему компилятор не ругается на shared_ptr?
Дело в том, что unique_ptr и shared_ptr требуют полного определения типа в разных местах. Детали сложны (и о них я не знаю), но первому нужен статический deleter, тогда как второму - динамический.
Вывод: в нашей копилке костылей еще одно неправильное решение - заменить unique_ptr на shared_ptr 😊.
👍3❤1
По умолчанию, при форматировании вывода в поток булевых значений, будет использоваться
1 и 0 для true и false. Пример:
Возможно, что удобнее (особенно для отладки) выводить булевы значения строкой,
также как они задаются в коде. Для этого нужно добавить в поток манипулятор boolalpha:
Интересно то, что манипулятор действует на все последующие записи булевых значений
для этого потока. То есть он будет применяться и после ; и после std::endl.
Отменить форматирование булевых флагов можно записав в поток манипулятор
std::noboolalpha:
(Интересно, а для ввода это работает? кто проверит?)
Честно говоря, мне бы хотелось увидеть нормальные true/false по-умолчанию,
но видимо обратная совместимость.
Еще одной интересной особенностью форматирования вывода является округление
значений с плавающей точкой.
Посмотрим на вывод такого примера (на вашем окружении может быть другой результат):
Поток выбрал точность в 6 знаков после точки (мы эту точность не задавали, поэтому может быть другой),
и проведет не отсечение, а округление в последней цифре.
1 и 0 для true и false. Пример:
bool t = true;
bool f = false;
std::cout << t << " " << f << std::endl;
// Выведет: 1 0
Возможно, что удобнее (особенно для отладки) выводить булевы значения строкой,
также как они задаются в коде. Для этого нужно добавить в поток манипулятор boolalpha:
std::cout << std::boolalpha;
std::cout << t << " " << f << std::endl;
// Выведет: true false
Интересно то, что манипулятор действует на все последующие записи булевых значений
для этого потока. То есть он будет применяться и после ; и после std::endl.
Отменить форматирование булевых флагов можно записав в поток манипулятор
std::noboolalpha:
std::cout << std::noboolalpha << t << " " << f << std::endl; // 1 0
(Интересно, а для ввода это работает? кто проверит?)
Честно говоря, мне бы хотелось увидеть нормальные true/false по-умолчанию,
но видимо обратная совместимость.
Еще одной интересной особенностью форматирования вывода является округление
значений с плавающей точкой.
Посмотрим на вывод такого примера (на вашем окружении может быть другой результат):
double d1 = 0.1234567890;
double d2 = 0.1234564890;
std::cout << d1 << std::endl;
std::cout << d2 << std::endl;
// Вывод:
// 0.123457
// 0.123456
Поток выбрал точность в 6 знаков после точки (мы эту точность не задавали, поэтому может быть другой),
и проведет не отсечение, а округление в последней цифре.
👍3
#Шпаргалка-повторялка. Rvalue или Lvalue?
Упрощенно Lvalue (left value) это то, что стоит слева от оператора присваивания =, а rvalue (right value) справа:
Если можно получить адрес некоего выражения, то зачастую это lvalue. Если нельзя - то это обычно rvalue.
Rvalue концептуально часто временный объект без имени (например результат работы функции, временное значение, литерал).
А вот lvalue обычно имеет имя.
А еще rvalue могут быть перемещены, но об этом в другой раз.
Упрощенно Lvalue (left value) это то, что стоит слева от оператора присваивания =, а rvalue (right value) справа:
lvalue = rvalue. Однако, все немного сложнее.Если можно получить адрес некоего выражения, то зачастую это lvalue. Если нельзя - то это обычно rvalue.
Rvalue концептуально часто временный объект без имени (например результат работы функции, временное значение, литерал).
А вот lvalue обычно имеет имя.
А еще rvalue могут быть перемещены, но об этом в другой раз.
👍3🔥2
Использует ли std::optional указатели для реализации?
Нет. std::optional не выделяет память в куче, он может полностью работать на стеке.
Объект создается на месте в стековом куске памяти (размером с объект) внутри опшинала.
Поэтому нет и накладных расходов на переход по указателю (но есть накладные расходы на bool флаг для состояния инициализации + выравнивание).
Однако возникает вопрос, расходует ли std::optional память под неинициализированный объект? Ведь хранилище выделятся всегда на стеке. Думаю, что вы можете ответить на него самостоятельно (а еще посмотреть на sizeof для жирного объекта).
Выходит, что std::optional и не такой уж optional, память то всегда жрет)
Кстати, по причине такой реализации optional нельзя создавать с некоторыми типами, например не пойдут cсылки, функции, void, массивы.
Нет. std::optional не выделяет память в куче, он может полностью работать на стеке.
Объект создается на месте в стековом куске памяти (размером с объект) внутри опшинала.
Поэтому нет и накладных расходов на переход по указателю (но есть накладные расходы на bool флаг для состояния инициализации + выравнивание).
Однако возникает вопрос, расходует ли std::optional память под неинициализированный объект? Ведь хранилище выделятся всегда на стеке. Думаю, что вы можете ответить на него самостоятельно (а еще посмотреть на sizeof для жирного объекта).
Выходит, что std::optional и не такой уж optional, память то всегда жрет)
Кстати, по причине такой реализации optional нельзя создавать с некоторыми типами, например не пойдут cсылки, функции, void, массивы.
👍2❤1🔥1
Интересное.
В ядре линукса руками заинлайнили две функции, тем самым повысив на 12% скорость обработки входящих UDP пакетов.
https://www.opennet.ru/opennews/art.shtml?num=64783
У меня есть шутка про UDP, но она до тебя не дойдет (на 12% быстрее).
В ядре линукса руками заинлайнили две функции, тем самым повысив на 12% скорость обработки входящих UDP пакетов.
https://www.opennet.ru/opennews/art.shtml?num=64783
www.opennet.ru
В ядре Linux на 12% ускорена обработка входящих UDP-пакетов
В кодовую базу, на основе которой формируется ядро Linux 7.0, принят набор изменений, при проведении стресс-тестирования в 100-гигабитной сети позволивших повысить производительность обработки входящих UDP-пакетов на 12%. Оптимизация реализована путём ручного…
👍9😁1🤯1
Интересна ли тема Qt?
Сегодня речь пойдет про QPointer. Как известно, в Qt всё строится вокруг QObject со своей механикой владения объектами, отличной от использования "стандартных" умных указателей, типа std::shared_ptr.
Давайте посмотрим на такой пример: в наш класс передается некий наследник QObject, которым мы не владеем. Далее в какой-то момент времени нам необходимо удостовериться, а существует ли этот объект еще или нет.
В стандартном C++ мы бы воспользовались weak_ptr. Но в Qt есть специальный класс с довольно абстрактным названием QPointer, который ведет себя как обычный сырой указатель, однако автоматически очищается когда хранимый им указатель удаляется. Поэтому мы можем проверить, удалился ли объект по указателю.
Внесем простые изменения в класс:
Теперь проверка if (obj_) будет работать как ожидается, при этом нам не обязательно оборачивать указатель в QPointer при передаче в конструктор класса.
В чем отличия от weak/shared ptr? Во-первых не нужно изначально оборачивать указатель в shared, что может быть не всегда и возможно. Во-вторых работает только с наследниками QObject. Ну и механизм работы отличается, что кстати может легко сломаться в многопоточном коде.
О мёртвых указателях либо хорошо, либо ничего.
Сегодня речь пойдет про QPointer. Как известно, в Qt всё строится вокруг QObject со своей механикой владения объектами, отличной от использования "стандартных" умных указателей, типа std::shared_ptr.
Давайте посмотрим на такой пример: в наш класс передается некий наследник QObject, которым мы не владеем. Далее в какой-то момент времени нам необходимо удостовериться, а существует ли этот объект еще или нет.
struct Class: {
Class(QObject * obj) : obj_(obj) {}
void do() {
if (obj_) { // жив ли объект по указателю?
//obj_->method(); // UB, если нет
};
}
QObject * obj_;
};В стандартном C++ мы бы воспользовались weak_ptr. Но в Qt есть специальный класс с довольно абстрактным названием QPointer, который ведет себя как обычный сырой указатель, однако автоматически очищается когда хранимый им указатель удаляется. Поэтому мы можем проверить, удалился ли объект по указателю.
Внесем простые изменения в класс:
QPointer<QObject> * obj_;Теперь проверка if (obj_) будет работать как ожидается, при этом нам не обязательно оборачивать указатель в QPointer при передаче в конструктор класса.
В чем отличия от weak/shared ptr? Во-первых не нужно изначально оборачивать указатель в shared, что может быть не всегда и возможно. Во-вторых работает только с наследниками QObject. Ну и механизм работы отличается, что кстати может легко сломаться в многопоточном коде.
👍5😁2🔥1
Начиная с С++23 официально доступны директивы
можно будет писать короче и консистентнее:
Краткость — сестра таланта (и брат нового стандарта)
#elifdef и #elifndef, так что теперь например вместо:
#ifdef WINDOWS
//
#elif defined(MACOS)
//
#elif defined(LINUX)
//
#endif
можно будет писать короче и консистентнее:
#ifdef WINDOWS
//
#elifdef MACOS
//
#elifdef LINUX
//
#endif
👍5❤1
Хороший курс лекций по плюсам (на рутубе) - язык русский, а вот слайды почему-то на английском (на канале на рутубе есть и версия с русскими слайдами).
https://t.me/cpp_lects_rus/333
Cмотря какой rutube, смотря сколько slides
https://t.me/cpp_lects_rus/333
Telegram
C++ and other lectures
Немного подзамочного контента для моих уважаемых подписчиков.
Ни для кого не секрет, что мой магистерский курс этого года я записывал на английском языке. Меньше людей знает, что исходно он читался на русском языке, на английский я (нейронкой) до лекции…
Ни для кого не секрет, что мой магистерский курс этого года я записывал на английском языке. Меньше людей знает, что исходно он читался на русском языке, на английский я (нейронкой) до лекции…
👍2🔥2
Тут у ребят
https://t.me/cpp_durka/34
Лучше один раз увидеть, чем сто раз услышать nullptr
nullptr не всегда равен 0 (как могло бы ожидаться), а иногда -1 (название канала намекает).https://t.me/cpp_durka/34
Telegram
C++: Хроники Дурки🚑
Обычно когда спрашивают, что такое nullptr, получают в ответ, что это указатель равный нулю.
Более продвинутые говорят, что это зависит от имплементации, и приводят в пример какие-то embedded сценарии архитектур, до которых среднему программисту дела нет.…
Более продвинутые говорят, что это зависит от имплементации, и приводят в пример какие-то embedded сценарии архитектур, до которых среднему программисту дела нет.…
😁5💯1
Утверждён стандарт C++26.
Мне понравились новые няшные операторы😇 :
https://www.opennet.ru/opennews/art.shtml?num=65102
Мне понравились новые няшные операторы
^^ static_assert(^^i != ^^j);https://www.opennet.ru/opennews/art.shtml?num=65102
Please open Telegram to view this post
VIEW IN TELEGRAM
😁5👍1
Чистовик: Сайт отслеживает переход популярных проектов на модули C++. По прогнозу к 2167 году переедут все 😛 :
https://arewemodulesyet.org/
https://arewemodulesyet.org/
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4😁3🔥1