Предисловие. Я пытаюсь немного глубже понять метапрограммирование шаблонов C++ и кажется, что я застрял... Я пишу библиотеку, которую мы будем использовать для [де]сериализации бинарных данных. Ожидаемая структура распаковываемых данных в определенной степени известна, и мне кажется разумным использовать это знание для (1) проверки данных (2) пропуска ненужных частей и (3) распаковки данных непосредственно в структуры, известные во время компиляции. - как для того, чтобы избежать ненужного копирования, так и для того, чтобы клиентский код выглядел чище.
Так, например, я хочу реализовать функцию, которая будет распаковывать массив (массивы могут содержать разнородные данные, как в JSON). Для простоты предположим, что массив имеет фиксированный размер и не имеет вложенности.
Настоящая проблема Я хочу написать функцию, которая будет принимать входной буфер, содержащий сериализованные данные (или поток — это не имеет значения в нашем контексте) и std::tuple
, содержащий lvalues для вывода (параметр pack - худшая альтернатива, потому что в конце концов мне придется иметь дело с вложенностью). Итак, мне сначала нужно проверить, все ли типы в кортеже подходят для распаковщика, и выдать соответствующее сообщение об ошибке, если нет.
Итак, код примерно такой:
template<typename T>
struct is_integral_lvalue : std::integral_constant<bool,
std::is_lvalue_reference<T>::value &&
std::is_integral<T>::value &&
(sizeof(T) == 4 || sizeof(T) == 8)>
{
};
/* ... */
template<typename TInputBuffer, typename... TDest>
static TRet unpack_int_tuple(TInputBuffer src_buf, std::tuple<TDest...> &&dest) noexcept(is_noexcept)
{
static_assert(typelist::all_are<is_integral_lvalue, TDest...>::value,
"All types in a tuple must be integral lvalue-references");
/* do unpacking */
}
Условие is_integral_constant
может быть несколько произвольным. Поэтому желательно, чтобы шаблон all_are
мог использовать любой унарный предикат. Вопрос: что мне написать в typelist::all_are
(и, может быть, что я должен исправить в приведенном выше коде, чтобы можно было написать такой all_are
)?
Конечно, идеальным был бы рабочий пример, но я буду признателен за общие идеи/советы, если они будут полезны.
Ограничения Моя цель — не только реализовать эту функцию, но и понять, как она работает (решение типа «просто используйте boost::mpl» или «boost::hana» не подходит). Чем меньше посторонних вещей мы используем, тем лучше. Предпочтительно код должен быть на C++11 (мы пока не готовы использовать C++1y/GCC 4.9 в продакшене). Я также надеюсь, что можно избежать использования макросов препроцессора.
Кое-что, что я погуглил. Boost.MPL, конечно, можно было бы использовать, но он большой, использует медленные рекурсивные шаблоны (вместо вариадика) и сложно понять, что "под капотом". Boost::hana, к сожалению, основан на полиморфных лямбда-выражениях, которые не вошли в C++11. Я видел эту библиотеку https://github.com/Manu343726/Turbo, но кажется, что она требует слишком много изменений в коде, чтобы использовать его (чтобы обернуть почти каждый тип в его адаптеры). Он также использует такие вещи, как ленивое вычисление (при расширении шаблонов) - здесь это не нужно, и код будет намного труднее читать.
Эта библиотека https://github.com/ldionne/mpl11 почти Я нуждаюсь. Проблема, опять же, с обертками: and_
реализована как частный случай метафункции foldr
(которая разворачивается для лучшей производительности во время компиляции). И все они используют поднятие метафункций, лень и т. д., что делает их очень трудными для понимания (за исключением, может быть, опытных программистов на функциональных языках). Так что в принципе мне было бы достаточно объяснить, как пропустить все эти очень обобщенные и изощренные методы и написать тот же шаблон and_
, но более простым способом (для более конкретного использования).
T
, если вы используетеT&&
в шаблоне, вы в основном позволяете входить чему угодно, это то, что Скотт Мейерс называет универсальной ссылкой. 01.12.2014std::is_lvalue_reference<T>::value
иstd::is_integral<T>::value
. Они никогда не могут быть одновременно правдой. 01.12.2014std::forward
иstd::move
01.12.2014template<bool... values> struct all_of : std::integral_constant<bool, (values && ...)> {};
; или простой шаблон функцииconstexpr
в том же духе. 01.12.2014