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

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

Изменено 13.06.2019 11:56 rg45

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

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



RNS>
RNS>template<class T>
RNS>class DataSet
RNS>{
RNS>public:
RNS>    DataSet(const std::string& colname) {
RNS>        m_name = colname;
RNS>    }
RNS>    std::vector<T> getValues() {
RNS>        return m_values;
RNS>    }
RNS>    void addValue(const T& val) {
RNS>        m_values.push_back(val);
RNS>    }
RNS>    void write(const std::vector<T>& values) {
RNS>        // write out the vector
RNS>    }
RNS>private:
RNS>    std::string m_name;
RNS>    std::vector<T> m_values;
RNS>};
RNS>


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


RNS>
RNS>    std::vector<std::string> column_names = {"A", "B", "C"};
    
RNS>    std::vector<int> intValues;
RNS>    std::vector<float> floatValues;
RNS>    std::vector<double> doubleValues;
    
RNS>    DataSet<int> intDataset("A");
RNS>    DataSet<float> floatDataset("B");
RNS>    DataSet<double> doubleDataset("C");
    
RNS>    /*add elements to intValues, floatValues, doubleValues*/
    
RNS>    intDataset.write(intValues);
RNS>    floatDataset.write(floatValues);
RNS>    doubleDataset.write(doubleValues);
RNS>


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>...);
}


Теперь заполнить необходимые 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