template array in c++ with 3 different definitions - c++

I want to make an Array with 3 differnt definitions. More specifically i want an array like this : A[i,j]=(string, int, bool
string, int, bool)
I am trying to make a template but i am confused.

If you want to use templates you can make an array of structs:
template <class A, class B, class C>
struct Vars{
A a;
B b;
C c;
};
//declaring the types of the template you want to use
Vars <std::string, int, bool> *ray;
//initializing array in heap
ray = new Vars<std::string, int, bool>[SIZE];
Alternatively, If you have decided you always want to use this combination you can much more easily do this:
struct Vars{
std::string a;
int b;
bool c;
};
Vars *ray = new Vars[SIZE];

You can make a regular class that implements this behavior.
Something like this:
class specialArray {
char *** strings;
int **integers;
bool **booleans;
public:
specialArray(int rows, int columns, unsigned char data[]) {
//initialize arrays to correct sizes
/*add a special values, such as 253, 254, 255 (which are not
used for chars, bools, or strings (since the operating system
takes control of memory locations 253, 254, 255)) every time the
type of the array changes*/
/*Use the special values to determine which array each byte of
information should go to (and use casts). Arrays which are not
filled at each index should be set to 0 at that position*/
/*create a function access(int loc) to obtain a value. In this
function, check each of the three arrays until one of the arrays
at that index value is not 0. Return a byte array.*/
/*Implementing this in c++ is extremely inefficient, by the
way as you can see by the enormous amount of code this
requires*/
}
//do something similar for operator=()
}

If I understand you correctly, arrays -AFAIK- can have only one datatype. However you can use struct/classes pair two different types then make a one dimensional array out of it.
Check this code illustration, and check if it is what you are looking for.
template<typename T, typename U>
struct MyArray
{
T a;
U b;
};
int main()
{
MyArray<int,bool> arr[2];
arr[0] = {30,1};
cout << arr[0].a;
cout << " : ";
cout << arr[0].b;
}
You may also use pair, tuples and/or vectors... etc. It all depends on your vision and needs.

Related

Use an array with non-static size inside of a class

