Гепардово гнездо
638 subscribers
41 photos
1 file
81 links
El nido del guepardo.

@gepardius тащит в гнездо всякое интересное из мира программирования (и не только).

Etaoin shrdlu cmfwyp!

Еще один мой канал: @sofcheck.
Download Telegram
Обидно, что реакции в Telegram можно лишь искать в списке, а не набирать, как обычные эмодзи. А то иногда я провожу много времени, чтобы найти подходящую реакцию.

А еще нет раздела, в котором можно смотреть только стандартные реакции. А то они видны только среди недавних и замешаны вместе со всеми остальными :(

Для тех, кто не в курсе набора эмодзи с клавиатуры: попробуйте начать набирать :think или :думаю и посмотрите, что предлагает автодополнение :) И так работает со всеми эмодзи.
👍4🕊2🐳1
Еще в тему автодополнения и Telegram: если вы пользуетесь Telegram Desktop, попробуйте набрать :shrug:. Получится ¯\_(ツ)_/¯
👍3
Я обычный человек и печатаю своими человеческими руками

Оригинал (или вот ссылка на перевод)
😁4👍1🤯1
И еще в тему пауков 🕷🕷🕷
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Про явную специализацию шаблонной функции в С++

Рассмотрим такой код из нескольких файлов.

Файл a.h:
#include <iostream>

template<int N> void doit() { std::cout << 1 << std::endl; }

Файл a.cpp:
#include "a.h"

template<> void doit<1>() { std::cout << 2 << std::endl; }

Файл b.cpp:
#include "a.h"

int main() { doit<1>(); }

Что выдаст код — 1 или 2?

(Правильный ответ с объяснением ждите завтра!)
Что выдаст код?
Final Results
23%
1
23%
2
23%
Неизвестно
32%
🐳
🐳5
Про явную специализацию шаблонной функции в С++, часть 2

Что выдаст код — 1 или 2?

Зависит от настроек линковки!
$ g++ a.cpp b.cpp -o a -fuse-ld=lld && ./a
2
$ g++ -Wl,--start-lib a.cpp b.cpp -Wl,--end-lib -o a -fuse-ld=lld && ./a
1

Почему так получается? Все просто: символы, полученные при явной специализации, являются сильными, а при отсутствии явной специализации — слабыми. В первом случае получается так, что слабый символ перекрывается сильным, и мы получаем 2. Во втором случае (с --start-lib) сильный символ не используется, поскольку линкер видит, что у нас уже есть слабый символ, и игнорирует содержимое a.o, который был получен из a.cpp. Но тогда мы получаем 1.

(Если кто-то не понял объяснение выше, то советую для начала прочитать замечательный пост про линковку, а потом перечитать абзац выше еще раз. Если все равно непонятно, просите помощи в комментах.)

А вообще, судя по StackOverflow, программа попросту некорректна. Цитируя Стандарт C++,
> If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.
👍2😱2
Честно украдено с просторов интернетов (увы, не помню, откуда точно)
👍13
Может ли человек генерировать случайные числа независимо и равновероятно?

Попробуйте Aaronson Oracle. Его суть проста: надо нажимать случайно на клавиатуре буквы f и d. А далее простая модель (про ее работу можно почитать здесь) будет пытаться предсказывать вашу следующую букву.

Если вы набираете буквы равновероятно и независимо, то модель будет угадывать примерно в 50% случаев. Если нет — то ваш паттерн набора, скорее всего, все еще будет случайным, но явно не будет похож на последовательность независимых случайных событий.

Можете считать этот сайт наглядной демонстрацией того, что люди не понимают интуитивно, как должна выглядеть последовательность испытаний Бернулли (т.е. бросков монетки, после которых фиксируется, упала ли монета орлом или решкой)
👍3
Как экстренно оборвать сеанс SSH, не закрывая консоль

Короткий ответ: <Enter>~. (Enter, тильда, точка)

Длинный ответ: ~ включает Escape-последовательности в SSH. До этого надо предварительно нажать Enter. После этого можно вводить команды:
Supported escape sequences:
~. - terminate connection (and any multiplexed sessions)
~B - send a BREAK to the remote system
~C - open a command line
~R - request rekey
~V/v - decrease/increase verbosity (LogLevel)
~^Z - suspend ssh
~# - list forwarded connections
~& - background ssh (when waiting for connections to terminate)
~? - this message
~~ - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)

