Информация об изменениях

Сообщение Re: Вопрос по метапрограммированию от 13.06.2019 11:55

Изменено 13.06.2019 12:06 rg45

Re: Вопрос по метапрограммированию
Здравствуйте, RiNSpy, Вы писали:

RNS>Вообщем, проблема такая. Есть некое API, которое пишет куда-то вектора данных разных типов, которые я ему даю. Что-то вроде такого:


RNS>Мне надо обработать тонны подобных векторов и записать их методом Dataset::write. Для примера:


RNS>Проблема в том, что у меня этих веторов очень много, они разных типов. Их количество, имена и типы иногда меняются в спецификации, до этапа компилирования, и мне приходится их вручную менять в коде. Код получается очень некрасивый.


RNS>Хотелось бы что-то такое сделать, чтобы я мог где-то в одном месте определить их имена и типы, а потом создать вектора, DataSet'ы, вызвать write(...) в цикле. То есть если имена и типы поменяются, чтобы мне не надо было их менять вручную в 3х местах, и если у меня этих векторов 100 штук, чтобы не плодить 300 строк кода.


RNS>Как мне это элегантнее сделать?




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

https://ideone.com/lZjCQK

Связка имен с типами делается при помощи введения мнемонических имен, совпадающих с именами колонок (но только без кавычек). Эти имена используются как параметры шаблонов, служаших для доступа к типам, функциям и переменным:

template <typename> struct ColumnTraits;

#define DEFINE_COLUMN(name, type)   class name;   template<>   struct ColumnTraits<name>   {     using ColumnType = type;     static constexpr const char* column_name = #name;   };

template <typename Column>
constexpr const char* column_name = ColumnTraits<Column>::column_name;

template <typename Column>
using ColumnType = typename ColumnTraits<Column>::ColumnType;

template <typename Column>
using VectorType = std::vector<ColumnType<Column>>;

template <typename Column>
using DataSetType = DataSet<ColumnType<Column>>;

DEFINE_COLUMN(A, int)
DEFINE_COLUMN(B, float)
DEFINE_COLUMN(C, double)
DEFINE_COLUMN(D, int)
DEFINE_COLUMN(E, float)
DEFINE_COLUMN(F, double)


Теперь получить тип DataSet, соответствующий колонке "B", можно при помощи простой конструкции DataSetType<B>. Тип соответствующего вектора — VectorType<B>. Имя колонки — column_name<B>.

Помимо шаблонов типов можно так же определить шаблоны глобальных переменных, если они нужны. И предоставить дополнительные утилиты для работы с ними:

// You can even define templates for flobal variables, if it's necessary:

template <typename Column>
VectorType<Column> global_values;

template <typename Column>
DataSetType<Column> global_dataset(column_name<Column>);

// Yoy can provide utility for global writhing by specified types:
template <typename...Columns>
void write_values(const VectorType<Columns>&...values)
{
  std::initializer_list<int>{ (global_dataset<Columns>.write(values), 0)... };
}

template <typename...Columns>
void write_global_values()
{
  write_values<Columns...>(global_values<Columns>...);
}


Теперь заполнить необходимые dataset-ы, все или выборочно, можно простыми выражениями:

   global_dataset<D>.write({1, 2, 3, 4, 5});

   global_dataset<B>.write(global_values<B>);
    
   write_values<A, C>({1, 2}, {3.14, 0.1});
   
   write_global_values<F, A, C, E>();


Я нигде не использовал column_names потому, что не понял, зачем они нужны и какой у них скоуп. Их формирование без труда можно впихнуть внутрь макроса DEFINE_COLUMN, если хочется.

Полный текст примера здесь: https://ideone.com/lZjCQK
Re: Вопрос по метапрограммированию
Здравствуйте, RiNSpy, Вы писали:

RNS>Вообщем, проблема такая. Есть некое API, которое пишет куда-то вектора данных разных типов, которые я ему даю. Что-то вроде такого:


RNS>Мне надо обработать тонны подобных векторов и записать их методом Dataset::write. Для примера:


RNS>Проблема в том, что у меня этих веторов очень много, они разных типов. Их количество, имена и типы иногда меняются в спецификации, до этапа компилирования, и мне приходится их вручную менять в коде. Код получается очень некрасивый.


RNS>Хотелось бы что-то такое сделать, чтобы я мог где-то в одном месте определить их имена и типы, а потом создать вектора, DataSet'ы, вызвать write(...) в цикле. То есть если имена и типы поменяются, чтобы мне не надо было их менять вручную в 3х местах, и если у меня этих векторов 100 штук, чтобы не плодить 300 строк кода.


RNS>Как мне это элегантнее сделать?




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

https://ideone.com/lZjCQK

Связка имен с типами делается при помощи введения мнемонических имен, совпадающих с именами колонок (но только без кавычек). Эти имена используются как параметры шаблонов, служаших для доступа к типам, функциям и переменным:

template <typename> struct ColumnTraits;

#define DEFINE_COLUMN(name, type)   class name;   template<>   struct ColumnTraits<name>   {     using ColumnType = type;     static constexpr const char* column_name = #name;   };

template <typename Column>
constexpr const char* column_name = ColumnTraits<Column>::column_name;

template <typename Column>
using ColumnType = typename ColumnTraits<Column>::ColumnType;

template <typename Column>
using VectorType = std::vector<ColumnType<Column>>;

template <typename Column>
using DataSetType = DataSet<ColumnType<Column>>;

DEFINE_COLUMN(A, int)
DEFINE_COLUMN(B, float)
DEFINE_COLUMN(C, double)
DEFINE_COLUMN(D, int)
DEFINE_COLUMN(E, float)
DEFINE_COLUMN(F, double)


Теперь получить тип DataSet, соответствующий колонке "B", можно при помощи простой конструкции DataSetType<B>. Тип соответствующего вектора — VectorType<B>. Имя колонки — column_name<B>.

Помимо шаблонов типов можно так же определить шаблоны глобальных переменных, если они нужны. И предоставить дополнительные утилиты для работы с ними:

// You can even define templates for flobal variables, if it's necessary:

template <typename Column>
VectorType<Column> global_values;

template <typename Column>
DataSetType<Column> global_dataset(column_name<Column>);

// Yoy can provide utility for global writhing by specified types:
template <typename...Columns>
void write_values(const VectorType<Columns>&...values)
{
  std::initializer_list<int>{ (global_dataset<Columns>.write(values), 0)... };
}

template <typename...Columns>
void write_global_values()
{
  write_values<Columns...>(global_values<Columns>...);
}


После этого уже не нужно определять индивидуально для каждой колонки: intValues, floatValues, doubleValues, intDataset, floatDataset, doubleDataset... мы все уже сделали — парой шаблоннов переменных! И доступиться к ним теперь можно при помощи простых выражений: global_dataset<A>, global_values<B>...

И заполнить необходимые dataset-ы, все или выборочно, можно тоже простыми выражениями:

   global_dataset<D>.write({1, 2, 3, 4, 5});

   global_dataset<B>.write(global_values<B>);
    
   write_values<A, C>({1, 2}, {3.14, 0.1});
   
   write_global_values<F, A, C, E>();


Я нигде не использовал column_names потому, что не понял, зачем они нужны и какой у них скоуп. Их формирование без труда можно впихнуть внутрь макроса DEFINE_COLUMN, если хочется.

Полный текст примера здесь: https://ideone.com/lZjCQK