I want to use a class that can store a value for me inside of a 4d array (Matrix). I want to useSetSize in order to set the size of the Matrix from my main function.
class Value{
public:
int a;
int b;
int c;
int d;
//sets the values
void SetSize(long Sizea, int Sizeb, int Sizec, int Sized){
a = Sizea;
b = Sizeb;
c = Sizec;
d = Sized;
};
double Matrix[a][b][c][d];
void Putavalueinside (double input, long Positiona, long Positionb, long Positionc, long Positiond) {
Matrix[Positiona][Positionb][Positionc][Positiond] = input;
}
};
int main()
{
//makes a class object
Value Test;
//sets the size
Test.SetSize(3, 4, 5, 6);
//gives the Matrix a value
Test.Putavalueinside(10, 0, 0, 0, 0);
//prints it to the consol
std::cout << Test.Matrix[0][0][0][0];
}
However, the program does not compile and gives me the error:
|15|error: invalid use of non-static data member 'Value::a'|
Does anyone have an idea on how to solve this?
There are basically 2 ways to solve this. If you want to keep the array, then you need to move the dimension sizes from being constructor parameters to being template parameters. Template parameters are known at compile time and can be used to create an array. That would look like
template <size_t a, size_t b, size_t c, size_t d>
class Value{
public:
double Matrix[a][b][c][d]{};
void Putavalueinside (double input, long Positiona, long Positionb, long Positionc, long Positiond) {
Matrix[Positiona][Positionb][Positionc][Positiond] = input;
}
};
int main()
{
//makes a class object
Value<3,4,5,6> Test;
//gives the Matrix a value
Test.Putavalueinside(10, 0, 0, 0, 0);
//prints it to the consol
std::cout << Test.Matrix[0][0][0][0];
}
If you don't know what the sizes will be at compile time then you are going to need to do some dynamic memory allocation. To handle this you can use a std::vector as the storage type of the matrix and then you can use math to pretend it is a 4d structure. That would look like
class Value{
public:
size_t a;
size_t b;
size_t c;
size_t d;
std::vector<double> Matrix;
Value(size_t a_, size_t b_, size_t c_, size_t d_) : a(a_), b(b_), c(c_), d(d_), Matrix(a_ * b_ * c_ * d_) {}
void Putavalueinside (double input, long Positiona, long Positionb, long Positionc, long Positiond) {
Matrix[Positiona + Positionb * a + Positionc * a * b + Positiond * a * b * c] = input;
}
double Getvalueinside (long Positiona, long Positionb, long Positionc, long Positiond) {
return Matrix[Positiona + Positionb * a + Positionc * a * b + Positiond * a * b * c];
}
};
int main()
{
//makes a class object
Value Test(3,4,5,6);
//gives the Matrix a value
Test.Putavalueinside(10, 0, 0, 0, 0);
//prints it to the consol
std::cout << Test.Getvalueinside(0, 0, 0, 0);
}
The formula for flattening an n-dimension array can be found here: 4D position from 1D index?
Your array sizes need to be known at compile time if you want to avoid dynamic allocation. Possible implementation:
template <size_t a, size_t b, size_t c, size_t d>
class Value
{
double Matrix[a][b][c][d];
};
Also, consider using std::array. If you know the sizes only at runtime, you need to allocate dynamically (with new). Even better, use std::vector (which uses heap memory).
The size of a member array (as well as all automatic or static arrays) must be a compile time constant. So, what you're trying is not possible.
In order to have an array with non-static size, you must allocate the array dynamically. I recommend using std::vector to do the allocation.
Unfortunately, dynamic allocation means that you can only use a one dimensional array (only the outermost dimension of dynamic array may be determined at runtime). This is not a big deal however; the memory of a multi dimensional array is just as flat as the single dimensional array; this just means that you have to calculate the index instead of the compiler doing it for you.
Here is an example layout of a 2 x 2 x 2 x 2 array laid flat:
0123456789abcdef <- flat index
0000000011111111 <- dimension a
0000111100001111 <- dimension b
0011001100110011 <- dimension c
0101010101010101 <- dimension d
In C++ you should use hardly, if ever, C-style dynamic arrays (i.e. using new). You should define your Matrix as
std::vector<std::vector<std::vector<std::vector<double>>>> Matrix;
(I know it is ugly, but thats how it is, you should probably add a using matrix_t = std::vector<std::vector<std::vector<std::vector<double>>>>; somwhere, so you only have to type that once). Then initialize this vector in the constructor.
As constructor use
Value(size_t a, size_t b, size_t c, size_t d) :
Matrix({a, {b, {c,std::vector<double>(d,0.0)}}})
{}
You can also us a one-dimensional vector of size a*b*c*d and calculate the index in this array from the 4 indices in the matrix.
As you've written things, the field Value::Matrix must be allocated when you create a Value object, because its size determines the size of the Value object. This size can't be changed later on. (In fact, the size of an object needs to be a compile-time constant, so that it can be stack-allocated, made into arrays, etc.)
If you want something whose size is going to be determined at runtime, it needs to be heap-allocated, e.g. by using new[]. (There are various ways of wrapping new[] with higher-level constructs so that you don't have to keep track and delete[] the result when you're done with it, e.g. std::unique_ptr or std::vector, but you should learn how to work with pointers first so you understand what these are even doing and why.)
Also, you need to take into account the fact that a multidimensional array in C++ is an array of pointers to arrays. This is bad for performance due to extra indirection and poor cache locality, so you may want to implement things as a one-dimensional array under the hood.
Alternatively, if the size is known at compile time, it could be given as a template parameter:
template<int A, int B, int C, int D>
class Value
{
int a = A;
int b = B;
int c = C;
int d = D;
double Matrix[A][B][C][D];
//...
};
// ...
Value<3, 3, 2, 5> val;
Note that this still has the issues with multidimensional arrays mentioned above.

