I would appreciate your help!
Which data structure is used to implement heterogeneous array in C or C++ ? The array may contain any standard data types like int,float, char, string etc...
As ravi mentions, the appropriate data structure is called a tag-union (also called variant record). One way to implement it is this:
typedef union {
int tag;
struct {
int tag;
int val;
} int_;
struct {
int tag;
double val;
} double_;
} object;
Then you can make an array of these.
object arr[5];
You use the tag field to indicate which union member is in use. Typically with an enum.
enum type { INT, DOUBLE };
Then set the tag when creating the object, and check the tag before accessing. This can be encapsulated by using constructor functions.
object new_int(int i){
object ret;
ret.tag = INT;
ret.int_.val = i;
return ret;
}
object new_double(double d){
object ret;
ret.tag = DOUBLE;
ret.double_.val = d;
return ret;
}
And you usually want to use a switch on the tag for accessing, writing a different case for each type.
void print_object(object x){
switch(x.tag){
case INT: printf("%d\n", x.int_.val); break;
case DOUBLE: printf("%f\n", x.double_.val); break;
}
}
Or in some circumstances, you may want to fold the array into a single type so it can be accessed without checking the tag each time.
for (i = 0; i < sizeof arr/sizeof*arr; i++)
if (arr[i].tag == INT)
arr[i] = new_double(arr[i].int_.val);
There is no such array in c++ which can store elements of different types nor there is container in stl. Although there's one way to store different element in a container but condition is those types should be related through inheritance.
In C there's a concept called tagged union which can store different types giving tag as a means to specify which variable is actually there.
One more way to do this is using an array of void* pointers. Although that would be quite ugly C++. This would not be truly heterogeneous as you are using a type of pointer that any pointer can be cast into. It is similar to making a collection of base class type and then storing objects of derived classes.
This I got from Stroustrup article:-
If you need a heterogeneous container in C++, define a common interface for all the elements and make a container of those. For example:
class Io_obj { /* ... */ }; // the interface needed to take part in object I/O
vector<Io_obj*> vio; // if you want to manage the pointers directly
vector< Handle<Io_obj> > v2; // if you want a "smart pointer" to handle the objects
Apart from that Boost::Any can also be used:-
vector<Any> v;
I guess you could keep an array of pointers to anything
void* stuff[size];
const char* str = "hello";
int x = 20;
int *array = malloc(sizeof(int) * 5);
stuff[0] = str;
stuff[1] = &x;
stuff[2] = array;
Alternatively, an array of unions if you knew all the types before hand.
Related
This question already has answers here:
C++ std::map holding ANY type of value
(8 answers)
Closed 1 year ago.
Hey I'm new with c++ and I'm trying to create a map wich can store multiples types :
map<sting, `auto`> test_map;
test_map["first elem"] = 1;
test_map["second elem"] = 'c';
Wich obviously gets me some errors.
I've been looking a bit online and I have found interesting things but not an answer. Maybe I miss a bit of c++ vocabulary.
I would also try with some kind of class wich stores about any types and I don't know if it can works.
map<string, `my_class`> test_map;
map["first_elem"] = my_class("string");
map["first_elem"] = my_class(12);
Thank you for helping !
The auto keyword doesn't mean you can assign multiple value types to the same storage, it's merely an automatic type deduction tool in c++ useful when you deal with complex type names or purely unwritable types (such as capturing lamdas) eg.:
void foo()
{
bool b = true;
auto l = [&]() -> bool { return !b; };
}
If you want to store values of different (or any) types in the same storage space try using either std::varian or std::any (both require c++17 or higher).
std::variant allows you to store one of the earlier specified types and doesn't do additional allocations (it's size is enough to hold any object of specified type). It's a type safe alternative to unions.
void foo()
{
std::map<std::string, std::variant<int, float, char>> c; // this can only map int, float and char to std::string
}
std::any can store any type you want but will allocate when necessary.
void foo()
{
std::map<std::string, std::any> c; // this can map any type of value to std::string
}
I would like to iterate through a pre-allocated float array with a custom container that does not owns the data, but acts on a segment of it. Example, naming the container class LinhaSobre:
std::unique_ptr<float[]> data(new float[720]);
...
//creates container to iterate 26 floats starting from from data[12]
LinhaSobre cont(data.get()+12, 26);
//sets those elements to 1.5
for(size_t i = 0; i < cont.size(); i++)
cont[i] = 1.5f;
Here's a possible implementation of the operator[] :
//...
//LinhaSobre has a member mem0 which is initialized
//as a pointer to where the interval starts
float & LinhaSobre::operator[] (size_t i)
{
return *(mem0+i);
}
Notice that I'm returning a reference from LinhaSobre::operator[] to data that it does not owns. It should not interfere with the data's lifetime (constructors, destructors).
Now I want to expose the stored data by another pattern, std::array<float,4>, and not pure float. Example, naming the new class LinhaSobre4f:
std::unique_ptr<float[]> data(new float[720]);
...
//creates container to iterate 4 array<float, 4> starting from from data[12]
LinhaSobre4f l(data.get()+(3*4), 4);
//sets those elements to {1.5f, 2.5f, 3.5f, 4.5f};
for(size_t i = 0; i < l.size(); i++)
l[i] = { {1.5f, 2.5f, 3.5f, 4.5f} };
Notice that I treat the items as an array.
This would lead to some changes in the container class, my main concern is with the operator[], here's the full class code:
struct LinhaSobre4f
{
LinhaSobre4f(float * pos_begin, size_t size_):
pos0(pos_begin),
size_(size_){}
std::array<float, 4> & operator[](size_t i)const
{
std::array<float,4> * r =
reinterpret_cast<std::array<float,4>*> (pos0+(4*i));
return *r;
}
size_t size()const
{
return size_;
}
private:
float * pos0;
size_t size_;
};
The operator[] returns a reference to a block of memory treated as an std::array<float,4> that never really existed as such, but given the std::array memory layout guaranties, it works. I'm dubious about this, is it OK? (aside from memory alignment, which I'll guarantee). Am I allowed to expose an object like this, semantically? What is the correct term for this? (I've used fake object in the title).
Here's a live demo of the example. Here's another (the other link sometimes fails)
The C++ standard (I'm reading C++11) defines a std::array as follows:
The conditions for an aggregate (8.5.1) shall be met.
You are not guaranteed that a std::array is a POD. The C++ standard guarantees only that it's a class aggregate.
Based on that, I believe that your usage of reinterpret_cast to convert a POD array of floats to a std::array is undefined behavior.
Chances are that it'll work, with your compiler, but you are not guaranteed that this will be portable, or legal.
You might create a plain old reference_type:
struct LinhaSobre4f {
struct Ref {
Ref(float *m): m(m){};
Ref &operator=(std::initializer_list<float> const &l) {
std::copy(l.begin(), l.end(), m);
return *this;
}
private:
float *m;
};
Ref operator[](size_t i) { return m + 4 * i; }
private:
float *m;
};
Adding on Sam Varshavchik's answer, you may be interested in the span type (formerly known as array_view).
The span type is an abstraction that provides a view over a contiguous sequence of objects, the storage of which is owned by some other object (more details in P0122R1, CppCoreGuidelines and Guidelines Support Library Review: span<T>).
Conceptually, a span is simply a pointer to some storage and a count of the elements accessible via that pointer. It's so small that it can be passed by value.
An open source (header only), reference implementation is available at https://github.com/Microsoft/GSL (the implementation generally assumes a platform that implements C++14 support. There are specific workarounds to support MSVC 2013 and 2015).
I'm new to structs and I'm struggling a bit.
I have the following struct:
typedef struct
{
CHAR_t bWpId[10];
CHAR_t bWpDescr[25];
UINT16_t wCoA;
sUtmCoordinate_t Coordinate;
} sWaypoint_t;
typedef struct
{
sWaypointListElement Element[cMAX_WAYPOINTS];
UINT8_t bHead;
UINT8_t bTail;
UINT8_t bNumElements;
} sWaypointList;
Now each waypoint is an element in a waypointlist which is also a struct.
class CWaypointList
{
private:
sWaypointList WaypointList;
}
Now my question is how do I read in values in each element of the struct without writing accessors? Is accessors the only way to access the data within a private struct?
If I do it like this I get the error : expression must be a modifiable value.:
element.bWpId = {'0','0','0','0','0','0','0','0','0','1'};
You can't use that syntax to initialize an array outside of the arrays definition. You have to fill in all values manually.
Fortunately there are standard C++ functions to do that for use, like std::fill:
std::fill(std::begin(element.bWpId), std::end(element.bWpId), '0');
element.bWpId[9] = '1';
You can of course make a constructor for the sWaypoint_t structure, and initialize the array in that:
typedef struct sWaypoint_s
{
CHAR_t bWpId[10];
CHAR_t bWpDescr[25];
UINT16_t wCoA;
sUtmCoordinate_t Coordinate;
sWaypoint_s()
: bWpId{'0','0','0','0','0','0','0','0','0','1'}
{}
} sWaypoint_t;
The problem with this is that it requires a C++11 capable compiler.
In C++ you can not list-initialise array that has already been constructed. Same applies for list-initialising structures.
Using std::fill is one way, but I dislike its obscurity. Instead you can try using this helper function:
template <class T, size_t Size>
void copy_array(T (&dst)[Size], const T (&src)[Size])
{
std::copy(src, src+Size, dst);
}
Now you can "assign" any array. In the case of element.bWpId:
CHAR_t temp[] = {'0','0','0','0','0','0','0','0','0','1'};
copy_array(element.bWpId, temp);
The function will compile-time check arrays types and sizes, so it leaves no opportunity for a mistake. Which is a huge advantage over explicit std::fill and manual indexing.
All that provided you don't have access to C++11 compiler. If you have, just change the bWpId to std::array<Char_t, 10> and then you can list-initialise it anytime you want.
I used sprintf(element.bId,'0');
I am busy with a dynamic 2d array and I have declared it as follows:
string** MakeArray(int row,int col)
{
string** array;
array = new string* [col];
for(int r = 0; r < row; r++)
{
array[r] = new string [col];
}
return array;
}
Now I can place string values in the array. How can I place Integer values in the first column and strings in second and integers in third, if my array is 4 by 99?
The elements in an array are all the same type. To get what you're after, you probably want to start off rather differently, with an array of structs:
struct whatever {
int a;
std::string b;
int c;
};
std::vector<whatever> your_array;
Edit: although it's almost certainly a lousy idea, if you really need this to be a 2D array, you could try making all your elements the same type of union:
union whatever {
int a;
std::string b;
};
This has some pretty severe limitations though -- in fact, putting a std::string in a union isn't officially supported at all. There's a fairly decent chance it'll work, but no guarantee of it at all. If you really, really need to do something like this, you can make that member of the union a pointer instead. That is guaranteed to work, but also guaranteed to be so clumsy that making mistakes with it is nearly inevitable.
Don't do that. Instead create a struct that will represent single record in a table, and contain a string and two integers. Then create one dimensional array of those structures.
struct record
{
int a;
std::string b;
int c;
};
record* MakeArray(int row)
{
return new record[row];
}
better yet, ditch arrays and use std::vector:
std::vector<record> array(99);
Have you looked at having a vector/array of tuples, if you have C++11 available to you? So you could do something such as:
#include <tuple>
#include <vector>
typedef std::tuple<int, std::string, int> MyTuple;
std::vector<MyTuple> MakeVector()
{
std::vector<MyTuple> vecTuples;
for( int i = 0; i < 5; ++i )
{
vecTuples.emplace_back( std::make_tuple<int, std::string, int>( i, "Test"+i, i+5 ) );
}
return vecTuples;
}
C++ is a "strong-typed" language. One of the things that means is you cannot mix data types (unless they are related, like base-derived class hierarchical relationship).
In other words what you are doing is not what C++ directly supports.
Having said that there's something you can do that would do what you want - have an array of triplets, like this:
struct triplet
{
int first;
string second;
int third;
};
triplet** MakeArray(...
What you are doing in your example looks alot like a JS code though. Maybe what you want is to store all your data as strings? Then yes, you can use a 2D array of strings, but that requires you to convert datum into string when storing it and converting back from string for calculations. Which is a major performance issue
I have a std::vector of type boost::variant which contains pointers to standard built-in types.
In code
std::vector<boost::variant<int *, double *> > bv;
bv.resize(2);
int n = 42;
bv[0] = &n;
double d = 102.4;
bv[1] = &d;
This works well, and I can access the values in the vector by using boost::get<int *> or boost::get<double *>.
However, I was wondering if I could set the value of an element in the vector directly, instead of pointing it to a reference. In other words, I would like to do something like this
*bv[0] = 42;
*bv[1] = 102.4;
but this doesn't compile. Does anyone have advice on how to go about this?
Thanks.
EDIT:
Apparently, I don't have enough points to answer my own question, so figured I'd put my solution in the original question instead. Hope this helps someone who stumbles across this page.
I figured I'd answer my own question, in case it helps someone. I wrote a visitor class, that modifies what the pointer in the variant refers to. Here's the code for the visitor class (of course, this can be made a templated class).
class specific_visitor : public boost::static_visitor<>
{
public:
explicit specific_visitor(int i) : i_num(i) { }
explicit specific_visitor(double d) : d_num(d) { }
void operator()(int * i) const
{
*i = i_num;
}
void operator()(double * d) const
{
*d = d_num;
}
private:
int i_num;
double d_num;
};
In order to use it for the vector of variants in the original question, here's the code:
int i_num = 34;
boost::apply_visitor(specific_visitor(i_num), bv[0]);
double d_num = 9.81;
boost::apply_visitor(specific_visitor(d_num), bv[1]);
If you really need pointers, then
the answer is no, you can't,
because - most probably - there is no feasible way
to allocate and later free the needed memory.
If you need to allocate the memory for (almost?) all
the values, then boost::variant<int, double> is
a way to go.
If you need to store pointers to (almost?) all of
the values and allocate memory for a minority of them,
then you need a more complex solution (anyway).