Недавно довелось столкнутся с задачкой, в которой пользователь должен определить тип с перечнем свойств. Что-то вроде traits из нашего проекта RESTinio. При этом внутри такого типа обязательно должна быть константа high_watermark типа std::size_t. Например:
struct my_traits { ... // Какие-то определения типов. // Максимальный размер при достижении которого нужно провести чистку данных. static constexpr std::size_t high_watermark = 64 * 1024 * 1024; }; |
А кроме этого может быть определена и вторая константа, low_watermark. Т.е. класс свойств может выглядеть и так:
struct my_traits { ... // Какие-то определения типов. // Максимальный размер при достижении которого нужно провести чистку данных. static constexpr std::size_t high_watermark = 64 * 1024 * 1024; // Размер при достижении которого чистку данных следует остановить. static constexpr std::size_t low_watermark = 16 * 1024 * 1024; }; |
При использовании таких типов свойств нужно было как-то понять, есть ли в свойствах константа low_watermark. Если есть, то нужно было использовать именно ее значение. А если нет, то оставалось высчитывать нижний порог из high_watermark.
Попробовал применить для этой цели концепты, благо в проекте C++20. Получилось так, как показано под катом.
Это лучшее, что удалось придумать. К сожалению, в возможностях C++20 пока еще не сильно копенгаген, только учусь. Поэтому интересно, а можно ли было сделать лаконичнее и понятнее?
Полный код под катом, а проверить его можно, например, на Wandbox.
Upd. Под катом так же и второй вариант, более компактный за счет отсутствия одного из концептов и использования if constexpr внутри вспомогательной функции low_watermark.
Upd. Более безопасно использовать std::same_as вместо std::is_same_v.
Первый, исходный вариант:
#include <iostream> #include <type_traits> template<typename T> concept has_high_watermark = std::is_same_v<decltype(T::high_watermark), const std::size_t>; template<typename T> concept has_low_watermark = std::is_same_v<decltype(T::low_watermark), const std::size_t>; template<typename T> concept has_high_and_low_watermarks = has_high_watermark<T> && has_low_watermark<T>; struct with_high_watermark_only { static constexpr std::size_t high_watermark = 10240; }; struct with_both_watermarks { static constexpr std::size_t high_watermark = 10240; static constexpr std::size_t low_watermark = 8000; }; template<has_high_and_low_watermarks T> std::size_t low_watermark() { static_assert(T::high_watermark > T::low_watermark); return T::low_watermark; } template<has_high_watermark T> std::size_t low_watermark() { return (T::high_watermark / 3u) * 2u; } int main() { std::cout << "with_high_watermark_only: " << low_watermark<with_high_watermark_only>() << std::endl; std::cout << "with_both_watermarks: " << low_watermark<with_both_watermarks>() << std::endl; } |
Второй, более компактный.
#include <iostream> #include <type_traits> template<typename T> concept has_high_watermark = std::same_as<decltype(T::high_watermark), const std::size_t>; template<typename T> concept has_low_watermark = std::same_as<decltype(T::low_watermark), const std::size_t>; struct with_high_watermark_only { static constexpr std::size_t high_watermark = 10240; }; struct with_both_watermarks { static constexpr std::size_t high_watermark = 10240; static constexpr std::size_t low_watermark = 8000; }; template<has_high_watermark T> std::size_t low_watermark() { if constexpr(has_low_watermark<T>) { static_assert(T::high_watermark > T::low_watermark); return T::low_watermark; } else return (T::high_watermark / 3u) * 2u; } int main() { std::cout << "with_high_watermark_only: " << low_watermark<with_high_watermark_only>() << std::endl; std::cout << "with_both_watermarks: " << low_watermark<with_both_watermarks>() << std::endl; } |
Комментариев нет:
Отправить комментарий