Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to construct a tuple from an array

I am designing a C++ library that reads a CSV file of reported data from some experiment and does some aggregation and outputs a pgfplots code. I want to make the library as generic and easy to use as possible. I also want to isolate it from the data types that are represented in the CSV file and leave the option to user to parse each column as she desires. I also want to avoid Boost Spirit Qi or other heavy duty parser.

The simple solution I have is for the user to create a type for each column, with a constructor that takes "char *". The constructor does its own parsing for the value it is given, which is one cell from the data. The user then passes me a list of types; the schema, representing the types in a line of data. I use this type list to create a tuple, in which every member of the tuple is responsible for parsing itself.

The problem now is how to initialise (construct) this tuple. Dealing with tuples is of course not straightforward since iterating over their elements is mostly a compile-time operation. I used Boost Fusion at first to achieve this task. However, the function I used (transform) although might take a tuple as input (with the appropriate adapter), it does not seem to return a tuple. I need the return value to be a tuple so some other code can use it as an associative type-to-value container (access it by type via std::get<T>), while using only standard tools, that is, without using Boost. So I had to convert whatever Fusion's transform returned into std::tuple.

My question is how to avoid this conversion, and better yet how to avoid Boost Fusion completely.

A simple solution that comes to mind is to use the constructor of std::tuple, and somehow pass each element its respective "const *" that it needs to construct. However, while this is possible using some complicated template-based enumeration techniques, I am wondering if there is a straightforward "parameter-pack"-like approach, or an even simpler way to pass the values to the constructors of the individual elements of a tuple.

To clarify what I am seeking, kindly take a look at this following code.

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

I've highlighted the interesting line as "HERE" in a comment.

My question precisely is:

Is there a way to construct a std::tuple instance (of heterogeneous types, each of which is implicitly convertible from "char *") with automatic storage duration (on the stack) from a std::array<char *, N>, where N equals the size of that tuple?

The answer I am seeking should

  1. Avoid Boost Fusion
  2. (Simplicity condition) Avoid using more than 5 lines of boilerplate template-based enumeration code
  3. Alternatively, shows why this is not possible to do in the C++14 standard

The answer can use C++17 constructs, I wouldn't mind.

Thank you,

like image 652
Mohammad Alaggan Avatar asked May 04 '16 13:05

Mohammad Alaggan


People also ask

How do you turn an array into a tuple?

If we need to convert a numpy array to tuples, we can use the tuple() function in Python. The tuple() function takes an iterable as an argument and returns a tuple consisting of the elements of the iterable.

Can you have a tuple of arrays?

Tuples are a sort of list but with a limited set of items. In JavaScript, tuples are created using arrays. In Flow you can create tuples using the [type, type, type] syntax. When you are getting a value from a tuple at a specific index, it will return the type at that index.

Is a tuple the same as an array?

Tuples have a slight performance improvement to lists and can be used as indices to dictionaries. Arrays only store values of similar data types and are better at processing many values quickly.

How do you define an array of tuples?

The Tuple type represents a JavaScript array where you can define the data type of each element in the array. a tuple is a special type of array that stores multiple fields belonging to the different data types into the same collection. It is similar to the structures in the c programming language.


Video Answer


1 Answers

As with all questions involving std::tuple, use index_sequence to give you a parameter pack to index the array with:

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

Which you would use as:

std::tuple<Format...> the_line = as_tuple<Format...>(toks);
like image 104
Barry Avatar answered Oct 24 '22 17:10

Barry