Dynamic dereference of a n-level pointer

Suppose a n-dimensional array that is passed as template argument and should be traversed in order to save it to a file. First of all I want to find out the size of the elements the array consists of. Thereto I try to dereference the pointers until I get the first element at [0][0][0]...[0]. But I already fail at this stage:
/**
* #brief save a n-dimensional array to file
*
* #param arr: the n-level-pointer to the data to be saved
* #param dimensions: pointer to array where dimensions of <arr> are stored
* #param n: number of levels / dimensions of <arr>
*/
template <typename T>
void save_array(T arr, unsigned int* dimensions, unsigned int n){
// how to put this in a loop ??
auto deref1 = *arr;
auto deref2 = *deref1;
auto deref3 = *deref2;
// do this n times, then derefn is equivalent to arr[0]...[0], 42 should be printed
std::cout << derefn << std::endl;
/* further code */
}
/*
* test call
*/
int main(){
unsigned int dim[4] = {50, 60, 80, 50}
uint8_t**** arr = new uint8_t***[50];
/* further initialization of arr, omitted here */
arr[0][0][0][0] = 42;
save_array(arr, dim, 4);
}
When I think of this from a memory perspective I want to perform a n-indirect load of a given address.
I saw a related question that was asked yesterday:
Declaring dynamic Multi-Dimensional pointer
This would help me a lot as well. One comment states it is not possible since types of all expressions must be known at compile-time. In my case there's actually known everything, all callers of save_array will have n hardcoded before passing it. So I think it could be just a matter of defining stuff at the right place what I am yet not able to.
I know I am writing C-style code in C++ and there could be options to achieve this with classes etc., but my question is: Is it possible to achieve n-level pointer dereference by an iterative or recursive approach? Thanks!
First of all: Do you really need a jagged array? Do you want to have some sort of sparse array? Because otherwise, could you not just flatten your n-dimensional structure into a single, long array? That would not just lead to much simpler code, but most likely also be more efficient.
That being said: It can be done for sure. For example, just use a recursive template and rely on overloading to peel off levels of indirection until you get to the bottom:
template <typename T>
void save_array(T* arr, unsigned int* dimensions)
{
for (unsigned int i = 0U; i < *dimensions; ++i)
std::cout << ' ' << *arr++;
std::cout << std::endl;
}
template <typename T>
void save_array(T** arr, unsigned int* dimensions)
{
for (unsigned int i = 0U; i < *dimensions; ++i)
save_array(*arr, dimensions + 1);
}
You don't even need to explicitly specify the number of indirections n, since that number is implicitly given by the pointer type.
You can do basically the same trick to allocate/deallocate the array too:
template <typename T>
struct array_builder;
template <typename T>
struct array_builder<T*>
{
T* allocate(unsigned int* dimensions) const
{
return new T[*dimensions];
}
};
template <typename T>
struct array_builder<T**> : private array_builder<T*>
{
T** allocate(unsigned int* dimensions) const
{
T** array = new T*[*dimensions];
for (unsigned int i = 0U; i < *dimensions; ++i)
array[i] = array_builder<T*>::allocate(dimensions + 1);
return array;
}
};
Just this way around, you need partial specialization since the approach using overloading only works when the type can be inferred from a parameter. Since functions cannot be partially specialized, you have to wrap it in a class template like that. Usage:
unsigned int dim[4] = { 50, 60, 80, 50 };
auto arr = array_builder<std::uint8_t****>{}.allocate(dim);
arr[0][0][0][0] = 42;
save_array(arr, dim);
Hope I didn't overlook anything; having this many indirections out in the open can get massively confusing real quick, which is why I strongly advise against ever doing this in real code unless absolutely unavoidable. Also this raw usage of new all over the place is anything but great. Ideally, you'd be using, e.g., std::unique_ptr. Or, better yet, just nested std::vectors as suggested in the comments…
Why not just use a data structure like tree with multiple child nodes.
Suppose you need to store n dimensional array values, create a node pointing to the first dimension. Say your first dimension length is 5 then you have 5 child nodes and if your 2nd dimension size is 10. Then for each of these 5 node you have 10 child nodes and so on....
Some thing like,
struct node{
int index;
int dimension;
vector<node*> children;
}
It will be easier to traverse through tree and is much cleaner.

