Я хочу использовать шаблон для преобразования между различными типами образцов
template<class T,class U>
void convert(const T* source, U* dest, size_t n)
{
do
{
double G=double(max(*dest))/max(*source);
T diff=max(*source) - min(*source);
*dest=U(makeUnsigned(*source - min(*source))*G/makeUnsigned(diff)
+makeUnsigned(max(*source) - *source)*double(min(*dest))/makeUnsigned(diff));
++dest;
++source;
--n;
}
while(n!=0);
}
Теперь я хочу автоматически сгенерированную матрицу, чтобы я мог
convert[from][to](source,dest,n);
где каждый элемент относится к правильной версии. Я знаю, что здесь я должен приводить указатели на функции (каждый элемент должен быть указателем на функцию, принимающую const void *, void * size_t, который в любом случае эквивалентен).
Могу ли я сделать это?
Сначала напишите это:
template<typename T, typename U>
U convert( T const& src );
потому что все остальное должно быть написано с помощью шаблонного метапрограммирования. Хорошая вещь об этом — вы можете специализировать свои convert<int, double>
и ваш convert<std::string, int>
Если вам нужно.
Далее напишите это:
template<typename T, typename U>
void convert_buffer( T const* src, U* dest, size_t n );
который просто звонит convert
выше в цикле. Я разделил это на две функции, потому что возможно неоднородный код входит в convert
и вызов функции с видимым телом имеет практически нулевые накладные расходы.
А затем напишите это:
typedef void(*blind_converter)(void const*, void*, size_t);
template<typename T, typename U>
void convert_blind_buffer( void const* src, void* dest, size_t n ) {
return convert_buffer( reinterpret_cast<T const*>(src), reinterpret_cast<U*>(dest), n );
}
который инкапсулирует приведение и не требует от вас выполнения теоретически неверного приведения типа указателя. Это «конвертировать слепой» буфер — слепой, в том смысле, что void*
s.
Далее, мы не хотим поддерживать ваш массив NxN вручную. Тип для хранения вашего упорядоченного списка типов:
template<typename... Ts>
struct type_list {};
А затем мы пишем метапрограммирование для построения массива NxN:
template<typename Src, typename DestList>
struct make_convert_one_way;
template<typename Src, typename... Ds>
struct make_convert_one_way< Src, type_list<Ds...> > {
std::array< blind_converter, sizeof...(Ds) > operator()() const {
return { convert_blind_buffer< Src, Ds >... };
}
};
template<typename list>
struct make_convert_array;
template<typename... Ts>
struct make_convert_array< type_list<Ts...> > {
std::array< std::array<blind_converter, sizeof...(Ts) >, sizeof...(Ts) > operator()() const {
return { make_convert_one_way< Ts, type_list<Ts...> >... };
}
};
typedef type_list< int, char, double > my_list;
auto convert_array = make_convert_array<my_list>()();
Или что-то вдоль этих линий.
Если ваши типы source и dest не одинаковы, вышеприведенный код необходимо изменить, чтобы он занял два type_list
с, но в этом нет ничего принципиально сложного.
Следующей полезной вещью будет возможность сопоставления типа с индексом в вышеуказанном массиве, потому что поддержание этого должно быть задачей компилятора.
template<typename T, typename List, typename=void>
struct index_of;
template<typename T, typename T0, typename... Ts>
struct index_of<T, type_list<T0, Ts...>, typename std::enable_if<
std::is_same<T, T0>::value
>::type >: std::integral_constant< std::size_t, 0 > {};
template<typename T, typename T0, typename... Ts>
struct index_of<T, type_list<T0, Ts...>, typename std::enable_if<
!std::is_same<T, T0>::value
>::type >: std::integral_constant< std::size_t, index_of<T, type_list<Ts...>::value+1 > {};
что позволяет вам сделать это:
static_assert( index_of< int, my_list >::value == 0, "all is well!" );
Теперь вы можете обернуть это в какой-то тип одежды. Стратегия может выглядеть так:
template<typename List>
struct EnumDressing;
template<typename... Ts>
struct EnumDressing<type_list<Ts...>> {
enum type {
e_begin = 0,
e_end = sizeof...(Ts),
};
template<typename T>
static constexpr type value() {
return static_cast<type>( index_of<T, type_list<Ts...> >::value );
}
};
где у нас есть EnumDressing<my_list>::type
как тип enum
который представляет целочисленные имена для ваших типов, и значения этого можно получить через EnumDressing<my_list>::value<int>()
, Естественно вы убираете это с typedef
s:
typedef EnumDressing<my_list> Types;
typedef Types::type eType;
struct typed_array {
eType type;
void* buff;
size_t n;
};
void do_convert( typed_array src, typed_array dst) {
Assert(src.n == dst.n);
convert_array[ src.type ][ dst.type ]( src.buff, dst.buff, std::min( src.n, dst.n ) );
}
template<typename T, size_t N>
typed_array make_typed_array( T (&arr)[N] ) {
return { Types::value<T>(), reinterpret_cast<void*>( &arr[0] ), N };
}
int main() {
double d[100];
int i[100];
do_convert( make_typed_array( d ), make_typed_array( i ) );
}
с естественно актуальными вариантами использования, отделяющими создание typed_array
от его использования.
Других решений пока нет …