Чтобы выйти из вложенной SSH-сессии, не выходя из текущей, надо набрать <Enter>~~. (Enter, тильда, тильда, точка). Дело в том, что внешняя SSH-сессия преобразует ~~ в ~ и передаст во внутреннюю, а та уже, в свою очередь, спокойно завершится.
👍2
Шахматная задача на сегодня

(см. картинку выше)

Белые начинают и ставят мат.

FEN: 8/8/8/2p5/1pp5/brpp4/qpprpK1P/1nkbn3 w - - 0 1
Забавный Юникод

Угадайте, почему так происходит:
>>> ''.join(reversed('🇧🇬🇦🇲'))
'🇲🇦🇬🇧'

(запускалось в консоли Python)

В чем же дело? Догадки можно писать в комменты, завтра будет ответ.
😁6
Забавный Юникод: объяснение

На самом деле, все предельно просто.

Как известно, в Python строки хранятся в UTF-32, то есть по одному code point'у на один элемент строки. Таким образом, переворот строки происходит на уровне code point'ов.

Но отображение текста работает хитрее — каждый видимый на экране символ необязательно состоит из одного code point'а, может из нескольких. Такая совокупность code point'ов, образующая один видимый символ, называется extended grapheme cluster. Вычисление границ extended grapheme cluster происходит по определенному сложному алгоритму (который еще и дополняется при выходе новых версий Юникода!).

Теперь вернемся к нашим баранам флагам. На самом деле, в числе code point'ов Юникода нет никаких флагов, зато есть 26 специальных «букв» от A до Z. Две такие «буквы» интерпретируются как один extended grapheme cluster, а образовавшийся от них код страны как раз и задает флаг.

Например, строка '🇧🇬🇦🇲' состоит из четырех таких «букв»: BGAM (или 🇧 🇬 🇦 🇲, если у вас «буквы» умеют отображаться по отдельности). При реверсе мы получаем MAGB, где MA составляет 🇲🇦 (Марокко), а GB🇬🇧 (Британию)

Таким образом, загадка разгадана :)
🔥12
Как получить красивое имя типа в виде строки в C++

Есть два варианта.

Первый: через std::type_info::name(). Увы, в этом случае вернется замангленное имя, и его придется как-то приводить в человекочитаемый вид. Для этого нужно возиться с функциями вроде abi::__cxa_demangle() и подобными. Увы, такие вещи компиляторо-специфичны. Пример деманглинга (работает с GCC и скорее всего с Clang) можно посмотреть здесь.

Второй: использовать элегантный хак. Этот способ тоже компиляторо-специфичен: он точно работает для GCC, насчет других компиляторов не знаю. Дело в том, что в GCC есть __PRETTY_FUNCTION__, вместо которого компилятор подставляет имя текущей функции вместе со всеми ее шаблонными параметрами. Отсюда мы как раз можем выковырять имя типа. Вот пример реализации.
👍5
Форк-бомба Фибоначчи

Эта форк-бомба работает так, что количество процессов на i-й секунде работы равно i-му числу Фибоначчи.

Код:
#include <unistd.h>

int main() {
sleep(1);
do {
sleep(1);
if (!fork()) sleep(1);
} while (1);
}
👍7
Forwarded from Блог*
#prog #cpp #моё

В C++ есть такая вещь, как strict aliasing. Если коротко, то это предположение компилятора о том, что доступы по указателям (и ссылкам) существенно разных типов не пересекаются между собой. Подробнее про это можно прочитать, например, тут, ну а я покажу, как это влияет на (не)возможность оптимизировать код на C++. Все приведённые ниже примеры будут использовать компилятор GCC 12.2 с флагами --std=c++20 -O2 -pedantic-O3 компилятор векторизует код и делает его гораздо объёмнее и менее понятным).

Напишем вот такой код (где std::span<int> играет примерно ту же роль, что и &mut [i32] в Rust):

#include <span>