How to have a 2-dimensional array as a private variable in a class, and then set it in the constructor

In my platformer game which I'm writing in Visual C++, each level will initially be stored as a 2-dimensional array of ints. I decided it would make more sense to store this array in a class, so I created a class called Level. It looks like this:
class Level {
private:
int map[20][30];
public:
Level(int a[20][30]) {
map = a;
}
int getcell(int row, int column) {
return map[row][column];
}
};
As far as I can see - from looking up tutorials on class constructors, and passing 2-dimensional arrays as parameters, this should work, so I really don't understand why it doesn't.
On the line where I do map = a, I get an error: Error: expression must be a modifiable lvalue. I've looked this error up on stackoverflow, but I can't find any answers which relate to my problem.
So, how can I fix this error?
This doesn't really have anything to do with a constructor. You cannot assign arrays in C++. Whether in the constructor, or anywhere else.
There are two ways to work around it. The first way is the brute force way. Instead of
map = a;
write a loop to copy the contents of the array from the constructor's parameter into the class member array.
The second way is to stuff the array into an intermediate class:
class Level {
public:
struct level_map {
int map[20][30];
};
private:
level_map map;
public:
Level(const level_map &initial_map) : map(initial_map)
{
}
int getcell(int row, int column) {
return level_map.map[row][column];
}
};
This may or may not be practical, and introduces a little bit more complexity.
But the real answer here is to use std::vectors instead of plain arrays, which will solve all of these problems.
Others have already mentioned the real reason: you cannot assign an array to another using = operator. My two cents about your class:
map is not a good name, it may get conflict with std::map if using namespace std; or using std::map was specified somewhere.
The constant array sizes make this class non-reusable. Class should be flexible to allow any N*M sized 2D array. For this, better to use vector<vector<int>>.
getcell should be a const method, and it should do error checking with row and column numbers passed.
If you want this class to have static-sized array sizes and compile time, you may use class templates with row and column sizes as non type template arguments.
template<size_t row, size_t column>
class Level
{
int _map[row][column];
public:
Level(int src[row][column])
{
memcpy(_map, src, sizeof(_map)); // why not simply 'memcpy' ?
}
};
int main()
{
int source[10][2] = { {1, 2}, {3,4} };
Level<10, 2> ten_by_2(source);
}
Here the map is a constant value, which could not been assigned as an lvalue. This could be fixed by iterating the element of the array, and assign a[i][j] to map[i][j].
class Level {
private:
int map[20][30];
public:
Level(int a[20][30]) {
for(int i = 0; i < 20; ++i)
for(int j = 0; j < 30; ++j)
map[i][j] = a[i][j];
}
int getcell(int row, int column) {
return map[row][column];
}
};

More than one variable type in array

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

C++ initialization of struct containing an array

I have a structure that more or less follows this pattern:
struct sTruct {
int count;
struct {
int A;
int B;
int C;
} array[]; //count is the size of this array
};
I would like to be able to initialize these with something like the following syntax:
sTruct gInit1 = { 2, { {1,2,3},{4,5,6} }};
Really, that initialization syntax (or rather, the compactness of it) is more important than the specific struct layout. I do not have access to the standard containers (embedded platform), but I might be able to replicate some of their behavior if needed.
In final form, I would like to initialize an array of roughly 300 of these sTruct containers at once, just to add one more level of parenthesis.
You can't do it. If you gave the array a size you could. An alternative might be:
template < int size >
struct sTruct
{
struct { int a, int b, int c } array[size];
};
sTruct<2> gInit1 = {{1,2,3},{4,5,6}};
But, of course, all your sTructs are different types and so it may not be what you want. Your only other alternative is going to have to be free-store based and won't give you that syntax until initialization lists in 0x.