SedriX
SedriX

Reputation: 510

Read list of different types from istream into vector

I'm trying to read text of the following format from an istream (e.g. std::cin):

<type_code>
<n>
<elem_0> <elem_1> ... <elem_n-1>

where

For example, the following is a list of 5 std::strings:

3
5
apple banana orange kiwi tomato

Intuitive I did:

template<typename T>
void Work(std::vector<T> &A) {
    // do something ...
}

int main() {
    int type_code, len;
    std::cin >> type_code >> len;
    std::vector<int> ilist;
    std::vector<double> dlist;
    std::vector<char> clist;
    std::vector<std::string> slist;

    switch (type_code) {
        case 0:
            ilist.resize(len);
            for (int i = 0; i < len; ++i) {
                std::cin >> ilist[i];
            }
            Work(ilist);
            break;
        case 1:
            dlist.resize(len);
            for (int i = 0; i < len; ++i) {
                std::cin >> dlist[i];
            }
            Work(dlist);
            break;
        case 2:
            clist.resize(len);
            for (int i = 0; i < len; ++i) {
                std::cin >> clist[i];
            }
            Work(clist);
            break;
        case 3:
            slist.resize(len);
            for (int i = 0; i < len; ++i) {
                std::cin >> slist[i];
            }
            Work(slist);
            break;
        default:
            break;
    }
}

Due to the uncertainty of type until runtime, I have to instantiate 4 vector of different type. This produce duplicate code and more objects than I need (since eventually only 1 vector will be used).

What's the best way of handling here?

Upvotes: 0

Views: 347

Answers (3)

SedriX
SedriX

Reputation: 510

(Note: Please also refer to answers by @Jake Schmidt and @anastaciu. They provide great examples of C++17 std::any and std::variant)


I figure out that putting everything in a template function fits my need just well.

C++ is statically typed (whatever that means) so you have to write that switch statement anyway and instantiate the template function 4 times with explicit type arguments (and call them). It's not like python where you can write type_arr=[int,float,str] and use type_code to index it.

btw. it's a demo for insertion sort.

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <sstream>

template<typename T>
void print_vec(std::vector<T> vec) {
    for (auto &e : vec) std::cout << e << ' ';
    std::cout << std::endl;
}

template<typename T>
void Work(std::istream &in, int len) {
    std::vector<T> vec(len);
    for (int i = 0; i < len; ++i) in >> vec[i];
    for (int i = 1; i < len; ++i) {
        auto insert_it = std::upper_bound(vec.begin(), vec.begin() + i, vec[i]);
        std::rotate(vec.rend() - i - 1, vec.rend() - i, std::make_reverse_iterator(insert_it));
        print_vec(vec);
    }
}

void read(std::istream &in) {
    int type_code, len;
    in >> type_code >> len; // omitting garbage input check
    switch (type_code) {
        case 0:
            Work<int>(in, len);
            break;
        case 1:
            Work<double>(in, len);
            break;
        case 2:
            Work<char>(in, len);
            break;
        case 3:
            Work<std::string>(in, len);
            break;
        default:
            std::cout << "err" << std::endl;
            exit(EXIT_FAILURE);
    }
}

int main() {
//    std::istringstream iss{"0\n6\n21 25 49 25 16 8"};
//    read(iss);
    read(std::cin);
}

Upvotes: 0

Jake Schmidt
Jake Schmidt

Reputation: 1699

std::variant should work nicely for this... (requires C++17)

#include <variant>
#include <vector>
#include <string>
#include <iostream>

template<typename T>
void Work(std::vector<T>& v) {
    // do more stuff
}

// this is a type that will hold either a vector<int>, vector<double>, etc, or nothing
using variantoftypes = std::variant<std::vector<int>,
                std::vector<double>,
                std::vector<char>,
                std::vector<std::string>>;

template<typename T>
void doStuff(std::istream& in, variantoftypes& v, int len) {

    //assign v to hold a vector<T>
    v = std::vector<T>();

    //get the vector it's holding out...
    std::vector<T> &v_alias = std::get<std::vector<T>>(v); 

    // now v_alias will behave like you want it to
    v_alias.resize(len);
    for (int i = 0; i < len; ++i) {
        std::cin >> v_alias[i];
    }

    // work with the alias
    Work(v_alias);

}



int main() { 
    variantoftypes v;

    int type_code, len;
    std::cin >> type_code >> len;

    switch (type_code) {
        case 0:
            doStuff<int>(std::cin, v, len);
            break;
        case 1:
            doStuff<double>(std::cin, v, len);
            break;
        case 2: 
            doStuff<char>(std::cin, v, len);
            break;
        case 3:
            doStuff<std::string>(std::cin, v, len);
            break;
    }

}

Upvotes: 2

anastaciu
anastaciu

Reputation: 23832

Since C++17 you can use std::any

std::vector<std::any> vectorName; 

Another option can be std::variant, but here you have to specify the types the vector can hold as opposed to std::any.

Upvotes: 2

Related Questions