Vector That Can Have 3 Different Data Types C++

declare a c++ vector that can contain all data types defined in a namespace

You don't necessarily need to make the list right there, but you need to make the list somewhere. There's no way avoiding writing the list explicitly.

Like one of the answers in the linked question suggests, you could use a vector of std::any to store objects of any type. With the obvious drawback of losing the features of a variant type.


Regarding typedef signed short int int16;, I suggest taking a look at the standard <cstdint> header. It contains aliases for fixed width integers. There's no need to duplicate that effort (unless you need to support pre-C++11 and cannot use open source libraries).

Vector that can hold three data types using std::pair

std::vector< std::pair<std::pair<int, std::string>, std::string> > v; is possible, with

v[0].first.first = 42;
v[1].first.second = "hello";
v[2].second = "world";

std::tuple is a good alternative:

std::vector<std::tuple<int, std::string, std::string>> v = /*..*/;

std::get<0>(v[0]) = 42;
std::get<1>(v[0]) = "Hello";
std::get<2>(v[0]) = "World";

A proper structure allow to give semantic

struct Person
{
int age;
std::string firstName;
std::string lastName;
};

std::vector<Person> persons = /*...*/;

persons[0].age = 42;
persons[0].firstName = "John";
persons[0].lastName = "Doe";

Multidimensional array to store three different data types?

This is what classes are for:

struct data {
std::string name;
int name_describing_this_variable;
double another_descriptive_name;
};

You can store instances of this class in an array:

data arr[] {
{"John", 12, 40.025},
{"Amy", 20, 16000.411},
};

C++ Push Multiple Types onto Vector

In order to do that, you'll definitely need a wrapper class to somehow conceal the type information of your objects from the vector.

It's probably also good to have this class throw an exception when you try to get Type-A back when you have previously stored a Type-B into it.

Here is part of the Holder class from one of my projects. You can probably start from here.

Note: due to the use of unrestricted unions, this only works in C++11. More information about this can be found here: What are Unrestricted Unions proposed in C++11?

class Holder {
public:
enum Type {
BOOL,
INT,
STRING,
// Other types you want to store into vector.
};

template<typename T>
Holder (Type type, T val);

~Holder () {
// You want to properly destroy
// union members below that have non-trivial constructors
}

operator bool () const {
if (type_ != BOOL) {
throw SomeException();
}
return impl_.bool_;
}
// Do the same for other operators
// Or maybe use templates?

private:
union Impl {
bool bool_;
int int_;
string string_;

Impl() { new(&string_) string; }
} impl_;

Type type_;

// Other stuff.
};

Multiple Data Types with Vectors of Vectors

Conceptually Vector[long][int][double] doesn't make any sense. You can have a vector of vectors of vectors of something. There's only 1 type of something in the end.

Take a step out of dimensionality. If you're just trying to contain 3 values per element in a vector you can do that a number of ways. Make a vector of a type that contains your 3 values: your own struct probably.

Best way to read a files contents and separate different data types into separate vectors in C++

You are asking here how you should approach the given problem.

In normal software development we do several steps. First, we analyze the requirements, then think of a design and after that, we start coding. And finally, we verify and qualify the program. In reality there are more processes. But please take one important recommendation:

Before writing code. We should first clarify the “What” and then the “how”.

Last, but not least, when doing the coding, we should follow standard patterns to avoid the most common problems.

So, now let us look at your problem. You want to read a text file. The text file contains lines with comma separated values. The lines, with the comma separated values shall be split.

Then, there are always 2 lines that belong together. The first line contains index as a integer number and the question as a string, the second line consists of also an integer index and then an integer number denoting the answer.

All the data shall be read and stored for further processing.

At this point in time, we have done the requirement analysis in general.

Next is the “How”, the design, the “how we want to do things”

You were mentioning that you want to use 2 different vectors to store the questions and the answers. That approach is basically not that good.

Because the general rule is that you should store values that belong somehow together, even having different types, like int and string, in a “struct” or “class”. The same is valid for data in the first and then in the next line.

