Michael (mstone) wrote,
Michael
mstone

  • Music:

[C++, boost] Concept-based overloading in C++

Год назад, прочитав в C++ Template Metaprogramming про возможность явно управлять включением/невключением функции в overload set при помощи boost::enable_if, мысленно отнёс этот трюк к категории вещей типа «а вот гляди, братва, как я могу» — прикольных, но в реальной жизни не нужных. Оказалось, нужных, и ещё как.

Это нужно всегда, когда мы упираемся в отсутствие в C++ concept-based overloading. Хочется надеяться, что когда-нибудь оно-таки появится, но на данный момент язык не предоставляет готовых средств перегрузить функцию так, чтобы она вызывалась только для типов, удовлетворяющих некоему (произвольно сложному) заданному условию.

Я в это упёрся вчера при прикручивании к старому приложению новой функциональности. У приложения есть помойк свалк сложносочинённая структура данных глубокой вложенности и разнообразия, и нужно её сериализовать в текстовый файл в виде набора CSV-таблиц. Задача простая, но есть нюанс. Файл должен быть удобен для восприятия и редактирования человеком, поэтому к сериализации каждой структуры нужно подходить индивидуально. Как, например, выглядит в общем случае сериализация map-ы? Итерируем по всем элементам и рекурсивно сериализуем ключ, а потом значение. И то, и другое, в свою очередь, может быть произвольно сложной структурой, а значит каждый ключ и каждое значение становятся на выходе набором таблиц, в лучшем случае — одной таблицей. А если мапа простая, типа map<short,long>? Общий алгоритм превратит её в длинную серию n*2 таблиц из одного элемента каждая, хотя тут явно просится одна-единственная двухстолбцовая таблица из n строк. Значит, нам нужно перегрузить функцию сериализации для тех мапов, у которых и ключ, и значение являются числами.

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

А вот так выглядит решение с boost::enable_if:

template <typename K, typename V> typename enable_if<and_<is_arithmetic<K>,is_arithmetic<V> > >::type SerializeAsCsv( ostream &os, const map<K,V>& m ) { // Customized serialization }
Синтакс, конечно, жутковат, как и у всего темплейтного в C++, но зато исходную идею о том, в каких случаях должна быть применена данная специализация, получилось донести до компилятора в первозданном, естественном и читабельном (ну, понятно, в пределах читабельности темплейтного кода на C++ :) виде. А что может быть лучшей характеристикой качества кода?
Tags: boost, c++
Subscribe
  • Post a new comment

    Error

    default userpic
  • 3 comments