Open apolukhin opened 3 years ago
yndx-antoshkka, 28 декабря 2018, 15:35 Подобные предложения уже рассматриваются комитетом: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0199r0.pdf, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0814r2.pdf
Igor Baidiuk, 28 декабря 2018, 15:46 yndx-antoshkka, спасибо за ссылки. Если я правильно понимаю, первый концентрируется на default hash operator, а второй - на hash_combine. Первый ортогонален, а второй - не решает проблему "прибитости гвоздями" хеш-оператора к конкретному алгоритму. Это же предложение лежит в несколько иной плоскости.
Igor Baidiuk, 28 декабря 2018, 15:52 Нашёл этот пропозал: p0029r0 Пока что наиболее близко к теме.
Айдар Фаттахов, 2 февраля 2019, 14:26
Отделить функцию хэширования от данных Типы добавляют метод
Igor Baidiuk, 27 мая 2019, 14:29 asschool, не вижу противоречия. Упоминаемый метод ничего не говорит об алгоритме хеширования, а только о том, какие данные передаются в хешер. Алгоритм вычисления хеша лежит отдельно. Под "функцией хеширования" как правило понимают именно алгоритм.
Айдар Фаттахов, 27 мая 2019, 16:29 Igor Baidiuk,
Каждому конкретному типу не требуется ни знать про хеширование как таковое
Вы добавляете метод hash
Мне кажется в вашей модели визитор является лишним элементом, достаточно std::hash::operator() прнимать hasher
Решил воскресить эту идею. Постарался расписать основные моменты, в т.ч. кратко сравнить с P0814: Separate type hashing method from hash algorithm
Отличное начало!
Посмотрите ещё на https://wg21.link/p0029r0, там схожие идеи но сильно устеревшее предложение.
Ещё стоит продумать следующие моменты:
Hasher
в текущем описании приведёт к бесконечной рекурсииHashFunc
должен быть объектом, при этом у него может быть огромный стейтПосмотрите ещё на https://wg21.link/p0029r0, там схожие идеи но сильно устеревшее предложение.
Спасибо, посмотрю. Пока первое, что бросается в глаза - сборка хеша методом композиции. Я так понимаю, для некоторых хеш-функций это не лучшая идея т.к. первичная инициализация и подсчёт финального резуьтата могут быть относительно дорогими.
как использовать с контейнерами
Контейнер будет просто вызывать compute_hash_value(hash_func, value)
. Забыл добавить примеров, как раз опишу этот момент.
нужна функция для получения результата из хешера, результат не надо считать на каждый hash_combine (есть хеширующие функции, с дорогим финальным шагом, не надо этот шаг повторять на кажый hash_combine)
Для этого я и расписал Alternative HashFunc
, представляющую собой короткоживущий stateful объект. У него есть метод result
, возвращающий значение хеш-функции.
добавить хеширование изкоробки для pair и tuple
Да, безусловно. Просто я решил, что их имеет смысл делать в рамках reference implementation, чуть позже.
Hasher в текущем описании приведёт к бесконечной рекурсии
Спасибо за замечание, проверю. Я писал этот код как скетч, из головы. По хорошему, нужно делать реализацию в виде форка stdlib CLang'а, и уже её основательно гонять.
HashFunc должен быть объектом, при этом у него может быть огромный стейт
В общем, вы склоняетесь к альтернативному варианту.
Пара вопросов:
По ссылкам обнаруживается масса пропозалов на эту тему. Общий список примерно следующий:
hash_combine
HashFunc
на std::hash_code
и std::hash_combine
.hash_value
с поиском через ADL, hash_combine
для комбинирования значений; один алгоритм хеширования.hash_combine
Самый интересный вопрос - почему ни один из них так и не приняли.
@apolukhin Обновил proposal. Пока не осветил сравнение с предыдущими предложениями.
Proposal состоит как бы из двух частей:
С частью 1. всё неплохо. Надо ли сделать чтобы хешеры могли возвращать разные типы результата (например std::unit32_t, чтобы хеш можно было сохранять и передавать по сети/диску между разноразрядными платформами). Продумать интеграцию со стандартными контейнерами - как сразу начать использовать новые хеши? Расставить noexcept (или расписать, почему не расставили) для concept HashFunc
Часть 2. меня пугает. Кажется что это более общий механизм, который можно применять не только для хеширования, но и для сериализации/десериализации и т.п. Нужно либо расписать, почему это плохая идея, использовать этот механизм для других вещей, либо расписать его использование для других сценариев.
В любом случае, кажется часть 2 надо вынессти отдельно - по ней будет очень много вопросов и споров в комитете, в частности, как это соотносится с предложенной рефлексией.
С частью 1. всё неплохо.
Спасибо, стараюсь по мере возможности.
Надо ли сделать чтобы хешеры могли возвращать разные типы результата (например std::unit32_t, чтобы хеш можно было сохранять и передавать по сети/диску между разноразрядными платформами).
По сути, пропозал это и предлагает. HashFunc::result()
может возвращать произвольный тип. Он фиксирован как std::size_t
только для стандартных хешеров.
Продумать интеграцию со стандартными контейнерами - как сразу начать использовать новые хеши?
Этот момент описан в отдельном разделе. Я так понимаю, недостаточно внятно. Чего может не хватать:
std::hash
с enabledness по наличию новых функций хешированияЧто-то ещё?
Расставить noexcept (или расписать, почему не расставили) для concept HashFunc
Бесспорно, это надо сделать. Но, как мне кажется, уже после "устаканивания" первичной идеи.
Часть 2. меня пугает. Кажется что это более общий механизм, который можно применять не только для хеширования, но и для сериализации/десериализации и т.п. Нужно либо расписать, почему это плохая идея, использовать этот механизм для других вещей, либо расписать его использование для других сценариев.
В любом случае, кажется часть 2 надо вынессти отдельно - по ней будет очень много вопросов и споров в комитете, в частности, как это соотносится с предложенной рефлексией.
Вот здесь не совсем понимаю. В тексте нигде не упоминаются какие-либо варианты автогенерации hash_value
. Пропозал, по сути, нейтрален относительно подобной рефлексии. Всё, что предлагается - сделать максимально эргономичную композицию для реализации пользовательского кода. Хешер ничего не генерирует, он просто выбирает из пачки доступных реализаций, используя ADL. Если же вы говорите о коде вида hasher(value.first)
, то это просто более эргономичный вариант через перегрузку оператора (). Похожий подход применяется в cereal. К примеру, в пропозале P0029 для этого требуется явно использовать hash_combine
.
Или я вас неправильно понял. Тогда укажите пожалуйста на "2ю часть", о которой вы говорите.
Вы фактически предлагаете концепт HashFunc, который отвечает за то как хешировать, и hash_value
говорящий что хешировать:
struct foo {
void hash_value(auto& hasher) {
hasher(a);
hasher(b);
}
int a;
int b;
};
auto kHash = sha512hasher(foo{4, 2});
Люди в комитете зададут вопросы:
hash_value
- это ведь функция, применяющая функтор к каждому из полей класса. Можно ли обобщить hash_value
для других целей? Например для сериализации/дессериализации? Именно поэтому, вторая часть меня и беспокоит - непонятно куда заведёт обсуждение, и если обсуждение зайдёт в страшные дебри, то всё предложение будет под вопросом/затянется.
Спасибо, теперь вопрос понятен.
Короткий ответ:
Код местами похож на интроспекцию, но не требует её никоим образом. Задача - улучшить хеширование, не сделать его супер-универсальным. Однако сам подход вполне совместим с будущей рефлексией и может быть дополнен ею позднее.
Ответ по пунктам:
hash_value
- это ведь функция, применяющая функтор к каждому из полей класса. Можно ли обобщитьhash_value
для других целей? Например для сериализации/дессериализации?
В двух словах - нет. Сериализуемые данные в общем случае не равны хешируемым данным. Сам подход можно применить аналогичным образом. Сейчас в качестве примера можно смотреть ту же библиотеку cereal, упомянутую мной ранее. Но в любом случае это будет отдельный "микро-фреймворк".
не хочется писать hash_value руками для агрегатов, почему бы компилятору не генерировать эту функцию за нас?
Можно и генерировать, но этот пропозал намеренно не затрагивает вопрос автоматической генерации и полностью ортогонален ему. Ничто не мешает реализовать генерацию потом, или отдельным пропозалом, или через рефлексию (когда и если она будет).
в предложении делается интроспекция типа, как предложение соотносится с разрабатываемой рефлексией?
Вынужден не согласиться. То, что код похож на интроспекцию типа, не означает, что там эта интроспекция есть. В этом плане любая реализация hash_value
использует не больше интроспекции, чем существующие сейчас специализации std::hash
.
По замечаниям:
std::size_t
, явно указана в этом разделе - второй абзац и первый элемент в списке "Properties of proposed design".@target-san ещё немного комментов:
using std::hash_value;
вы намекаете на стандартные хешеры, но больше в proposal их нет. Надо сделать целостноstatic_assert(false
- надо сделать false
зависимым от T
, иначе assert всегда будет срабатыватьЧуть не забыл:
Перенос предложения: голоса +6, -0 Автор идеи: Igor Baidiuk
Разделить процесс хеширования на "провайдер данных" и собственно "хешер", попутно позволив реализовывать "провайдер данных" для типов независимо от std::hash
Небольшое введение. На данный момент, если пользователь желает реализовать функцию хеширования для своего типа данных, у него есть два пути
Итак, собственно proposal sketch
Типы, желающие быть хешированными, добавляют либо метод
либо свободную функцию
Метод при выборе имеет приоритет. Свободная функция позволяет реализовать хеш для типов, для которых автор забыл это сделать. Назначение - передать набор значимых для хеша конкретного экземпляра байт в экземпляр хешера, при этом не принимая никаких решений относительно того, как эти байты будут хешироваться и комбинироваться. Пример реализации:
Вторым компонентом этой системы является непосредственно хешер (ниже пример примитивного хешера)
Где hasher_traits являются аналогом allocator_traits и реализуют поддержку хеширования значений конкретных типов, приводя их к единственной реализации хеширования произвольного набора байт.
Преимущества:
Как всё это встроить в текущую модель. Стандартом предполагается наличие "включенных" и "выключенных" реализаций std::hash. При этом большинство реализаций выключены по умолчанию с помощью бланкет-реализации для любого типа T. Эту бланкет-реализацию можно расширить поддержкой указанных систем хеширования. Таким образом, std::hash по умолчанию приобретает примерно следующий вид