On the other hand, many data having the same type, should store in a container, like a std::array or std::vector (or others, depending of the use case).

And in your case, you would have a combination of both. So, first the data with different types in a struct and then a std::vector of these structs.

Example for the above (one of many possible solutions):

#include <iostream>
#include <vector>

struct Question {
int index{};
std::string text{};
};
struct Answer {
int index{};
int data{};
};

struct QuestionAndAnswer {
Question question{};
Answer answer{};
};

std::vector<QuestionAndAnswer> questionAndAnswer{};

OK, understood next.

We want to open a file and read it line by line. Opening a file for reading, can be done by defining a std::ifstream and then handing a filename to its constructor. This will open the file for you. And, at the end, when the variable of type std::ifstream falls out of scope, then destructor of the std::ifstream will automatically close the file for you.

You should always check the CPP Reference for any kind of questions to C++ functionalities.

As a general rule, you should always check the result of any IO-operation. This can be done with if (ifstreamVaraible). If you look in the definition of the IO-Stream functions than you can see that many of them return again a reference to the IO-Stream, with which that have been called. Example:

// Open the file
std::ifstream sourceFileStream(“test.txt”);

// Check, if it could be opened successfully
if (sourceFileStream) {

// Read the line and get the result
std::string line{};
if (std::getline(sourceFileStream, line)) {
. . .
}

How does this work? If you look in the documentation of the stream functions, then you will see that their bool and not operator ! are overwritten and return the status of a stream. For the above example if (sourceFileStream) { the compiler sees a stream variable in an if-statement, were it expects a boolean expression. It will then take the bool function of the stream and evaluates it.

Same is valid for if (std::getline(sourceFileStream, line)). This will first do the getline - operation, which will read the line. And then getline returns a reference to the stream. Then the if- statement contains quasi again if (sourceFileStream) and the bool operator will be called.

With this mechanism, you can (and should) check the result of all IO-operations.

If we want to read many lines in a loop then the rule is, to put the std::getline(sourceFileStream, line) statement into the while statements condition part. Otherwise you will always read a line to much.

You will see from not so experienced developers often something like ‘while (!sourceFileStream.eof())’ or, similar, while (sourceFileStream). This is considered to be wrong. There are many many statements her on SO, explaining that in more detail.

Next, if you want to learn C++ and use better approaches, then you should make use of object-oriented programming. The first step is, to put data and methods operating on this data in one class or struct. For your case, it would mean that the input functions should be part of the structs.

In C++ input is done via the extractor operator >>. And therefore we should add an extractor operator to your structs.

The syntax for that is (with the example of the answer struct=:

struct Answer {
int index{};
int data{};

friend std::istream& operator >> (std::istream& is, Answer& a) {
// Read an index, a comma, and the answer
char comma{};
return is >> a.index >> comma >> a.data;
}
};

This is a function for the class(or struct) “Answer”, that takes a stream and an answer as input and will return again a reference to the stream. Now, we could write:

Answer answer{};
if (!(std::cin >> answer))
std::cerr << “Problem while reading answer\n”;

The if- statement will execute the embedded extraction operation. The compiler will see a stream, the extractor operator >> and a variable of type Answer. So, it will call the function defined in the above struct. The operation will return a reference to the stream and the ! operator of the stream, will indicate, if the stream ha and issue.

A similar mechanism can be implemented for output, so we can overwrite the inserter operator >> as well.


With all that, we can split the above big problem into very small units, which can be implemented in an easy and understandable way.

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <iomanip>

struct Question {
// Data
int index{};
std::string text{};

// Extractor operator
friend std::istream& operator >> (std::istream& is, Question& q) {
char comma{};
// First read the index, then the comma, then eat up white space. A reference to is will be returned. getline
// will read the rest of the line by using is. It will also return is, which is then the function return value
return std::getline(is >> q.index >> comma >> std::ws, q.text);
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const Question& q) {
return os << "Question: Index: " << q.index << "\tText: " << q.text;
}
};
struct Answer {
// Data
int index{};
int data{};

// Extractor operator
friend std::istream& operator >> (std::istream& is, Answer& a) {
char comma{};
// Read the index, then the comma, then data. A reference to is will be returned.
return is >> a.index >> comma >> a.data;
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const Answer& a) {
return os << "Answer: Index: " << a.index << "\tData: " << a.data;
}
};
struct QuestionAndAnswer {
// Data
Question question{};
Answer answer{};

// Extractor operator
friend std::istream& operator >> (std::istream& is, QuestionAndAnswer& q) {
// Read question and answer
return is >> q.question >> q.answer;
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const QuestionAndAnswer& q) {
return os << q.question << "\t\t" << q.answer;
}
};

int main() {

// Here we will store all questions and answers
std::vector<QuestionAndAnswer> questionAndAnswer{};

// Open the source file and check, if it could be opened
std::ifstream sourceFileStream("r:\\filepath.txt");
if (sourceFileStream) {

QuestionAndAnswer temp{};

// Read all questions and answers in a loop
while (sourceFileStream >> temp)
questionAndAnswer.push_back(temp);

// Show debug output
for (const QuestionAndAnswer& qa : questionAndAnswer)
std::cout << qa << '\n';
}
else
std::cerr << "\nError: Could not open source file\n";
}

Using this approach would be one of many recommended approaches for a c++ programmer.


A source file containing

1, Question 1
2, 1
3, Question 2
4, 2
5, Question 3
6, 3
7, Question 4
8, 4
9, Question 5
10, 5

will create the output:

Question: Index: 1      Text: Question 1                Answer: Index: 2        Data: 1
Question: Index: 3 Text: Question 2 Answer: Index: 4 Data: 2
Question: Index: 5 Text: Question 3 Answer: Index: 6 Data: 3
Question: Index: 7 Text: Question 4 Answer: Index: 8 Data: 4
Question: Index: 9 Text: Question 5 Answer: Index: 10 Data: 5

If you want to stick to you original idea, then you could use:

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <iomanip>

int main() {

// Here we will store all questions and answers
std::vector<std::string> questions{};
std::vector<int> answers{};

// Open the source file and check, if it could be opened
std::ifstream sourceFileStream("r:\\filepath.txt");
if (sourceFileStream) {

std::string question{};
int temp{}, answer{};
char comma{};

// Read all questions and answers in a loop
while (std::getline(sourceFileStream >> temp >> comma >> std::ws, question))
if (sourceFileStream >> temp >> comma >> answer) {
// We do not want to go out fo sync. Always store questions and answers together
questions.push_back(question);
answers.push_back(answer);
}

// Debug output
for (unsigned int k = 0; k < questions.size(); ++k)
std::cout << "Question: " << questions[k] << "\t\tAnswer: " << answers[k] << '\n';
}
else
std::cerr << "\nError: Could not open source file\n";
}

But maybe not that recommended...

How do I feed a function containing a vector with different data types with these different data types?

You need to move items in vector, as your items are no copyable:

v1.emplace_back(std::move(anotherCircle));

How to store objects with different datatypes in one vector

Here are a few alternatives.

If the different data types you want to store are POD types, you can create a union to store them.

union u {
int intvalue;
double double value;
}

vector<u> vec;

You can also store pointers to instances of disparate types via a union.

A disadvantage of using unions like this is that you need some way of knowing what type is a actually stored in each instance.

Or If you want to store objects, you can arrange your objects to all inherit from a common base class, then allocate instances of your class on the heap and store pointers to the instances in the vector. Depending on how your objects are too be used you would have to cast the pointers and/or use virtual methods to call methods on the objects.

class base {
...
}

class d1 : base {
...
}

vector<base*> vec;

vec.push_back((base*)new d1());

Or you can use boost.variant. This is arguably the best way because it is typesafe compared to the other methods I mentioned.



Related Topics



Leave a reply



Submit