TGStat
TGStat
Введите текст для поиска
Расширенный поиск каналов
  • flag Russian
    Язык сайта
    flag Russian flag English flag Uzbek
  • Вход на сайт
  • Каталог
    Каталог каналов и чатов Региональные подборки Тематические подборки Платные каналы Поиск каналов
    Добавить канал/чат
  • Рейтинги
    Рейтинг каналов Рейтинг чатов Рейтинг публикаций
    Рейтинги брендов и персон
  • Аналитика
  • Поиск по публикациям
  • Мониторинг Telegram
  • Продвижение
    Реклама через Яндекс Бизнес Реклама в каналах через TGStat Agency Реклама на сайте TGStat.ru
Грокаем C++

10 Aug, 13:00

Открыть в Telegram Поделиться Пожаловаться

Мапим значения на типы. Ч2
#опытным

Напомню проблему:

enum class DataType: uint8_t {
kInt32,
kDouble,
kUint64
};

template
T create_from_buffer(const void* buffer) {
static_assert(std::is_trivially_copyable_v,
"Type T must be trivially copyable");

T obj;
std::memcpy(&obj, buffer, sizeof(T));
return obj;
}

Как на основе DataType создать объект соответствующего типа?

Compile-time мапа из массива, где по индексу подкапотного числа, стоящего за каждым перечислителем, позволяет получить соответствующий нужный тип и выбрать правильный обработчик. И все это за О(1) с тратами на сдвиг элемента в массиве

Если же перечислителям явно присвоены числа, то этот номер не прокатит и надо думать дальше.

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

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

Сделаем структуру аля ноду маппинга:

template
struct Node { static constexpr DataType key = D; using type = T; };

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

И в качестве пака шаблонных параметров нашей шаблонной функции мы будем передавать список нод Node, Node, Node.

В функции мы должны сделать примерно следующее. Перебираем рантайм значение enum'а на соответствие значению Key из нод пака параметров. Перебираем, пока не найдем нужный и после используем правильную ноду для получения замаппленого типа.

И вот этот перебор можно делать через fold expression и короткозамкнутый оператор ||. Как только условие истинно, вычисления прекращаются.

template
DataVariant ConvertImpl(const void* buffer, DataType type) {
DataVariant result;
bool found = ((Es::key == type
? (result = create_from_buffer(buffer), true)
: false) || ...);
if (!found) throw std::runtime_error("Unknown type");
return result;
}

DataVariant Convert(const void* buffer, DataType type) {
return ConvertImpl<
Node,
Node,
Node
>(buffer, type);
}

Также тут используется фишка оператора запятой, что результатом выражения является только самый крайний справа операнд.

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

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

Код кстати по размеру уже не сильно больше изначального свитча.

Вот примерчик с рабочим кодом, можете поиграться.

Use tricks. Stay cool.

#template #cpp17

2.8k 0 18 9 27
Каталог
Каталог каналов и чатов Подборки каналов Поиск каналов Добавить канал/чат
Рейтинги
Рейтинг каналов Telegram Рейтинг чатов Telegram Рейтинг публикаций Рейтинги брендов и персон
API
API статистики API поиска публикаций API Callback
Наши каналы
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Почитать
Академия TGStat Исследование Telegram 2019 Исследование Telegram 2021 Исследование Telegram 2023
Контакты
Справочный центр Поддержка Почта Вакансии
Всякая всячина
Пользовательское соглашение Политика конфиденциальности Публичная оферта
Наши боты
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot