2016-05-04 3 views
4

Я разрабатываю библиотеку C++, которая считывает CSV-файл с данными из некоторого эксперимента и выполняет некоторую агрегацию и выводит код pgfplots. Я хочу сделать библиотеку максимально простой и простой в использовании. Я также хочу выделить его из типов данных, которые представлены в файле CSV, и оставить возможность пользователю анализировать каждый столбец по своему желанию. Я также хочу избежать Boost Spirit Qi или другого сильного сильного анализатора.Как построить кортеж из массива

Простым решением для пользователя является создание типа для каждого столбца с конструктором, который принимает «char *». Конструктор выполняет собственный синтаксический анализ для заданного значения, которое является одной ячейкой данных. Затем пользователь передает мне список типов; схема, представляющая типы в строке данных. Я использую этот список типов для создания кортежа, в котором каждый член кортежа отвечает за синтаксический анализ.

Проблема заключается в том, как инициализировать (построить) этот кортеж. Разумеется, обращение с кортежами не так просто, поскольку итерация по их элементам в основном выполняется во время компиляции. Сначала я использовал Boost Fusion для достижения этой задачи. Однако функция, которую я использовал (преобразование), хотя может принимать кортеж как вход (с соответствующим адаптером), похоже, что он не возвращает кортеж. Мне нужно, чтобы возвращаемое значение являлось кортежем, поэтому какой-то другой код может использовать его как ассоциативный контейнер типа-к-значению (доступ к нему по типу через std::get<T>) при использовании только стандартных инструментов, то есть без использования Boost. Поэтому мне пришлось преобразовать все преобразования Fusion в std :: tuple.

Мой вопрос заключается в том, как избежать этого преобразования, и еще лучше, как избежать Boost Fusion полностью.

Простым решением, которое приходит на ум, является использование конструктора std :: tuple и каким-то образом передать каждому элементу его соответствующую «const *», которую он должен построить. Однако, хотя это возможно, используя некоторые сложные методы перечисления на основе шаблонов, мне интересно, существует ли простой подход с параметром-пакетом или даже более простой способ передать значения конструкторам отдельных элементов кортеж.

Чтобы уточнить, что я ищу, любезно ознакомьтесь с этим следующим кодом.

#include <cstdio> 
    #include <array> 
    template <typename...> struct format {}; 
    template <typename...> struct file_loader {}; 
    template <typename... Format> 
    struct 
    file_loader<format<Format...> > { 
     void load_file() { 
      size_t strsize = 500u; 
      char *str = new char[strsize](); 

      auto is = fopen("RESULT","r"); 
      /* example of RESULT: 
       dataset2,0.1004,524288 
       dataset1,0.3253,4194304 
      */ 
      while(getline(&str, &strsize, is) >= 0) { 
       std::array<char*, 3> toks{}; 
       auto s = str; 
       int i = 2; 
       while(i --> 0) 
        toks[i] = strsep (&s, ","); 
       toks[2] = strsep (&s, ",\n"); 

       std::tuple<Format...> the_line{ /* toks */ } ; // <-- HERE 
       //// current solution: 
       // auto the_line{ 
       // as_std_tuple(// <-- unnecessary conversion I'd like to avoid 
       // boost::fusion::transform(boost::fusion::zip(types, toks), boost::fusion::make_fused(CAST())) 
       // )}; 


       // do something with the_line 
      } 
     } 
    }; 

    #include <string> 
    class double_type { 
    public: 
     double_type() {} 
     double_type(char const *token) { } // strtod 
    }; 
    class int_type { 
    public: 
     int_type() {} 
     int_type(char const *token) { } // strtoul 
    }; 

    int main(int argc, char *argv[]) { 
     file_loader< format< std::string, 
          double_type, 
          int_type > > 
     {}.load_file(); 


     return 0; 
    } 

Я выделил интересную строку как «ЗДЕСЬ» в комментарии.

Мой вопрос именно это:

Есть ли способ построить экземпляр станд :: кортеж (разнородных типов, каждый из которых неявно преобразованы из «полукокса *») с автоматической продолжительности хранения (в стеке) от std::array<char *, N>, где N равно размеру этого кортежа?

Ответ Я ищу должен

  1. Избегайте подталкивания Fusion
  2. (Простота условие) Избегайте использования более чем 5 строк шаблонного шаблонов на основе кода перечисления
  3. С другой стороны, показывает, почему это невозможно сделать в стандарте C++ 14

Ответ может использовать конструкторы C++ 17, я бы не прочь.

Спасибо,

+0

Было бы хорошо знать, может ли преобразование Fusion напрямую возвращать std :: tuple. –

+2

'while (i -> 0)' Пожалуйста, не делайте этого. Вы будете пугать детей. :) – erip

+0

@erip Я на самом деле не использую это. В моем реальном коде я использую boost irange. Я просто не хотел приносить все нестандартные вещи и включает в себя мой код здесь. Разве вы не думаете, что иранге будет страшнее? –

ответ

8

Как и во всех вопросах, связанных std::tuple, используйте index_sequence, чтобы дать вам параметр обновления индексировать массив:

template <class... Formats, size_t N, size_t... Is> 
std::tuple<Formats...> as_tuple(std::array<char*, N> const& arr, 
           std::index_sequence<Is...>) 
{ 
    return std::make_tuple(Formats{arr[Is]}...); 
} 

template <class... Formats, size_t N, 
      class = std::enable_if_t<(N == sizeof...(Formats))>> 
std::tuple<Formats...> as_tuple(std::array<char*, N> const& arr) 
{ 
    return as_tuple<Formats...>(arr, std::make_index_sequence<N>{}); 
} 

Что вы будете использовать, как:

std::tuple<Format...> the_line = as_tuple<Format...>(toks); 
+0

Ты избил меня! +1 – erip

+0

Очень хорошо. +1 для проверки работоспособности enable_if :)! –

+0

Вместо этого я бы вместо этого использовал 'static_assert'. Но +1. –

Смежные вопросы