All streams
Search
Write a publication
Pull to refresh
38
0.1
Николай Меркин @nickolaym

User

Send message

Наверное, смысл в tiny - в меньшем энергопотреблении?

В том и прикол, что гцц значение концепта (булевой константы) кеширует в лучшем виде.

В вот значение атомарного ограничения (но только для данного концепта) он иногда пересчитывает, а иногда кеширует. Переход с false на true он диагностирует, а в обратную сторону - не догадался. Надо будет им зарепортить, уж больно смешной недочёт. Они не предполагали, что ограничение может откатываться.

Есть разные способы откатить ограничение вида `requires{ f(x); }`.

  • явно запретить наилучшую сигнатуру, `= delete`

  • добавить конкурирующие сигнатуры, чтобы нельзя было выбрать наилучшую

  • какой-то ещё способ я случайно нашёл, когда писал статью, но сейчас что-то не могу вспомнить

Ещё замечание.

Во-первых, у конструктора (конструкторов) может быть более одной перегрузки. Поэтому Reflect<T> по-хорошему должен был бы отдавать не один кортеж типов, а набор кортежей.

Во-вторых, если конструктор содержит шаблонные аргументы, то Caster сработает криво. Либо конструктор примет его как родного, либо не примет вообще, потому что это будет считаться двойным неявным приведением.

В-третьих, если конструктор принимает initializer_list, - то получится странное

struct X { X(std::initializer_list<int>); };
struct Y { Y(char, std::initializer_list<int>); };

// caster
struct C { template<class T> opertor T() const; };

requires{ X{}; }  // якобы 0-арный
requires{ X{{C{}}}; }  // int - чтобы узнать тип элемента списка

requires{ Y{C{}, C{}}; }  // char, initializer_list<int> - тут всё просто

В-четвёртых, явно объявленные стандартные конструкторы - копирования/перемещения - добавят угару. Даже если они =default.

В общем, техника имеет ряд ограничений, либо её надо допиливать и тестировать на всём безумном сочетании условий.

Тут затесалась ошибка, делающая даже не UB, а IFNDR.

Во-первых, шаблонная константа kTest инстанцируется в точке первого использования - и далее переиспользуется. Поэтому

static_assert(!kTest<U>);
...
static_assert( kTest<U>);

не прокатит. Либо первый ассерт упадёт, либо второй.

Во-вторых, https://timsong-cpp.github.io/cppwp/temp.constr.constr#temp.constr.atomic-3.sentence-5

Даже если мы будем перезапрашивать выражение у данной шаблонной функции

template<class T, int UniqueID> constexpr bool kTest = requires{.....T....};

static_assert(!kTest<U, __LINE__>);
...
static_assert( kTest<U, __LINE__>);

или чтоб совсем по-хитрому

template<class T, auto F=[]{}> constexpr bool kTest = requires{.....T....};

static_assert(!kTest<U /* , F = some unique lambda */>);
...
static_assert( kTest<U /* , F = some unique lambda */>);

то нарушим указанный пункт стандарта.

Вот можете полюбоваться

https://habr.com/ru/articles/857744/

Чиселки я привёл лишь затем, чтобы явно показать разные воплощения шаблона.

На самом деле, противоядие от "the satisfaction result is different for identical atomic constraints" состоит в том, чтобы сделать ограничения не идентичными. Для этого и надо пропихнуть переменный параметр шаблона внутрь.

// всё ещё ill-formed-опасная версия
template<class T, int I> concept boo =
  requires(T x) { f(x); }  // одно атомарное ограничение
  && (I != 0);  // другое атомарное ограничение (ну например)

// well-formed
template<class T, int I> concept boo =
  requires(T x) { f(x); I; };  // одно большое атомарное ограничение

Трюк с неявным уникальным параметром шаблона - а каждая лямбда уникальна - это дополнительный уровень. В первую очередь, чтобы не писать чиселки. Чтобы каждое воплощение шаблона было новым. Но в любом случае, придётся упоминать его внутри requires.

Но мне кажется, что это плохая практика для больших программ. Как минимум, это приведёт к размножению воплощений. Сведём с ума сперва компилятор, а затем линкер...

Разве что как тест на ill-formed (godbolt):

// основной концепт для работы
template<class T> concept boo =
  requires(T x) { f(x); };

// проверочное значение "по месту" - уже не концепт, во избежание
template<class T, auto F = []{}> constexpr bool boo_checker =
  requires(T x) { f(x); F(); };

.....

void g(boo auto x) {
  static_assert(boo_checker<decltype(x)>);
}
void g(auto x) {
  static_assert(!boo_checker<decltype(x)>);
}

Вещественный остаток - это всё те же игры с округлением, только спрятанные под капот.

Так что не считается!

В куске гипса квадратное делается элементарно. Опалубка из досок, по-вашему, какая проще? А мелкие отверстия - ну, возьмите брусок и ткните в гипс до того, как он схватится.

"За N комментов до Гитлера"

