Reputation: 510
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
<type_code>
: 0=int
, 1=double
, 2=char
, 3=std::string
;<n>
is the number of elements in the list;For example, the following is a list of 5 std::string
s:
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
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
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
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