TGStat
TGStat
Type to search
Advanced channel search
  • flag English
    Site language
    flag Russian flag English flag Uzbek
  • Sign In
  • Catalog
    Channels and groups catalog Regional compilations Thematic compilations Платные каналы Search for channels
    Add a channel/group
  • Ratings
    Rating of channels Rating of groups Posts rating
    Ratings of brands and people
  • Analytics
  • Search by posts
  • Telegram monitoring
  • Promotion
    Advertising through Yandex Business Advertising in channels through TGStat Agency Advertising on TGStat.ru website
Грокаем C++

10 Aug, 13:00

Open in Telegram Share Report

Мапим значения на типы. Ч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
Catalog
Channels and groups catalog Channels compilations Search for channels Add a channel/group
Ratings
Rating of Telegram channels Rating of Telegram groups Posts rating Ratings of brands and people
API
API statistics Search API of posts API Callback
Our channels
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Read
Академия TGStat Telegram Research 2019 Telegram Research 2021 Telegram Research 2023
Contacts
Справочный центр Support Email Jobs
Miscellaneous
Terms and conditions Privacy policy Public offer
Our bots
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot