Is there a C++ container that I could use or build that can contain, say, int
and string
and double
types? The problem I\'m facing is
What I have for this question is not what I hoped would work. By what I think that you would like, is a container that stores multiple value types, that you can access at will.
However, as such, a container would have to specify what value it holds, so you could have a class with 500 data types in it, with a correlating constructor for each data type, however, that would be super memory inefficient.
Here is my proposed suggestion, I have worked on for a day, And I hope it meets your criteria:
#include <iostream>
#include <vector>
using namespace std;
enum class type: unsigned int {int_t, unsigned_int_t, string_t, double_t, float_t, bool_t, unipointer_t, vector_int_t, vector_unipointer_t};//just add item types here and in the switch statement to hold more void_ps in unipointer...
class unipointer {
void* obj;//the pointer to the data. any type of pointer.
type objtype;//the object type, kept as an enum class.
struct void_p {//template magic... ;D
void* void_ptr;
template<typename T>//when object is initialized, it converts the the void* pointer to the output value.
operator T() {
return reinterpret_cast<T&>(void_ptr);
}
void_p(void* val): void_ptr(val) {};
};
public:
unipointer(void_p ptr, type ptrtype) : obj(ptr), objtype(ptrtype) {}
type get_type(void) {//Once you call this function, you know the type of data stored, and can call other functions accordingly.
return objtype;
}
template<typename T>//With a temlate, get any value through a pointer to it.
T get_ptr(void){
return reinterpret_cast<T&>(obj);
}
template<typename T>//With a temlate, get any value, as an object
T get_object(void) {
return *get_ptr<T*>();
}
void_p get_auto_pointer(void) {//get any pointer to value, can't be assigned to "auto*"!
return unipointer::void_p(obj);
}
void_p get_auto_object(void) {//get any value, can't be assigned to "auto"!
return *(void_p*)get_auto_pointer();
}
};
void process_stuff(unipointer& thing, unsigned int num_of_tabs);
int main() {
double initialization = 1.2345;
float even_another = 3.14159f;
unipointer items(new vector<unipointer>{//one thicc object instance
//Initialization examles:
unipointer(new int(-12345), type::int_t),
unipointer(new unsigned int(4'294'967'295), type::unsigned_int_t),
unipointer(new string("That is how I store my items."), type::string_t),
unipointer(&initialization, type::double_t),
unipointer(&even_another, type::float_t),
unipointer(new bool(1), type::bool_t),
unipointer(new unipointer(new unipointer(new unipointer(new string("OMG! NESTING!"), type::string_t), type::unipointer_t), type::unipointer_t), type::unipointer_t),
unipointer(new vector<int>{ 1,2,3 }, type::vector_int_t),
unipointer(new vector<unipointer>{
unipointer(new string("That is how I store my nested items."), type::string_t),
unipointer(new vector<int>{4,5,6}, type::vector_int_t),
unipointer(new string("Is your head brimming with ideas yet?"), type::string_t)
} , type::vector_unipointer_t)
}, type::vector_unipointer_t);
cout << "What is in the \"items\" unipointer:" << endl;
process_stuff(items, 1);
system("pause");
}
void process_stuff(unipointer& thing, unsigned int num_of_tabs) {
//declare variables & lamda for interpretaion methods, using variable assignment with "get auto object/pointer"
unsigned int* test = 0;
double test_2 = 0;
auto tab_to_current = [num_of_tabs]() {
for (unsigned int i = 0; i < num_of_tabs; ++i) {
cout << "\t";
}
};
//format the thing.
tab_to_current();
//look through and do stuff
switch (thing.get_type()) {//just add item types here and in the enum class to hold more void_ps in unipointer...
case type::int_t:
cout << "The integer: " << *thing.get_ptr<int*>() << "." << endl;//one way of getting object back from class
break;
case type::string_t:
cout << "The string: \"" << thing.get_object<string>() << "\"." << endl;//another way
break;
case type::unsigned_int_t:
test = thing.get_auto_pointer();//another way
cout << "The unsigned integer: " << *test << "." << endl;//don't forget to de-reference it!
delete test;
break;
case type::double_t:
test_2 = thing.get_auto_object();
cout << "The double: " << test_2 << "." << endl;//even another way!
break;
case type::float_t:
cout << "The float: " << float(thing.get_auto_object()) << "." << endl;//even another way!
break;
case type::bool_t:
cout << "The boolean: " << *(bool*)thing.get_auto_pointer() << "." << endl;//even another way!
break;
case type::unipointer_t:
cout << "A unipointer, and in it:" << endl;
process_stuff(*&thing.get_object<unipointer>(), num_of_tabs+1);
tab_to_current();
cout << "[End of unipointer]" << endl;
break;
case type::vector_int_t:
cout << "A vector of integers, and in it:" << endl;
for (unsigned int i = 0; i < thing.get_object<vector<int>>().size(); ++i) {
tab_to_current();
cout << "\tItem " << i << ": " << thing.get_object<vector<int>>().at(i) << endl;
}
tab_to_current();
cout << "[End of vector of integers]" << endl;
break;
case type::vector_unipointer_t:
cout << "A vector of unipointers, and in it:" << endl;
for (unsigned int i = 0; i < thing.get_object<vector<unipointer>>().size(); ++i) {
process_stuff(*&thing.get_object<vector<unipointer>>().at(i), num_of_tabs + 1);
}
tab_to_current();
cout << "[End of unipointer vector]" << endl;
break;
}
}
The "unipointer" class should be initialized with a pointer to any object type, and also the type of object. The class can return, through a function, your data, although it is not very safe, and could be called with the wrong type of data.
This is just an example of what could work, I sure hope that you take inspiration from it.
And, to answer your original question, you would set up a list, or vector with the following format:
vector/list:
|
|unipointer(*double)
|
|unipointer(*int)
|
|unipointer(*string)
|
...
|
end
PS: I am a beginner with objects and templates, so this might be messy. Many apoligies.
Well, the first question would be: Why do you think you need to store objects of different, totally unrelated types in the same container? That seems fishy to me.
If I had the need, I'd look into boost::variant
or boost::any
.