Но кстати, как раз Гитлер сперва настаивал на фрактуре, а потом передумал и стал настаивать на антикве.

Вы хорошо понимаете теорию погрешностей?

А если программист, который сравнивает вещественные числа, как раз хорошо понимает, и следит за наличием-отсутствием погрешностей в своём коде? У него будет результат ХЗ? Или это у вас будет мнение ХЗ о его результате?

Давайте начнём с самого простого: 0.5 == 1.0 / 2.0, тут хз или не хз?

Хахаха, автор написал статью про джунов, а тут к нему набежали сеньоры, которые ещё и в теории погрешностей разбираются.

Кстати о погрешностях.

Знаете такую штуку, как функция линейной интер/экстраполяции?

lerp(x, y, r) = x + (y-x) * r

lerp(x, y, 0) = x

lerp(x, y, 1) = y

Казалось бы, что может пойти не так? А много что. Ошибка нормализации разности, и вжух, lerp(x,y,1) > y.

Правильный способ описан, например, вот тут:

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0811r3.html

а в std::lerp для вещественных чисел даже используется FMA вместо отдельного умножения и сложения.

Тем не менее, банковское округление и округление от нуля - это тоже традиционные. В том смысле, что используются на практике, а не добавлено "с потолка", лишь бы было.

Поскольку "к ближайшему целому" неоднозначно для полуцелых чисел, то опытный кодер обязательно уточнил бы у собеседующего, чего именно тот хочет. А заодно и рассказал бы и про три самых популярных.

Тут, конечно, для собеседующего может прозвенеть красный звоночек overqualified, "чего это я собеседую на позицию джуна, а ко мне пришёл то ли мидл (а то и сеньор), то ли лютый зануда..."

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

Малболге хотя бы подкинул головняка.

Я уже и забыл про их существование... Ну, помянем.

1) Административное решение. Не принимать библиотеку в ветку qa-checked или как оно там называется в вашем репозитории. А если прямо совсем мешает разработке, то отревертить в вашей основной ветке - мастер/транк/как оно там называется.

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

Как вообще можно было вмержить в мастер эпохальное изменение, минуя тесты? Или часть библиотек живёт вне репозитория? Ну хотя бы докерфайл же должен жить в репе? Значит, надо переделывать, чтобы системные зависимости тоже там оказались.

2) Выкиньте свой boid, вы рискуете отхватить проблемы в самых неожиданных местах, где будет делаться проверка на void / не-void. Потому что вжух, и там окажется не-void.

Кстати, зачем у boid конструктор от более чем одного аргумента, да ещё и с приплясыванием "кроме наследников boid"? Чую, у вас там где-то говнокод с оператором запятая притаился, который оказался вовсе не оператором...

А не хотите ли проверить, что где-то запятая ведёт себя не так, как вы ожидаете?

3) Специализация / if-constexpr в общем случае, плюс разные техники scope guard там, где они применимы.

4) Фактически, вам очень жирно подсветили, что именно ваша обвязка не умеет работать с void. Заодно проверьте, умеет ли она корректно работать с другими разнообразными типами. Ссылочные, некопируемые, неполные, - вот это всё.

Вот прямо сходу бросается в глаза:

template <typename F, typename... Args>
auto test_running_time(pcstr test_name, F&& f, Args&&... args) {
    .....
    auto result = gtl::invoke(gtl::forward<F>(f), gtl::forward<Args>(args)...);
    .....
    return result;
}

Локальная переменная по значению, и возврат по значению. Знаете, сколько граблей тут рассыпано? Часть из них можно было бы убрать с помощью decltype(auto) .

5) Юниттесты на вашу библиотеку тестов есть? Они покрывают это разнообразие?

На списках - сам бог велел слиянием сортировать. Это даёт гарантии на логлинейное время.

Настоящий квиксорт на массиве - тоже неустойчивый.

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

А вот плюс - в том, что она гарантирует логлинейное время.

Промышленные методы сортировки - это сплав быстрой, вставок и пирамиды.

  • если массив маленький, то вставками он сортируется быстрее, просто потому, что два цикла быстрее, чем рекурсии

  • если глубина рекурсии превысила порог, кратный логарифму длины массива, это признак того, что набор плохой для квиксорта, и надо валить оттуда, пока мы до квадрата не доигрались, - запускают пирамиду

А потому что халява сэр. Ещё бы на хаскелле написали, там синтаксического шума примерно в пять раз меньше будет.

Сделать inplace quicksort на питоне - примерно столько же кода, как на яве, ну вдвое меньше строк за счёт фигурных скобок.

это шараханья туда-сюда.

с пикабу на реддит по случаю любого первого попавшегося бунда

с реддита на пикабу по случаю любого невменяемого модерирования

История реддита показывает, что как только аудитория оказывается достаточно лакомым куском для Очень Больших Политиков, они захватывают инициативу и устраивают там филиал демпартии США.

А, упс. Ну в таком случае для некоторых n решения тупо нет.

Information

Rating
3,981-st
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Registered
Activity