So, I have declared a typedef as:
typedef float Matrix4[4][4];
when I try to Initialize it as:
void getXRotationMatrix(float theta, Matrix4 ans) {
ans = { {0,0,0,0},{1,1,1,1},{0,0,0,0},{1,1,1,1} };
}
it gives an error:
too many initializer values
however, it works when I initialize it as:
void getXRotationMatrix(float theta, Matrix4 ans) {
float a[4][4] = { {0,0,0,0},{1,1,1,1},{0,0,0,0},{1,1,1,1} };
ans = a;
}
It doesn't give any error. Can someone please explain?
when I try to Initialize it as: ans = { {0,0,0,0},{1,1,1,1},{0,0,0,0},{1,1,1,1} };
Note that
ans = { {0,0,0,0},{1,1,1,1},{0,0,0,0},{1,1,1,1} };
is an assignment and not an initialization. On the other hand,
float a[4][4] = { {0,0,0,0},{1,1,1,1},{0,0,0,0},{1,1,1,1} };
is an initialization and so works.
The correct way to initialize ans would be:
Matrix4 ans{ {0,0,0,0},{1,1,1,1},{0,0,0,0},{1,1,1,1} };
Also note that array assignment is not possible in C++.
There is also the concept of type decay that you have to take care(into account) for ans = a;. In particular, both ans and a are actually pointers and so when you write
ans = a;
you're actually assigning one pointer to another instead of one array to another.
You can use a std::vector as follows:
typedef std::vector<std::vector<float>> Matrix4;
void getXRotationMatrix(float theta, Matrix4 ans) {
ans = { {0,0,0,0},{1,1,1,1},{0,0,0,0},{1,1,1,1} };
}
Error with initialization of 2d arrays in C++
when I try to Initialize it as:
ans = { {0,0,0,0},{1,1,1,1},{0,0,0,0},{1,1,1,1} };
Problem 1: That's not initialisation. That's assignment. Arrays cannot be assigned, but that's irrelevant due to next point.
Problem 2: ans is not an array. Function parameters are never arrays. Although you wrote it as an array, it has been adjusted to be a pointer to the element of the said array type. And the braced-init list that you provided is not a valid right hand argument for assigning a pointer.
however, it works when I initialize it as:
ans = a;
It becomes well-formed because a is a valid expression to assign to the pointer. The array will implicitly convert to a pointer to first element.
But the pointer is local to the function, so that function has no side-effects whatsoever.
I recommend returning instead of attempting to modify the parameter. Now, arrays cannot be returned in C++, but there is a simple solution: You can return class instances, and classes may contain array as a member. There is a class template for such array wrapper in the standard library. It's called std::array. Example:
std::array<float[4], 4>
getXRotationMatrix() {
return {{
{0,0,0,0},
{1,1,1,1},
{0,0,0,0},
{1,1,1,1},
}};
}
Related
Let's say I have a struct that I am just using as an aggregate data holding device. As an example, a struct below holds two integer values. Let's assume I have a vector of these structs called myVec and I want to add a new struct to the vector with push_back. My understanding is that in C++11 and after one can achieve this without needing to make a named variable and passing that variable as an argument but instead can do the following.
struct coordinate {
int y;
int x;
};
//assume an initialized vector<coordinate> myVec
//and two ints newY, newX not known at compile time
myVec.push_back( {newY,newX});
Based on the Notes section of https://en.cppreference.com/w/cpp/language/aggregate_initialization,
It seems like this is an example of using aggregate initialization in a constructor initializer list but I might be wrong about that. That source indicates that this syntax doesn't work pre-C++11 and while it works with MSVS2017 it doesn't with Visiual C++ for Python which is what I am targeting. Is there a syntax pre-C++11 that would allow me to add another struct to the vector without first making a named struct variable and passing that named variable to the push_back function? While this is a single example of what I'm trying to accomplish, I will have several such vectors in my project and the structs won't always be simple pairs of ints. I would prefer to avoid having several "temp" dummy variables floating around as placeholders for arguments simply because it feels cleaner to avoid having them floating around. An example of what I would prefer to avoid but will use if necessary is given below. The example adds the list of all coordinates where both the x and y values are between 0 and 9 inclusive to the vector.
coordinate temp = {0,0};
for (int r = 0; r < 10; ++r){
for (int c = 0; c < 10; ++c){
temp.y = r;
temp.x = c;
myVec.push_back(temp);
}
}
Just give your struct a constructor:
struct coordinate {
int x, y;
coordinate( int ax, int ay ) : x(ax), y(ay) {}
};
You can then say things like:
myVec.push_back( coordinate( 12, 42 ) );
I was wondering if there is an easy way to access an element of a two dimensional array using a COORD struct.
for example:
COORD myCoord = {2,6};
TwoDiArray myArray;
myArray.at(myCoord) = 10;
I have no idea how to properly do this. Any suggestions?
You just have to define a proper function at():
class TwoDiArray {
...
// assuming there is some 2-dimensional array representation arr
public:
int& at(COORD c) noexcept { return arr[c.x][c.y]; }
};
I’m making the assumption that your TwoDArray type is a type you can’t change and which is accessed - well - like a two dimensional array, e.g., an alias for a two dimensional built-in array (otherwise see #Jodocus’s answer). You could implement an accessory taking an array reference and a COORD object:
template <typename Array2D>
auto at(Array2D&& array, COORD c) -> decltype(array[c.x][c.y]) {
return array[c.x][c.y];
}
(I don’t know the name of the COORD members)
You’d use the function like this:
at(myArray, myCoord) = 10;
I am pretty new to C++ with Boost.
I want an object of class "world" to have an array named "chunk" of type "octreenode". Previously I had an ordinary one-dimensional array, and this worked fine. Now I'm trying to move to using a 3D array with Boost's multi_array functionality, and I'm really not sure what I'm doing wrong.
Simplified code:
class world {
public:
typedef boost::multi_array<octreenode, 3> planetchunkarray; // a boost_multi for chunks
typedef planetchunkarray::index index;
planetchunkarray *chunk;
world(double x,double y,double z,
int widtheast, int widthnorth, int height) :
originx(x), originy(y), originz(z),
chunkseast(widtheast), chunksnorth(widthnorth), chunksup(height) {
chunk = new planetchunkarray(boost::extents[chunksnorth][chunkseast][chunksup]);
planetchunkarray::extent_gen extents;
for (int cz = 0; cz < chunksnorth; ++cz) {
for (int cx = 0; cx < chunkseast; ++cx) {
for (int cy = 0; cy < chunksup; ++cy) {
(*chunk)[cz][cx][cy] = new octreenode(1,72);
}
}
}
}
};
After which if I attempt to make the assignment
root->planet[0]->chunk[0][0][0]->material = 4;
I get the error:
error: base operand of '->' has non-pointer type 'boost::detail::multi_array::sub_array<octreenode, 1u>'|
"octreenode" has the relevant constructor, and this line worked in identical syntax when it was just:
root->planet[0]->chunk[0]->material = 4;
(with a one-dimensional array). Similarly, while it compiled fine with a one-dimensional array, trying to pass the chunk to functions that expect a pointer to an "octreenode" object, such as:
compactoctree(root->planet[p]->chunk[cz][cx][cy], 0, 14);
generates the error
error: cannot convert 'boost::detail::multi_array::sub_array<octreenode, 1u>' to 'octreenode*' for argument '1' to 'short int compactoctree(octreenode*, int, int)'|
Would be very grateful for any suggestions, I'm sure I'm missing something obvious.
Your array is of value type (octreenode), not pointer type (octreenode*)
Therefore you are not supposed to try to assign a pointer to a dynamically allocated octreenode (new is for heap allocation, by default).
Instead, just assign a value:
(*chunk)[cz][cx][cy] = octreenode(1,72);
In fact, there's no reason to use new on the multi array in the first place either:
UPDATE
In the comments it has been raised that more things could be optimized and that you consider that useful additions to the answer about the compilation error.
So here goes: if you indeed want to initialize all array elements with the exact same value,
You can make the loops way more efficient by forgetting about the array shapes for a moment:
std::fill_n(chunk.data(), chunk.num_elements(), octreenode {1, 72});
If you know octreenode is a POD type, you could write
std::uninitialzed_fill_n(chunk.data(), chunk.num_elements(), octreenode {1, 72});
but a smart library implementation would end up calling fill_n anyways (because there's no gain). You can use uninitialized_fill_n if octreenode is not a POD type, but it is trivially destructible.
In fact, there's no reason to use new on the multi array in the first place either. You can just use the constructor initialization list to construct the multi_array member
Live On Coliru
#include <boost/multi_array.hpp>
#include <type_traits>
struct octreenode { int a; int b; };
class world {
public:
world(double x, double y, double z, int widtheast, int widthnorth, int height)
:
originx(x), originy(y), originz(z),
chunkseast(widtheast), chunksnorth(widthnorth), chunksup(height),
chunk(boost::extents[chunksnorth][chunkseast][chunksup])
{
octreenode v = { 1, 72 };
std::fill_n(chunk.data(), chunk.num_elements(), v);
}
private:
double originx, originy, originz;
int chunkseast, chunksnorth, chunksup;
typedef boost::multi_array<octreenode, 3> planetchunkarray; // a boost_multi for chunks
typedef planetchunkarray::index index;
planetchunkarray chunk;
};
int main() {
world w(1,2,3,4,5,6);
}
How should I return an array from a function? My code is
float ClassArray::arr_sub(float a[100][100], float b[100][100]) {
int i,j;
for(i = 1; i < 10; i++) {
for(j = 1; j < 10; j++){
f[i][j]=b[i][j]-a[i][j];
}
}
return f;
}
and the f returned from this function should be assigned to another array g declared in some other class.
float g[100][100];
g= cm.arr_sub(T,W);
but while building the classes, it says incompatible type assignment of float to float[100][100].
My answer here to another question on arrays explains why you don't want to use arrays.
As I say in that answer you can't assign an array like you're trying:
float g[100];
g = foo(); // illegal, assigning to arrays is not allowed
Another of the weird restrictions on arrays is that you're not allowed to return them from functions:
float foo()[100]; // illegal, returning an array from a function is not allowed
Also note that when you declare a function like float arr_sub(float a[100][100]) you might think you're passing an array by value, but in fact that invokes another of the weird exceptions made for arrays. In C and C++, whenever you declare a formal parameter of a function to be an array, the type is adjusted from 'array' to 'pointer to the array's element type'.
Since arrays don't behave like they ought, you should instead use std::array or std::vector:
std::array<float,100> foo(); // works
std::array<float,100> g;
g = foo(); // works
To do multi-dimentional arrays you can use:
std::array<std::array<float,100>,100> g;
Though that's a bit cumbersome so you can typedef it:
typedef std::array<std::array<float,100>,100> Matrix;
Matrix ClassArray::arr_sub(Matrix a, Matrix b) {
...
}
Matrix g;
g = cm.arr_sub(T,W);
And if you have a compiler that supports C++11 you can even do a template type alias:
template<typename T,int Rows,int Columns>
using Matrix2d = std::array<std::array<T,Columns>,Rows>;
Matrix2d<float,100,100> g;
Note on performance
There is one reason you might not want to return an std::array by value. If the array is large then there may be a signficant performance cost in copying the data from the return value into the variable you assign it to. If that ever proves to be a problem for you, then the solution with std::array is the same as it would be for other large types; use an 'out' parameter instead of returning by value.
void arr_sub(Matrix a, Matrix b, Matrix &result);
Matrix g;
arr_sub(T,W,g);
This doesn't apply to std::vector because std::vector can take advantage of move semantics to avoid having to copy all its elements.
If you insist on using "plain C" 2D arrays, the best thing is to pass a pointer to the result along with the two input parameters, rather than passing the arrays by value the way you did.
However, the best thing to do in C++ is to use vector<vector<float> > instead, and pass it by reference.
void ClassArray::arr_sub(
const vector<vector<float> > &a
, const vector<vector<float> > &b
, vector<vector<float> > &res)
{
for(int i=0 ; i != a.size() ; i++)
for(int j=0 ; j != b.size() ; j++)
res[i][j] = b[i][j] - a[i][j];
}
void main() {
vector<vector<float> > a(100, vector<float>(100, 12.345));
vector<vector<float> > b(100, vector<float>(100, 98.765));
vector<vector<float> > res(100, vector<float>(100, 0));
arr_sub(a, b, res);
}
The best way to do this is to wrap everything into a class. From the look of things, its a Matrix.
There are probably a hundred Matrix classes out there already, so it is really pointless to write another one.
But, if this is a learning exercise it might be worthwhile.
To answer your asked question, make a third argument to your function: float result[100][100]. Inside your function, write the results into the result array.
This works because in C and C++, arrays are always passed by reference and never by value. This is because C++ passes only the pointer to the beginning of the array.
if you really wish to return an array and some how manage to use it in the main(), the most efficient way would be to declare the returning array as dynamic. that way you will avoid losing the pointer to this new array as it will be allocated in heap and not in stack.
I have struct:
struct mat4 {
float m[16];
mat4();
...
float *getFloat();
}
float *mat4::getFloat() {
return m;
}
Now I want to make m equal to m from newly created matrix r:
void mat4::rotate(vec3 v) {
mat4 r, rx, ry, rz;
...
matrix calculations
...
m = *r.getFloat();
}
But this gives me error "incompatible types in assignment of ‘float’ to ‘float [16]’"
I have searched Google and tried different ways but no success so far.
Please tell me how could I do that?
getFloat() returns a pointer. m is an array. If you want to copy all of what is returned into m you will need to use std::copy or std::memcpy:
std::memcpy(m, r.getFloat(), sizeof(m));
If you meant to get just one float from getFloat() you could do:
m[0] = *r.getFloat();
r.getFloat() is returning a pointer to a single float. This is dereferenced to give a single float, and then assigned to an array of 16 floats.
Assuming that m contains the entire state of mat4, you can use the built in assignment operator:
*this = r;
The compiler will automatically implement the struct dump/ memcpy to copy all 16 floats.
Don't use naked C arrays. This is C++, we can do a lot better:
#include <tr1/array> // or <array> if you're in MSVC or GCC
typedef std::tr1::array<float, 16> myfloats;
int main()
{
myfloats a, b;
a[0] = /* ... fill the array */
b = a; // done
}
You can also put the array into your matrix structure:
struct mat4
{
typedef std::tr1::array<float, 16> myfloats;
myfloats & data() { return m_data; }
mat4(); /* ... etc. ... */
private:
myfloats m_data;
};
You should be able to just assign variables of type mat4 to each other directly!
Use std::copy as:
#include <algorithm>
std::copy(r.m, r.m+16, m);
Also, if you can use r.m directly, why call r.getFloat() then?
r.getFloat() returns a pointer to "its" m. Arrays cannot be copied with simple assignment; you would have to work either with memcpy() or memmove() or with a for loop over the array elements.