Мувать не всегда дешево
#новичкам
С приходом мув семантики настали "прекрасные плюсы будущего". Нет никакого копирования, чудо-оптимизации бороздят просторы стека и кучи. Не жизнь, а сказка.
Но мир не такой уж солнечный и приветливый. Это очень опасное...
Если вы придерживаетесь RAII, пользуетесь контейнерами и умными указателями, то вы практически всегда пользуетесь правилом нуля и никогда не определяете самостоятельно специальные методы класса и, в частности, конструктор перемещения и оператор перемещающего присваивания. Компилятор сгенерирует их за вас, ленивых дядь.
Рано или поздно вы немного отрываетесь от "низов": вас уже не интересует КАК конкретно эти методы реализованы. Вы оперируете более высокоуровневыми сущностями и полагаетесь на компилятор.
И вот вы в ситуации, когда у вас есть данные, обернутые в класс, которые легально по контексту кода можно мувнуть или скопировать. Условно говоря, у вас есть функция Process, которая принимает данные по значению, чтобы поддержать оба варианта передачи: копирование и мув:
Что выбрать?
"Конечно мувнуть, это же не долгое копирование, выполнится быстро" - вот к таким не совсем корректным мыслям может привести "оторванность от низов".
Кажется, что у некоторых людей есть ощущение, что данные из одного объекта как-то перетекают в другой объект и это происходит очень быстро.
Но это не так! Перемещение - это поверхностное копирование.
Возьмем простой пример:
Что будет происходить при перемещении
Чуть сложнее:
Что будет при перемещении
Можно еще занулить конечно, но это редко происходит из соображений перфоманса.
Получается, что реально "переместить" вы можете только данные, выделенные на куче. И то они никуда не перемещаются. Вы просто копируете указатель из одного объекта в другой, при этом сами данные никак не затрагиваются.
Более того. Даже если вы используете std::string, то не всегда мув будет быстрее копирования! Thanks to SSO.
Получается, что никто никуда не течет. Все так же пресловуто копируется, кроме динамических данных под указателями.
Теперь снова актуализируем вопрос: мувать или копировать?
И ответ уже не плоскости оптимизации, а в плоскости логики кода. Перемещайте, когда вам в текущем скоупе объект больше не нужен и копируйте, если нужен. Тогда вы не пытаетесь оптимизировать код, а передаете владение объектом другому коду. Редко, когда вы на авито продаете вещи, чтобы заработать. Вы их продаете, чтобы от лишнего избавиться и дать их тем, кому они нужны, особой выгоды не ожидая. Вот здесь примерно это и должно происходить.
В реальности все немного сложнее и всегда будут исключения, но просто хочу обратить внимание, что мув семантика - это в первую очередь про передачу владения объектом и только потом уже оптимизация.
Think logically. Stay cool.
#cppcore #cpp11
#новичкам
С приходом мув семантики настали "прекрасные плюсы будущего". Нет никакого копирования, чудо-оптимизации бороздят просторы стека и кучи. Не жизнь, а сказка.
Но мир не такой уж солнечный и приветливый. Это очень опасное...
Если вы придерживаетесь RAII, пользуетесь контейнерами и умными указателями, то вы практически всегда пользуетесь правилом нуля и никогда не определяете самостоятельно специальные методы класса и, в частности, конструктор перемещения и оператор перемещающего присваивания. Компилятор сгенерирует их за вас, ленивых дядь.
Рано или поздно вы немного отрываетесь от "низов": вас уже не интересует КАК конкретно эти методы реализованы. Вы оперируете более высокоуровневыми сущностями и полагаетесь на компилятор.
И вот вы в ситуации, когда у вас есть данные, обернутые в класс, которые легально по контексту кода можно мувнуть или скопировать. Условно говоря, у вас есть функция Process, которая принимает данные по значению, чтобы поддержать оба варианта передачи: копирование и мув:
void Process(Data data);
Что выбрать?
"Конечно мувнуть, это же не долгое копирование, выполнится быстро" - вот к таким не совсем корректным мыслям может привести "оторванность от низов".
Кажется, что у некоторых людей есть ощущение, что данные из одного объекта как-то перетекают в другой объект и это происходит очень быстро.
Но это не так! Перемещение - это поверхностное копирование.
Возьмем простой пример:
struct Data {
int a;
double b;
};
Data obj1{3, 3.14};
Data obj2 = std::move(obj1);Что будет происходить при перемещении
obj1? Копирование a и b.Чуть сложнее:
struct Data {
std::array<int, 5> arr;
};
Data obj1{.arr = {1, 2, 3, 4, 5}};
Data obj2 = std::move(obj1);Что будет при перемещении
obj1, а значит и arr? Тоже копирование! std::array - это массив, фиксированного размера, расположенный на стеке. Как вы собираетесь его перемещать в другой объект? Под другой объект уже выделена своя память на стеке, вы не можете один кусок стека переместить в другой. Вы можете только скопировать значения.Можно еще занулить конечно, но это редко происходит из соображений перфоманса.
Получается, что реально "переместить" вы можете только данные, выделенные на куче. И то они никуда не перемещаются. Вы просто копируете указатель из одного объекта в другой, при этом сами данные никак не затрагиваются.
struct Data {
std::string * str;
// member functions for making it work properly
};
Data obj1{.str = new std::string("Hello, World!")};
Data obj2 = std::move(obj1);obj2 теперь имеет такое же значение указателя str, как и obj1, но сама строка оказалась нетронутой.Более того. Даже если вы используете std::string, то не всегда мув будет быстрее копирования! Thanks to SSO.
Получается, что никто никуда не течет. Все так же пресловуто копируется, кроме динамических данных под указателями.
Теперь снова актуализируем вопрос: мувать или копировать?
И ответ уже не плоскости оптимизации, а в плоскости логики кода. Перемещайте, когда вам в текущем скоупе объект больше не нужен и копируйте, если нужен. Тогда вы не пытаетесь оптимизировать код, а передаете владение объектом другому коду. Редко, когда вы на авито продаете вещи, чтобы заработать. Вы их продаете, чтобы от лишнего избавиться и дать их тем, кому они нужны, особой выгоды не ожидая. Вот здесь примерно это и должно происходить.
В реальности все немного сложнее и всегда будут исключения, но просто хочу обратить внимание, что мув семантика - это в первую очередь про передачу владения объектом и только потом уже оптимизация.
Think logically. Stay cool.
#cppcore #cpp11
3👍38❤15🔥6😎5
Передача владения
#новичкам
Захотелось совсем немного развить тему предыдущего поста.
В целом, мув семантика она не столько про оптимизацию(для этого есть например rvo/nrvo), сколько про передачу владения объектами. И то, что std::move ничего не мувает(а пытается сделать каст к rvalue reference) хорошо укладывается в эту концепцию. Данные не перемещаются, но вы говорите, что передаете владение этими данными.
Здесь мы передаем владение вектором из foo в bar. Заметьте, что bar оперирует правой ссылкой, то есть никакие перемещающие конструкторы не вызывались. Но такая сигнатура говорит о главном: bar ожидает эксклюзивного права владения над этим вектором. Вы должны явно мувнуть объект, чтобы вызвать bar. И не важно, что он дальше bar с этим вектором делает. Может ничего не сделает, а может и использует как-то данные. Но так решил автор кода: вызов bar предполагает передачу ему владения вектором.
Другой пример:
Функция double_elements принимает вектор по значению и возвращает набор из удвоенных элементов.
Функция foo 2 раза вызывает удвоение значений элементов. По логике функции foo, ей еще нужен vec в целости и сохранности(нужно доложить в него элемент). Поэтому она и передает в первый раз vec в double_elements по значению. Но после второго вызова вектор ей больше не нужен. Поэтому можно передать владение им в double_elements: возможно он им распорядится лучше.
Еще одна вещь, которая подчеркивает передачу владения: moved-from объект практически никак в общем случае нельзя безопасно использовать, кроме как безопасно разрушить или переприсвоить(в комментах под прошлым постом более конкретно обсуждали этот момент). Даже если функция принимает rvalue reference, это не значит, что она не изменяет объект: возможно внутренние вызовы это делают.
Поэтому можно принять за правило, что, передав владение, вы больше физически не имеете права пользоваться объектом. Это как продав компанию, вы бы продолжили иметь то же влияние на нее. Нетушки. Либо крестик снимите, либо трусы наденьте. Либо передали владение и забыли, либо скопировали и дальше попользовались.
Give away what you don't need. Stay cool.
#cppcore #cpp11
#новичкам
Захотелось совсем немного развить тему предыдущего поста.
В целом, мув семантика она не столько про оптимизацию(для этого есть например rvo/nrvo), сколько про передачу владения объектами. И то, что std::move ничего не мувает(а пытается сделать каст к rvalue reference) хорошо укладывается в эту концепцию. Данные не перемещаются, но вы говорите, что передаете владение этими данными.
void bar(std::vector<int>&& vec) {
// do nothing
}
void foo() {
std::vector<int> vec = {1, 2, 3};
bar(std::move(vec));
}Здесь мы передаем владение вектором из foo в bar. Заметьте, что bar оперирует правой ссылкой, то есть никакие перемещающие конструкторы не вызывались. Но такая сигнатура говорит о главном: bar ожидает эксклюзивного права владения над этим вектором. Вы должны явно мувнуть объект, чтобы вызвать bar. И не важно, что он дальше bar с этим вектором делает. Может ничего не сделает, а может и использует как-то данные. Но так решил автор кода: вызов bar предполагает передачу ему владения вектором.
Другой пример:
std::vector<int> double_elements(std::vector<int> vec) {
for (auto& elem: vec) {
elem *= 2;
}
return vec;
}
void foo() {
std::vector<int> vec = {1, 2, 3};
{
auto doubled = double_elements(vec);
std::println("{}", doubled);
}
vec.push_back(4);
{
auto doubled = double_elements(std::move(vec));
std::println("{}", doubled);
}
}
Функция double_elements принимает вектор по значению и возвращает набор из удвоенных элементов.
Функция foo 2 раза вызывает удвоение значений элементов. По логике функции foo, ей еще нужен vec в целости и сохранности(нужно доложить в него элемент). Поэтому она и передает в первый раз vec в double_elements по значению. Но после второго вызова вектор ей больше не нужен. Поэтому можно передать владение им в double_elements: возможно он им распорядится лучше.
Еще одна вещь, которая подчеркивает передачу владения: moved-from объект практически никак в общем случае нельзя безопасно использовать, кроме как безопасно разрушить или переприсвоить(в комментах под прошлым постом более конкретно обсуждали этот момент). Даже если функция принимает rvalue reference, это не значит, что она не изменяет объект: возможно внутренние вызовы это делают.
Поэтому можно принять за правило, что, передав владение, вы больше физически не имеете права пользоваться объектом. Это как продав компанию, вы бы продолжили иметь то же влияние на нее. Нетушки. Либо крестик снимите, либо трусы наденьте. Либо передали владение и забыли, либо скопировали и дальше попользовались.
Give away what you don't need. Stay cool.
#cppcore #cpp11
❤18👍12🔥6
Оборачиваем вспять байты
#новичкам
Когда мы низкоуровнево работаем с сетью, то надо понимать, что в данных, полученных по сети, нужно реверсировать порядок байтов, чтобы правильно интерпретировать значения. Также реверсировать порядок нужно при отправке данных по сети. Это происходит из-за того, что в стеке протоколов TCP/IP принят порядок Big-endian - старший байт хранится по младшему адресу. А на большинстве хостов(десктопов и серверов) - Little-endian: младший байт хранится по младшему адресу.
Соответственно нужны функции для реверсирования байтов. Обычно для этого используют либо компиляторные интринсики:
Либо системное апи:
Либо какое-нибудь библиотечное решение:
Но в С++23 появилась стандартная функция для разворачивания порядка байтов!
Работает она только для интегральных типов и вот ее возможная реализация:
Результат у нее собственно ровно тот, который и ожидается:
Как всегда стандарт запаздывает лет на 10-15-20, но хорошо, что все-таки завезли эту полезную функцию, которую можно кроссплатформенно использовать.
Use standard solutions. Stay cool.
#cpp23
#новичкам
Когда мы низкоуровнево работаем с сетью, то надо понимать, что в данных, полученных по сети, нужно реверсировать порядок байтов, чтобы правильно интерпретировать значения. Также реверсировать порядок нужно при отправке данных по сети. Это происходит из-за того, что в стеке протоколов TCP/IP принят порядок Big-endian - старший байт хранится по младшему адресу. А на большинстве хостов(десктопов и серверов) - Little-endian: младший байт хранится по младшему адресу.
Соответственно нужны функции для реверсирования байтов. Обычно для этого используют либо компиляторные интринсики:
### GCC/Clang
uint16_t swapped16 = __builtin_bswap16(value);
uint32_t swapped32 = __builtin_bswap32(value);
uint64_t swapped64 = __builtin_bswap64(value);
### MSVC:
uint16_t swapped16 = _byteswap_ushort(value);
uint32_t swapped32 = _byteswap_ulong(value);
uint64_t swapped64 = _byteswap_uint64(value);
Либо системное апи:
#include <arpa/inet.h> // Linux/macOS
// или
#include <winsock2.h> // Windows
uint16_t network_to_host16 = ntohs(value);
uint16_t host_to_network16 = htons(value);
uint32_t network_to_host32 = ntohl(value);
uint32_t host_to_network32 = htonl(value);
uint64_t network_to_host64 = ntohll(value);
uint64_t host_to_network64 = htonll(value);
Либо какое-нибудь библиотечное решение:
#include <boost/endian/conversion.hpp>
uint32_t value = 0x12345678;
uint32_t swapped = boost::endian::endian_reverse(value);
uint32_t to_big = boost::endian::native_to_big(value);
uint32_t to_little = boost::endian::native_to_little(value);
Но в С++23 появилась стандартная функция для разворачивания порядка байтов!
template< class T >
constexpr T byteswap( T n ) noexcept;
Работает она только для интегральных типов и вот ее возможная реализация:
template<std::integral T>
constexpr T byteswap(T value) noexcept
{
static_assert(std::has_unique_object_representations_v<T>,
"T may not have padding bits");
auto value_representation = std::bit_cast<std::array<std::byte, sizeof(T)>>(value);
std::ranges::reverse(value_representation);
return std::bit_cast<T>(value_representation);
}
Результат у нее собственно ровно тот, который и ожидается:
template<std::integral T>
void dump(T v, char term = '\n')
{
std::cout << std::hex << std::uppercase << std::setfill('0')
<< std::setw(sizeof(T) * 2) << v << " : ";
for (std::size_t i{}; i != sizeof(T); ++i, v >>= 8)
std::cout << std::setw(2) << static_cast<unsigned>(T(0xFF) & v) << ' ';
std::cout << std::dec << term;
}
int main()
{
static_assert(std::byteswap('a') == 'a');
std::cout << "byteswap for U16:\n";
constexpr auto x = std::uint16_t(0xCAFE);
dump(x);
dump(std::byteswap(x));
std::cout << "\nbyteswap for U32:\n";
constexpr auto y = std::uint32_t(0xDEADBEEFu);
dump(y);
dump(std::byteswap(y));
std::cout << "\nbyteswap for U64:\n";
constexpr auto z = std::uint64_t{0x0123456789ABCDEFull};
dump(z);
dump(std::byteswap(z));
}
// OUTPUT
// byteswap for U16:
// CAFE : FE CA
// FECA : CA FE
// byteswap for U32:
// DEADBEEF : EF BE AD DE
// EFBEADDE : DE AD BE EF
// byteswap for U64:
// 0123456789ABCDEF : EF CD AB 89 67 45 23 01
// EFCDAB8967452301 : 01 23 45 67 89 AB CD EF
Как всегда стандарт запаздывает лет на 10-15-20, но хорошо, что все-таки завезли эту полезную функцию, которую можно кроссплатформенно использовать.
Use standard solutions. Stay cool.
#cpp23
❤28👍12😁8🔥5
Атрибуты лямбды
#опытным
В прошлом посте код с картинки реально компилируется и, если вы не поняли, что это за чертовщина, то следующие несколько постов будут для вас.
В С++11 у нас появилась возможность указывать атрибуты для функции. Например:
Вы можете, например, пометить возвращаемое значение функции, как то, которое нельзя игнорировать, и компилятор даст вам по сопатке, если вы его все же заигнорите.
Ну это функции. А как же лямбды? Хочется и для них указывать атрибуты.
И атрибуты для возвращаемого значения лямбды завезли в С++23. Выглядит это так:
После скобок для захвата вы указываете список атрибутов в квадратных скобках. Выглядит интересно. Не очень элегантно, но интересно.
Одни скажут: "усложнение синтаксиса!". Другие скажут, что давно пора лямбды подтягивать ко всем возможностям обычных функций.
Тут как бы все просто: не хотите - не используйте. У лямбды и так полно опциональных обвесок, одним больше, одним меньше. Можно определить шаблонную лямбду и обвесить ее всякими концептами с trailing return type. И это будет страшный зверь. Можно сделать отдельный пост, как может выглядеть ультимативная лямбда.
Ну а если вы хотите немного больше синтаксически говорить кодом, то теперь можете использовать атрибуты для лямбд.
Don't ignore. Stay cool.
#cpp23
#опытным
В прошлом посте код с картинки реально компилируется и, если вы не поняли, что это за чертовщина, то следующие несколько постов будут для вас.
В С++11 у нас появилась возможность указывать атрибуты для функции. Например:
[[nodiscard]] int ComplicatedCompute() {
return 2*2;
}
ComplicatedCompute();
// warning: ignoring return value of 'int ComplicatedCompute()',
// declared with attribute nodiscardВы можете, например, пометить возвращаемое значение функции, как то, которое нельзя игнорировать, и компилятор даст вам по сопатке, если вы его все же заигнорите.
Ну это функции. А как же лямбды? Хочется и для них указывать атрибуты.
И атрибуты для возвращаемого значения лямбды завезли в С++23. Выглядит это так:
auto complicated_compute = [] [[nodiscard]] () { return 2 * 2; };
complicated_compute();
// warning: ignoring return value of 'main()::<lambda()>',
// declared with attribute 'nodiscard'После скобок для захвата вы указываете список атрибутов в квадратных скобках. Выглядит интересно. Не очень элегантно, но интересно.
Одни скажут: "усложнение синтаксиса!". Другие скажут, что давно пора лямбды подтягивать ко всем возможностям обычных функций.
Тут как бы все просто: не хотите - не используйте. У лямбды и так полно опциональных обвесок, одним больше, одним меньше. Можно определить шаблонную лямбду и обвесить ее всякими концептами с trailing return type. И это будет страшный зверь. Можно сделать отдельный пост, как может выглядеть ультимативная лямбда.
Ну а если вы хотите немного больше синтаксически говорить кодом, то теперь можете использовать атрибуты для лямбд.
Don't ignore. Stay cool.
#cpp23
❤21👍9🔥8😁3
Атрибуты везде
#опытным
Используют атрибуты функций не только лишь все, мало кто знает, куда их можно пихать.
Есть на самом деле 3 легальных места для навешивания атрибутов на функцию.
1️⃣ Перед типом возвращаемого значения:
Тогда он работает при непосредственном использовании функции.
2️⃣ После имени функции:
В таком виде атрибут тоже применяется к самой функции.
3️⃣ После параметров:
Тогда атрибут применяется к типу функции, а не к самой функции. Разница вот в чем:
Обычный вызов функции прекрасно компилируется. Но вот использование типа функции через decltype помечается как устаревшее.
Причем gcc и clang по-разному интерпретируют эту ситуацию. Clang говорит, что gnu::deprecated нельзя применять к типам и игнорирует атрибут. Вот ссылка на годболт для интересующихся.
Соответственно, в лямбде в тех же местах можно ставить атрибуты:
Признавайтесь, знали?)
Have your own opinion. Stay cool.
#cppcore
#опытным
Используют атрибуты функций не только лишь все, мало кто знает, куда их можно пихать.
Есть на самом деле 3 легальных места для навешивания атрибутов на функцию.
1️⃣ Перед типом возвращаемого значения:
[[deprecated]] int foo() { return 42; }Тогда он работает при непосредственном использовании функции.
foo();
// warning: 'int foo()' is deprecated
2️⃣ После имени функции:
int foo [[deprecated]] () { return 42; }В таком виде атрибут тоже применяется к самой функции.
3️⃣ После параметров:
int foo() [[gnu::deprecated]] { return 42; }Тогда атрибут применяется к типу функции, а не к самой функции. Разница вот в чем:
int foo() [[gnu::deprecated]] { return 42; }
int main() {
foo(); // no warnings
using FuncType = decltype(foo); // use of type is deprecated
}Обычный вызов функции прекрасно компилируется. Но вот использование типа функции через decltype помечается как устаревшее.
Причем gcc и clang по-разному интерпретируют эту ситуацию. Clang говорит, что gnu::deprecated нельзя применять к типам и игнорирует атрибут. Вот ссылка на годболт для интересующихся.
Соответственно, в лямбде в тех же местах можно ставить атрибуты:
auto complicated_compute = [] [[nodiscard]] () [[gnu::deprecated]] {
return 2 * 2;
};Признавайтесь, знали?)
Have your own opinion. Stay cool.
#cppcore
👍23🤯19❤9🔥6
Атрибуты параметров функции
#новичкам
Атрибуты можно также применять к параметрам функции. Это помогает чуть полнее в коде функции аннотировать некоторые свойства параметров.
Вы определили какой-то интерфейс с методом, принимающим один параметр. И в какой-то момент появилась необходимость создать наследника, реализующего этот интерфейс, однако реализации не нужен параметр param. Возможно Implementation - это какой-то мок, у которого в принципе пустая реализация.
Если вы активно используете варнинги компилятора и прочие линтеры, при попытке собрать такой код вы скорее всего увидите предупреждение/ошибку компиляции. Чтобы стало все чётенько, стоит пометить
Однако из стандартных атрибутов по сути имеет смысл использовать только этот самый maybe_unused.
Но атрибуты - это не только средство общения с компилятором. Это еще и средство налаживания коммуникации между автором кода и его пользователями/читателями.
Например:
Вы поместили в хэдэр такое объявление, тем самым явно сказав пользователю и компилятору, что указатели не должны быть нулевыми. Если компилятор докажет в compile-time, что в функцию передали nullptr, то он выкинет предупреждение. Ну а пользователь четко по сигнатуре видит, что функция не ожидает нулевой указатель и как порядочный гражданин не будет его передавать.
Annotate your code. Stay cool.
#cppcore
#новичкам
Атрибуты можно также применять к параметрам функции. Это помогает чуть полнее в коде функции аннотировать некоторые свойства параметров.
class Interface {
public:
virtual void method(int param) = 0;
};
class Implementation : public Interface {
public:
void method(int param) override {
// this implementation doesn't use param so mark it
}
};Вы определили какой-то интерфейс с методом, принимающим один параметр. И в какой-то момент появилась необходимость создать наследника, реализующего этот интерфейс, однако реализации не нужен параметр param. Возможно Implementation - это какой-то мок, у которого в принципе пустая реализация.
Если вы активно используете варнинги компилятора и прочие линтеры, при попытке собрать такой код вы скорее всего увидите предупреждение/ошибку компиляции. Чтобы стало все чётенько, стоит пометить
param атрибутом maybe_unused, тем самым явно указав компилятору, что параметр не используется намеренно. И проблема исчезнет.Однако из стандартных атрибутов по сути имеет смысл использовать только этот самый maybe_unused.
Но атрибуты - это не только средство общения с компилятором. Это еще и средство налаживания коммуникации между автором кода и его пользователями/читателями.
Например:
size_t safe_strcpy(
[[gnu::nonnull]] char* dest,
[[gnu::nonnull]] const char* src,
size_t dest_size
);
Вы поместили в хэдэр такое объявление, тем самым явно сказав пользователю и компилятору, что указатели не должны быть нулевыми. Если компилятор докажет в compile-time, что в функцию передали nullptr, то он выкинет предупреждение. Ну а пользователь четко по сигнатуре видит, что функция не ожидает нулевой указатель и как порядочный гражданин не будет его передавать.
Annotate your code. Stay cool.
#cppcore
❤21🔥14👍8🤯1
Множество атрибутов
#опытным
Если вы хотите указать несколько атрибутов для вашей функции, вы можете использовать следующий синтаксис:
1️⃣ Списочный. Внутри одних скобок перечисляете все атрибуты:
2️⃣ Многоскобочный. Для больших любителей распиленных квадратов. Очень больших:
Больше квадратных скобок!
Также если вы используете несколько атрибутов из какого-то одного неймспейса, то можете использовать директиву using:
Но тогда котлеты отдельно, мухи отдельно. Все атрибуты одного неймспейса нужно уносить в отдельные скобки. Это фича С++17.
Что интересно, вы можете написать полную чупуху:
И это скомпилируется! Стандарт поддерживает любые implementation-defined атрибуты. Причем неизвестные атрибуты просто игнорируются. Правда игнор спровождается варнингами, которые тем не менее можно скрыть опциями, подобным -Wno-attributes.
Таким образом, если ваш код компилируется под разные системы, то вы можете не стесняясь использовать дублирующие атрибуты, предоставляемые разными компиляторами. Так на любой платформе можно получить одинаковое поведение.
Love squares. Stay cool.
#cppcore #cpp17
#опытным
Если вы хотите указать несколько атрибутов для вашей функции, вы можете использовать следующий синтаксис:
1️⃣ Списочный. Внутри одних скобок перечисляете все атрибуты:
[[gnu::always_inline, gnu::const, gnu::hot, nodiscard]] int f();
2️⃣ Многоскобочный. Для больших любителей распиленных квадратов. Очень больших:
[[gnu::always_inline]] [[gnu::hot]] [[gnu::const]] [[nodiscard]] int f();
Больше квадратных скобок!
Также если вы используете несколько атрибутов из какого-то одного неймспейса, то можете использовать директиву using:
[[using gnu : always_inline, const, hot]] [[nodiscard]] int f();
Но тогда котлеты отдельно, мухи отдельно. Все атрибуты одного неймспейса нужно уносить в отдельные скобки. Это фича С++17.
Что интересно, вы можете написать полную чупуху:
[[rust, will, replace, cpp]] int f();
И это скомпилируется! Стандарт поддерживает любые implementation-defined атрибуты. Причем неизвестные атрибуты просто игнорируются. Правда игнор спровождается варнингами, которые тем не менее можно скрыть опциями, подобным -Wno-attributes.
Таким образом, если ваш код компилируется под разные системы, то вы можете не стесняясь использовать дублирующие атрибуты, предоставляемые разными компиляторами. Так на любой платформе можно получить одинаковое поведение.
Love squares. Stay cool.
#cppcore #cpp17
🔥21❤12👍9👎1
Яндекс приглашает на встречу РГ21 С++ 15 декабря в Москве
Собираемся сообществом экспертов и энтузиастов С++, чтобы обсудить развитие стандарта, участие российских разработчиков в нем, а еще — внезапные новинки.
В центре встречи — выступление Антона Полухина, руководителя группы разработки общих компонентов в Яндексе. Он поделится новостями с последней встречи международного Комитета по стандартизации, расскажет о прогрессе в работе над С++26 и ответит на вопросы о том, как российским разработчикам участвовать в развитии стандарта языка.
Когда: 15 декабря, 18:30
Где: Москва, офлайн + онлайн-трансляция
Регистрация на встречу
Собираемся сообществом экспертов и энтузиастов С++, чтобы обсудить развитие стандарта, участие российских разработчиков в нем, а еще — внезапные новинки.
В центре встречи — выступление Антона Полухина, руководителя группы разработки общих компонентов в Яндексе. Он поделится новостями с последней встречи международного Комитета по стандартизации, расскажет о прогрессе в работе над С++26 и ответит на вопросы о том, как российским разработчикам участвовать в развитии стандарта языка.
Когда: 15 декабря, 18:30
Где: Москва, офлайн + онлайн-трансляция
Регистрация на встречу
🔥9❤3👍3
Парсим ужас
#новичкам
Вот мы и рассмотрели все необходимые компоненты, чтобы понять, что написано здесь:
Если включить clang-format, то код преобразится во что-то такое:
Давайте посмотрим, откуда так много скобок:
1️⃣ Перед типом возвращаемого значения main определены 2 пустые области для указания атрибутов функции main.
2️⃣ Перед телом функции main определена пустая область для атрибутов, применяемых к типу функции main.
3️⃣ После блока захвата лямбды определена пустая область для атрибутов, применяемых к самой лямбде.
4️⃣ Внутри списка параметров лямбды определена пустая область для атрибутов, применяемых к единственному параметру лямбды.
5️⃣ Сама лямбда является generic и принимает массив неизвестного типа.
6️⃣ Перед телом лямбды определены 2 пустые области для атрибутов, применяемых к типу лямбды.
7️⃣ Вызываем лямбду с помощью указателя на функцию main.
8️⃣ Ну и разбавили это дело несколькими лишними скоупами по пути.
Не так уж и сложно оказалось)
Так, новичковая часть закончилась.
#опытным
Интересно, что этот код компилируется на gcc, но не на clang.
cppinsights показывает, что лямбда раскрывается во что-то такое:
То есть по факту мы имеем шаблонный оператор с auto параметром.
Как интерпретировать эту штуку - дело нетривиальное и по ходу компиляторы это делают по-разному. Видимо gcc при попытке инстанцировать шаблон с параметром int() выводит auto как тот же самый тип функции int() и в итоге лямбда принимает указатель на функцию. А clang при попытке инстанцировать шаблон выводит тип параметра функции как массив функций int() и не может принять main в качестве такого параметра.
Пишите ваше мнение, кто прав, кто виноват)
Deal with horrible things step by step. Stay cool.
#cppcore #compiler
#новичкам
Вот мы и рассмотрели все необходимые компоненты, чтобы понять, что написано здесь:
[[]][[]]int main()[[]]{{[][][[]][[]]{{{}}}(main);}}Если включить clang-format, то код преобразится во что-то такое:
/*1*/[[]][[]] int main()/*2*/[[]] {
{
[]/*3*/[[]](/*4*/[[]] /*5*/auto [])/*6*/[[]][[]] {
{
{}
}
}/*7*/(main);
}
}Давайте посмотрим, откуда так много скобок:
1️⃣ Перед типом возвращаемого значения main определены 2 пустые области для указания атрибутов функции main.
2️⃣ Перед телом функции main определена пустая область для атрибутов, применяемых к типу функции main.
3️⃣ После блока захвата лямбды определена пустая область для атрибутов, применяемых к самой лямбде.
4️⃣ Внутри списка параметров лямбды определена пустая область для атрибутов, применяемых к единственному параметру лямбды.
5️⃣ Сама лямбда является generic и принимает массив неизвестного типа.
6️⃣ Перед телом лямбды определены 2 пустые области для атрибутов, применяемых к типу лямбды.
7️⃣ Вызываем лямбду с помощью указателя на функцию main.
8️⃣ Ну и разбавили это дело несколькими лишними скоупами по пути.
Не так уж и сложно оказалось)
Так, новичковая часть закончилась.
#опытным
Интересно, что этот код компилируется на gcc, но не на clang.
cppinsights показывает, что лямбда раскрывается во что-то такое:
class __lambda_5_17 {
public:
template <class type_parameter_0_0>
inline /*constexpr */ auto operator()(auto *) const {
{ {}; };
}
private:
template <class type_parameter_0_0>
static inline /*constexpr */ auto __invoke(auto *__param0) {
return __lambda_5_17{}.operator()<type_parameter_0_0>(__param0);
}
public:
// /*constexpr */ __lambda_5_17() = default;
};То есть по факту мы имеем шаблонный оператор с auto параметром.
Как интерпретировать эту штуку - дело нетривиальное и по ходу компиляторы это делают по-разному. Видимо gcc при попытке инстанцировать шаблон с параметром int() выводит auto как тот же самый тип функции int() и в итоге лямбда принимает указатель на функцию. А clang при попытке инстанцировать шаблон выводит тип параметра функции как массив функций int() и не может принять main в качестве такого параметра.
Пишите ваше мнение, кто прав, кто виноват)
Deal with horrible things step by step. Stay cool.
#cppcore #compiler
Telegram
Грокаем C++
И это все компилируется!
Сможете сказать, откуда каждая скобка взялась?)
Сможете сказать, откуда каждая скобка взялась?)
❤🔥21😁12❤8👍3🔥2
Мок собеседования
#новичкам #опытным
Представьте, что вы вкатун в АйТишку плюсовую. Прочитали несколько книжек, прошли кучу бесплатных курсов и дописали свой первыйвелосипед пет-проект.
Пора бы попробовать устроиться на работу. Но учеба и учебные проекты - это одно, а собеседования - это совсем другое. Надо знать, как их проходить, это отдельная наука.
Но как узнать, как проходить собесы, если никогда их не проходил?(оставим за скобками вопрос, как вообще добраться до собеса, это то еще шаманство)
Для этого существуют мок-собеседования. То есть дословно "имитация" собеседования.
В идеале его проводить с живым человеком, например вашим ментором или любым другим опытным чуваком, который изъявляет желание.
Но если вы стеснительный волк-одиночка, к тому же еще и бедный(за занятия с ментором нужно платить), то и для вас есть вариант.
В сети лежит куча готовых мок-собеседований по С++ на позиции разных уровней.
На самом деле видео мок-собесов - не бомже-вариант, а маст хэв для любого человека, неуверенного в своих скиллах прохождения собеседований. Весь из себя сеньор, выходивший на рынок 10 лет назад, тоже скорее всего для себя что-то подчерпнет.
Даже в русскоязычном пространстве можно найти много таких видосов. Основные мок-интервьюеры у нас это:
- Ambushed raccoon
- Владимир Балун
Ну а мы за вас собрали подборку всех(или почти всех) мок-собесов на русском языке по С++ и разбили их по уровням.
Junior:
- https://www.youtube.com/watch?v=_-EkLLZ5svk
- https://www.youtube.com/watch?v=H1mIHJxnm9E
- https://www.youtube.com/watch?v=PQ1C_0EAHFI
- https://www.youtube.com/watch?v=BCpHj698D8U
- https://youtu.be/7g8HufwNa0g?si=XVKRsuoHN20MJx3i
- https://youtu.be/a18qTcWn-II?si=OttfqKh0bHLjueOY
- https://www.youtube.com/live/rLOgkn6xVQA?si=lFqwGf_Wsr8IohHo
- https://youtu.be/VfoxaNLVtmQ?si=elt-OyZWB5tXp5hI
Middle:
- https://www.youtube.com/watch?v=nMdNehH8-Ss
- https://www.youtube.com/watch?v=Ed37R0FvkQ8
- https://www.youtube.com/watch?v=IDqMy4_xkb4
- https://www.youtube.com/watch?v=BOUEbS5L4-8
- https://www.youtube.com/watch?v=PwVMcxCBIkg
- https://www.youtube.com/watch?v=Np6UrKN6ZbA
- https://www.youtube.com/watch?v=1Ez3kbK_3bI
- https://www.youtube.com/watch?v=s6BXbEPaw5g
- https://www.youtube.com/watch?v=yfoFtu28n4o
- https://www.youtube.com/watch?v=cT3fonCyxJk
- https://www.youtube.com/watch?v=xwb2FAKxCUo
- https://www.youtube.com/watch?v=bOgz4K-ARzQ
- https://www.youtube.com/watch?v=wR4VRCp_BYo
- https://youtu.be/5enBKMwOST0?si=i-5mdyrxeeFiZKo1
Senior:
- https://www.youtube.com/watch?v=OwMEK_W8Ysw
- https://www.youtube.com/watch?v=dZpe58HKX-8
Просто вопросы и задачи с собесов:
- https://www.youtube.com/watch?v=boYk6gFg84E
- https://www.youtube.com/watch?v=ViHNB0_1j90
- https://www.youtube.com/watch?v=aYM7lksQ8yg
- https://www.youtube.com/watch?v=UdY_YMFx7SY
- https://www.youtube.com/watch?v=PStQ4jhhz08
- https://www.youtube.com/watch?v=wMYfg_iPqMQ
Просмотрев эти видосы(возможно по нескольку раз) и переписав ответы на все вопросы себе в тетрадочку или файлик, вы будете знать ответы на 95% устных вопросов, которые вам будут задавать в условной компании Х.
Возможно вы не все будете до конца понимать, но уж очень глупых ошибок точно не совершите.
Ну а для любителем native english есть канал Кодингового Иисуса. Он постоянно у себя на стримах спрашивает у людей за плюсы. В основном люди валятся на простых вопросах, но иногда попадаются качественные собеседники. По крайней мере практика языка вам будет точно обеспечена.
Еще раз. Смотреть эти видосы можно(и почти нужно) примерно всем, кто задумывается о смене работы. Кому-то вспомнить, кому-то заполнить пробелы, кому-то понять, что все совсем плохо и садиться учить базу. Каждый найдет себе занятие по душе.
Practice makes perfect. Stay cool.
#interview
#новичкам #опытным
Представьте, что вы вкатун в АйТишку плюсовую. Прочитали несколько книжек, прошли кучу бесплатных курсов и дописали свой первый
Пора бы попробовать устроиться на работу. Но учеба и учебные проекты - это одно, а собеседования - это совсем другое. Надо знать, как их проходить, это отдельная наука.
Но как узнать, как проходить собесы, если никогда их не проходил?(оставим за скобками вопрос, как вообще добраться до собеса, это то еще шаманство)
Для этого существуют мок-собеседования. То есть дословно "имитация" собеседования.
В идеале его проводить с живым человеком, например вашим ментором или любым другим опытным чуваком, который изъявляет желание.
Но если вы стеснительный волк-одиночка, к тому же еще и бедный(за занятия с ментором нужно платить), то и для вас есть вариант.
В сети лежит куча готовых мок-собеседований по С++ на позиции разных уровней.
На самом деле видео мок-собесов - не бомже-вариант, а маст хэв для любого человека, неуверенного в своих скиллах прохождения собеседований. Весь из себя сеньор, выходивший на рынок 10 лет назад, тоже скорее всего для себя что-то подчерпнет.
Даже в русскоязычном пространстве можно найти много таких видосов. Основные мок-интервьюеры у нас это:
- Ambushed raccoon
- Владимир Балун
Ну а мы за вас собрали подборку всех(или почти всех) мок-собесов на русском языке по С++ и разбили их по уровням.
Junior:
- https://www.youtube.com/watch?v=_-EkLLZ5svk
- https://www.youtube.com/watch?v=H1mIHJxnm9E
- https://www.youtube.com/watch?v=PQ1C_0EAHFI
- https://www.youtube.com/watch?v=BCpHj698D8U
- https://youtu.be/7g8HufwNa0g?si=XVKRsuoHN20MJx3i
- https://youtu.be/a18qTcWn-II?si=OttfqKh0bHLjueOY
- https://www.youtube.com/live/rLOgkn6xVQA?si=lFqwGf_Wsr8IohHo
- https://youtu.be/VfoxaNLVtmQ?si=elt-OyZWB5tXp5hI
Middle:
- https://www.youtube.com/watch?v=nMdNehH8-Ss
- https://www.youtube.com/watch?v=Ed37R0FvkQ8
- https://www.youtube.com/watch?v=IDqMy4_xkb4
- https://www.youtube.com/watch?v=BOUEbS5L4-8
- https://www.youtube.com/watch?v=PwVMcxCBIkg
- https://www.youtube.com/watch?v=Np6UrKN6ZbA
- https://www.youtube.com/watch?v=1Ez3kbK_3bI
- https://www.youtube.com/watch?v=s6BXbEPaw5g
- https://www.youtube.com/watch?v=yfoFtu28n4o
- https://www.youtube.com/watch?v=cT3fonCyxJk
- https://www.youtube.com/watch?v=xwb2FAKxCUo
- https://www.youtube.com/watch?v=bOgz4K-ARzQ
- https://www.youtube.com/watch?v=wR4VRCp_BYo
- https://youtu.be/5enBKMwOST0?si=i-5mdyrxeeFiZKo1
Senior:
- https://www.youtube.com/watch?v=OwMEK_W8Ysw
- https://www.youtube.com/watch?v=dZpe58HKX-8
Просто вопросы и задачи с собесов:
- https://www.youtube.com/watch?v=boYk6gFg84E
- https://www.youtube.com/watch?v=ViHNB0_1j90
- https://www.youtube.com/watch?v=aYM7lksQ8yg
- https://www.youtube.com/watch?v=UdY_YMFx7SY
- https://www.youtube.com/watch?v=PStQ4jhhz08
- https://www.youtube.com/watch?v=wMYfg_iPqMQ
Просмотрев эти видосы(возможно по нескольку раз) и переписав ответы на все вопросы себе в тетрадочку или файлик, вы будете знать ответы на 95% устных вопросов, которые вам будут задавать в условной компании Х.
Возможно вы не все будете до конца понимать, но уж очень глупых ошибок точно не совершите.
Ну а для любителем native english есть канал Кодингового Иисуса. Он постоянно у себя на стримах спрашивает у людей за плюсы. В основном люди валятся на простых вопросах, но иногда попадаются качественные собеседники. По крайней мере практика языка вам будет точно обеспечена.
Еще раз. Смотреть эти видосы можно(и почти нужно) примерно всем, кто задумывается о смене работы. Кому-то вспомнить, кому-то заполнить пробелы, кому-то понять, что все совсем плохо и садиться учить базу. Каждый найдет себе занятие по душе.
Practice makes perfect. Stay cool.
#interview
❤53🔥17👍9❤🔥4🙏4
🎯 В C++ вызываемый объект — это намного больше, чем “функция”. За тридцать лет язык прошёл путь от простых указателей до мощных лямбда-выражений, которые лежат в основе современного параллелизма, асинхронности, алгоритмов и гибких архитектур.
На открытом уроке 18 декабря в 20:00 мск вы увидите эволюцию callable-подходов во всей широте: от function pointers и функторов до std::bind, std::function и современных лямбд. Разберёмся, что происходит «под капотом», как выбирать подходящий инструмент и почему именно лямбды стали стандартом промышленного C++.
Такой разбор особенно полезен тем, кто хочет писать более выразимый, модульный и безопасный код. Понимание callable-моделей даёт гибкость: проще проектировать API, реализовывать callback-механику, строить асинхронность, оптимизировать архитектуру приложения и мыслить “по-современному”.
⚡️Присоединяйтесь к открытому уроку в преддверие старта курса «C++ Developer» — получите базу, на которой строятся потоки исполнения, асинхронные модели и ключевые элементы современного C++20/23: https://otus.pw/cY5r/
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
На открытом уроке 18 декабря в 20:00 мск вы увидите эволюцию callable-подходов во всей широте: от function pointers и функторов до std::bind, std::function и современных лямбд. Разберёмся, что происходит «под капотом», как выбирать подходящий инструмент и почему именно лямбды стали стандартом промышленного C++.
Такой разбор особенно полезен тем, кто хочет писать более выразимый, модульный и безопасный код. Понимание callable-моделей даёт гибкость: проще проектировать API, реализовывать callback-механику, строить асинхронность, оптимизировать архитектуру приложения и мыслить “по-современному”.
⚡️Присоединяйтесь к открытому уроку в преддверие старта курса «C++ Developer» — получите базу, на которой строятся потоки исполнения, асинхронные модели и ключевые элементы современного C++20/23: https://otus.pw/cY5r/
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
❤4👍3🔥3🥱3
Как запустить cpp файл из консоли?
Да, да, именно запустить файл. Берете bash, берете файл. Как одно воткнуть в другое и получить результат работы программы?
Следите за пальцами:
Есть ли мы попытаемся запустить файл с этим содержимым через терминал, то будет выполняться следующая последовательность шагов:
1️⃣ Ядро смотрит на первые 2 байта файла и пытается найти там шебанг -
2️⃣ Если шебанга нет и файл не является исполняемым(как у нас), то файл считается shell-скриптом и исполняется с помощью текущего командного интерпретатора.
3️⃣ Для shell-скриптов символ # обозначает начало однострочного комментария, поэтому первая строчка игнорируется.
4️⃣ Интерпретатор встречает команду компиляции и выполняет ее.
5️⃣ Теперь мы пытаемся реально скомпилировать этот файл с помощью g++. На этапе препроцессинга ветка условия
6️⃣ После компиляции интерпретатор запускает исполняемый файл и завершает работу на инструкции
Напишите в терминале
И увидите заветные слова.
Дожили! Превратили С++ в питон...
PS: Большое спасибо за идею и материалы Даниилу @dkay7.
Have a fun. Stay cool.
#fun
Да, да, именно запустить файл. Берете bash, берете файл. Как одно воткнуть в другое и получить результат работы программы?
Следите за пальцами:
#if 0
g++ test.cpp -o test && ./test
exit 0
#endif
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
}
Есть ли мы попытаемся запустить файл с этим содержимым через терминал, то будет выполняться следующая последовательность шагов:
1️⃣ Ядро смотрит на первые 2 байта файла и пытается найти там шебанг -
#!. Если нашла, то в этой строчке будет указан путь до нужного интерпретатора.2️⃣ Если шебанга нет и файл не является исполняемым(как у нас), то файл считается shell-скриптом и исполняется с помощью текущего командного интерпретатора.
3️⃣ Для shell-скриптов символ # обозначает начало однострочного комментария, поэтому первая строчка игнорируется.
4️⃣ Интерпретатор встречает команду компиляции и выполняет ее.
5️⃣ Теперь мы пытаемся реально скомпилировать этот файл с помощью g++. На этапе препроцессинга ветка условия
#if 0 выбросится из текста файла и компилироваться будет только реально С++ код.6️⃣ После компиляции интерпретатор запускает исполняемый файл и завершает работу на инструкции
exit 0. Остальной С++ код он уже не увидит.Напишите в терминале
chmod +x test.cpp
./test.cpp
И увидите заветные слова.
Дожили! Превратили С++ в питон...
PS: Большое спасибо за идею и материалы Даниилу @dkay7.
Have a fun. Stay cool.
#fun
❤51🔥17👍16😁13🤯11👀2🗿2🐳1
Bootstrap
#новичкам
Если вы когда-нибудь заходили в исходники своего компилятора, то могли заметить, что его исходный код написан на С++.
Также возможно вы слышали, что компилятор java написан на java.
Если вдуматься в эти факты, то невольно задаешься вопросом: а как это вообще возможно? Нельзя же себя поднять за волосы из болота.
Вообще-то барон Мюнхгаузен смог и мы сможем!
Нужно лишь немного опоры. Чуть-чуть оттолкнуться.
Давайте пройдем весь процесс от и до.
1️⃣ Вы решили написать свой язык самый лучший язык во всей вселенной GOAT. Разработали синтаксис и правила языка, пару раз пооргазмировали от его крутости.
2️⃣ Дальше вам нужно уметь конвертировать код на новом языке в программу. Выхода нет: вы быстренько пишите простой компилятор или интерпретатор GOAT на уже существующем языке. Пусть для определенности вы написали компилятор на С. Компилируете его каким-нибудь gcc и у вас появляется первый компилятор языка GOAT. Это так называемый компилятор начальной загрузки или bootstrap компилятор.
Отлично! Вы можете писать программы на новом языке!
3️⃣ Но компилятор - это же тоже программа. Теперь вы можете на GOAT написать компилятор GOAT и скомпилировать его bootstrap компилятором. В итоге у вас получится рабочий компилятор GOAT, написанный на языке GOAT! Вот яйцо и родило яйцо.
4️⃣ В дальнейшем вы можете развивать компилятор, используя сам язык GOAT и перекомпилируя его самим собой.
Весь этот процесс называется bootstrapping.
Первый компилятор и jvm были написаны на С. Первый компилятор С был написан на ассемблере. Pascal'я - на Fortran.
Интересно, что clang изначально был написан на урезанной версии С++, которая компилировалась и на gcc, и на msvc. Поэтому clang можно использовать на винде.
Преимущества бутстраппинга:
👉🏿 Демонстрирует зрелость и самодостаточность языка. Можно развиваться самостоятельно и не зависеть от других технологий.
👉🏿 Разработчики компилятора могут использовать все возможности языка, для которого они пишут компилятор.
👉🏿 Тестирование возможностей языка. Если вам недостаточно инструментов в языке, чтобы написать компилятор, значит язык нужно дорабатывать.
👉🏿 Упрощается разработка компилятора. Не нужно знать несколько языков.
А вот как бутстрапился С++ разберем в следующем посте.
Be self-sufficient. Stay cool.
#tools #compiler
#новичкам
Если вы когда-нибудь заходили в исходники своего компилятора, то могли заметить, что его исходный код написан на С++.
Также возможно вы слышали, что компилятор java написан на java.
Если вдуматься в эти факты, то невольно задаешься вопросом: а как это вообще возможно? Нельзя же себя поднять за волосы из болота.
Вообще-то барон Мюнхгаузен смог и мы сможем!
Нужно лишь немного опоры. Чуть-чуть оттолкнуться.
Давайте пройдем весь процесс от и до.
1️⃣ Вы решили написать свой язык самый лучший язык во всей вселенной GOAT. Разработали синтаксис и правила языка, пару раз пооргазмировали от его крутости.
2️⃣ Дальше вам нужно уметь конвертировать код на новом языке в программу. Выхода нет: вы быстренько пишите простой компилятор или интерпретатор GOAT на уже существующем языке. Пусть для определенности вы написали компилятор на С. Компилируете его каким-нибудь gcc и у вас появляется первый компилятор языка GOAT. Это так называемый компилятор начальной загрузки или bootstrap компилятор.
Отлично! Вы можете писать программы на новом языке!
3️⃣ Но компилятор - это же тоже программа. Теперь вы можете на GOAT написать компилятор GOAT и скомпилировать его bootstrap компилятором. В итоге у вас получится рабочий компилятор GOAT, написанный на языке GOAT! Вот яйцо и родило яйцо.
4️⃣ В дальнейшем вы можете развивать компилятор, используя сам язык GOAT и перекомпилируя его самим собой.
Весь этот процесс называется bootstrapping.
Первый компилятор и jvm были написаны на С. Первый компилятор С был написан на ассемблере. Pascal'я - на Fortran.
Интересно, что clang изначально был написан на урезанной версии С++, которая компилировалась и на gcc, и на msvc. Поэтому clang можно использовать на винде.
Преимущества бутстраппинга:
👉🏿 Демонстрирует зрелость и самодостаточность языка. Можно развиваться самостоятельно и не зависеть от других технологий.
👉🏿 Разработчики компилятора могут использовать все возможности языка, для которого они пишут компилятор.
👉🏿 Тестирование возможностей языка. Если вам недостаточно инструментов в языке, чтобы написать компилятор, значит язык нужно дорабатывать.
👉🏿 Упрощается разработка компилятора. Не нужно знать несколько языков.
А вот как бутстрапился С++ разберем в следующем посте.
Be self-sufficient. Stay cool.
#tools #compiler
❤29👍15🔥6🤯1