Вопрос по метапрограммированию
От: RiNSpy  
Дата: 11.06.19 20:11
Оценка:
Вообщем, проблема такая. Есть некое API, которое пишет куда-то вектора данных разных типов, которые я ему даю. Что-то вроде такого:


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


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

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


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

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

Как мне это элегантнее сделать?
Re: Вопрос по метапрограммированию
От: reversecode google
Дата: 11.06.19 20:26
Оценка:
забыли указать версию плюсов на которой нужно
ну возьмите std::any если 17 можно
или из буста если версия старее
Re: Вопрос по метапрограммированию
От: kov_serg Россия  
Дата: 12.06.19 00:36
Оценка: 4 (1)
Здравствуйте, RiNSpy, Вы писали:

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

RNS>Хотелось бы что-то такое сделать, чтобы я мог где-то в одном месте определить их имена и типы, а потом создать вектора, DataSet'ы, вызвать write(...) в цикле. То есть если имена и типы поменяются, чтобы мне не надо было их менять вручную в 3х местах, и если у меня этих векторов 100 штук, чтобы не плодить 300 строк кода.
RNS>Как мне это элегантнее сделать?
Например так:
struct Writer {
    typedef std::function<void()> fn;
    std::vector<fn> list;
    void add(fn t) { list.push_back(t); }
    void write() { for(auto x : list) x(); }
};
#define DECL_DATASET_FOR(var,ds,name) \
    DataSet<decltype(var)::value_type> ds(name); \
    column_names.push_back(name); \
    writer.add([&]{ ds.write(var); });

...
    std::vector<int> intValues;
    std::vector<float> floatValues;
    std::vector<double> doubleValues;
...
    Writer writer; 
    std::vector<std::string> column_names;
    DECL_DATASET_FOR(intValues,intDataset,"A")
    DECL_DATASET_FOR(floatValues,floatDataset,"B")
    DECL_DATASET_FOR(doubleValues,doubleDataset,"C")
    // еще 297 шт
    
    /*add elements to intValues, floatValues, doubleValues*/
    
    writer.write();
Re: Вопрос по метапрограммированию
От: B0FEE664  
Дата: 13.06.19 11:29
Оценка:
Здравствуйте, RiNSpy, Вы писали:

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


Имена уникальны?
И каждый день — без права на ошибку...
Re: Вопрос по метапрограммированию
От: rg45 СССР  
Дата: 13.06.19 11:55
Оценка:
Здравствуйте, 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; \
  };

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


Собственно это и есть ядро метаданных. Для доступа к ним пишется унифицированный набор шаблонных аксессоров:


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>>;


Теперь получить тип 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>);


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

Теперь можно предоставить дополнительные утилиты для работы с этими глобальными переменными:

// 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

P.S. В этом примере не будет работать автоматическое выведение типов для шаблонной функции write_values. По понятным причинам. Это можно победить, при необходимости, но сейчас я этого не делаю, чтобы не усложнять пример.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 13.06.2019 12:41 rg45 . Предыдущая версия . Еще …
Отредактировано 13.06.2019 12:35 rg45 . Предыдущая версия .
Отредактировано 13.06.2019 12:33 rg45 . Предыдущая версия .
Отредактировано 13.06.2019 12:32 rg45 . Предыдущая версия .
Отредактировано 13.06.2019 12:23 rg45 . Предыдущая версия .
Отредактировано 13.06.2019 12:16 rg45 . Предыдущая версия .
Отредактировано 13.06.2019 12:11 rg45 . Предыдущая версия .
Отредактировано 13.06.2019 12:09 rg45 . Предыдущая версия .
Отредактировано 13.06.2019 12:06 rg45 . Предыдущая версия .
Отредактировано 13.06.2019 12:00 rg45 . Предыдущая версия .
Отредактировано 13.06.2019 11:58 rg45 . Предыдущая версия .
Отредактировано 13.06.2019 11:56 rg45 . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.