void increment(std::span<int> arr, const int* value) {
for (auto& x: arr) {
x += *value;
}
}

(в дальнейшем для экономии места я буду опускать #include <span>)

Смысл этого кода очень простой: увеличить все числа в данном диапазоне на данную величину. Казалось бы, тут в цикле есть доступ по указателю, который имеет смысл вынести из тела (loop-invariant code motion). Но во что этот код переводит компилятор?

lea rcx, [rdi+rsi*4]
cmp rdi, rcx
je .L1
.L3:
mov eax, DWORD PTR [rdx]
add DWORD PTR [rdi], eax
add rdi, 4
cmp rdi, rcx
jne .L3
.L1:
ret

Тело цикла располагается между метками .L1 и .L3. Конкретно сейчас нас интересуют две инструкции:

mov eax, DWORD PTR [rdx]
add DWORD PTR [rdi], eax

Выходит, что в регистре rdx располагается адрес, указатель value, а в регистре rdix, адрес текущего элемента спана. На каждой итерации процессор загружает значение из памяти и потом складывает со значением в другом месте в памяти. Почему же?

Дело в том, что переданный указатель может указывать на сам элемент внутри спана. increment может быть использована, например, так:

void use_increment(std::span<int> a) {
increment(a, &a[1]);
}

И const в составе типа указателя тут не панацея: он лишь запрещает модифицировать доступ через указатель, но не гарантирует, что значение, на которое он указывает, действительно не изменяется. Выходит, компилятор генерирует неэффективный код ради того, чтобы правильно работал случай, который программист навряд ли стал бы писать намеренно.

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

Попробуем, например, short:

void increment(std::span<int> arr, const short* value) {
// тело без изменений
}

Что генерирует компилятор?

lea rax, [rdi+rsi*4]
cmp rdi, rax
je .L1
movsx edx, WORD PTR [rdx]
.L3:
add DWORD PTR [rdi], edx
add rdi, 4
cmp rdi, rax
jne .L3
.L1:
ret

Ага, то есть доступ к value (с sign extension, разумеется) —

movsx edx, WORD PTR [rdx]

— вынесен за пределы цикла! Так в чём же разница по сравнению с предыдущими примерами?

Вот тут как раз и вступает в силу правила strict aliasing (aka последний параграф в [basic.lval]): не смотря на то, что сформировать указатель на short из указателя на int можно, эти два типа отличаются, и получение доступа к значению одного типа через указатель на другой является неопределённым поведением. Так как в корректной программе на C++ неопределённого поведения не может произойти, компилятор использует этот факт, чтобы обосновать корректность выноса доступа к памяти из цикла.

Однако! У правил strict aliasing есть нюансы насчёт того, по указателям (на самом деле glvalue, но не суть) каких типов можно получать доступ к значениям других типов. В частности, unsigned и signed варианты того же типа не считаются существенно отличными, и потому при передаче const unsigned* value компилятор оставляет доступ к value в теле цикла.
🔥3
Forwarded from Блог*
И ещё у этого правила есть совсем неожиданное исключение: доступ по указателю на char, unsigned char и std::byte. При передаче value по указателю на один из этих типов компилятор оставляет доступ в цикле.

Внимательные читатели могли заметить, что этот список не включает в себя signed char (и, кстати, в этом месте C++ отличается C, в котором алиаситься могут указатели любых вариантов char). Тем не менее, вариант increment с указателем на signed char последние версии и GCC (12.2), и clang (15.0.0) не могут скомпилировать с выносом доступа к value из цикла. Почему — не ясно. Наверное, это можно считать багом.

Поиграться со всеми упомянутыми вариантами: godbolt.org/z/8rr3hWTbf
(ссылка для расшаривания, к сожалению, потеряла все имена вкладок с кодом. Ну хоть имена вкладок с компиляторами оставила)
Как измерить высоту здания с помощью барометра?

Сэр Эрнест Резерфорд, президент Королевской Академии и лауреат Нобелевской премии по физике, рассказывал следующую историю, служащую великолепным примером того, что не всегда просто дать единственно правильный ответ на вопрос.

Некоторое время назад коллега обратился ко мне за помощью. Он собирался поставить самую низкую оценку по физике одному из своих студентов, в то время как этот студент утверждал, что заслуживает высшего балла. Оба, преподаватель и студент согласились положиться на суждение третьего лица, незаинтересованного арбитра; выбор пал на меня.

Экзаменационный вопрос гласил: «Объясните, каким образом можно измерить высоту здания с помощью барометра». Ответ студента был таким: «Нужно подняться с барометром на крышу здания, спустить барометр вниз на длинной веревке, а затем втянуть его обратно и измерить длину веревки, которая и покажет точную высоту здания».

Случай был и впрямь сложный, так как ответ был абсолютно полным и верным! С другой стороны, экзамен был по физике, а ответ имел мало общего с применением знаний в этой области.

Я предложил студенту попытаться ответить еще раз. Дав ему шесть минут на подготовку, я предупредил его, что ответ должен демонстрировать знание физических законов. По истечении пяти минут он так и не написал ничего в экзаменационном листе. Я спросил его, сдается ли он, но он заявил, что у него есть несколько решений проблемы, и он просто выбирает лучшее.

Заинтересовавшись, я попросил молодого человека приступить к ответу, не дожидаясь истечения отведенного срока. Новый ответ на вопрос гласил: «Поднимитесь с барометром на крышу и бросьте его вниз, замеряя время падения. Затем, используя формулу L = (a*t^2)/2, вычислите высоту здания».

Тут я спросил моего коллегу, преподавателя, доволен ли он этим ответом. Тот, наконец, сдался, признав ответ удовлетворительным. Однако студент упоминал, что знает несколько ответов, и я попросил его открыть их нам.

«Есть несколько способов измерить высоту здания с помощью барометра», начал студент. «Например, можно выйти на улицу в солнечный день и измерить высоту барометра и его тени, а также измерить длину тени здания. Затем, решив несложную пропорцию, определить высоту самого здания.»

«Неплохо», сказал я. «Есть и другие способы?»

«Да. Есть очень простой способ, который, уверен, вам понравится. Вы берете барометр в руки и поднимаетесь по лестнице, прикладывая барометр к стене и делая отметки. Сосчитав количество этих отметок и умножив его на размер барометра, вы получите высоту здания. Вполне очевидный метод.»

«Если вы хотите более сложный способ», продолжал он, «то привяжите к барометру шнурок и, раскачивая его, как маятник, определите величину гравитации у основания здания и на его крыше. Из разницы между этими величинами, в принципе, можно вычислить высоту здания. В этом же случае, привязав к барометру шнурок, вы можете подняться в вашим маятником на крышу и, раскачивая его, вычислить высоту здания по периоду прецессии.»

«Наконец», заключил он, «среди множества прочих способов решения проблемы лучшим, пожалуй, является такой: возьмите барометр с собой, найдите управляющего зданием и скажите ему: «Господин управляющий, у меня есть замечательный барометр. Он ваш, если вы скажете мне высоту этого здания».

Тут я спросил студента — неужели он действительно не знал общепринятого решения этой задачи. Он признался, что знал, но сказал при этом, что сыт по горло школой и колледжем, где учителя навязывают ученикам свой способ мышления.

Студентом этим был Нильс Бор (1885–1962), датский физик, лауреат Нобелевской премии 1922 г.
🔥5
Для любителей ассемблера

Советую почитать книгу xchg rax, rax. В ней нет ничего, кроме 0x40 (т.е. 64) страниц, на каждой из которых расположен листинг ассемблерного кода без никаких пояснений. От читателя требуется понять написанный код и разгадать, что он делает. На просторах Интернетов есть подсказки, но лучше попытаться без них :)

[для тех, кто хочет подсказок: вот и вот, а лучше решайте сами :)]

Еще забавно, что автор продает бумажную версию.

Когда я обнаружил эту книгу, я залип и не мог больше ничего делать, пока не досмотрел до конца. Многое (однако не все) я смог решить, тогда пришлось смотреть подсказки. Загадкой остается лишь номер 0x16, про который даже в подсказках нет идей :( Но если кто-то здесь решил, можете смело писать в комментариях :)
👍2🤯1