Array member declaration and initialization - c++

I want to make vertices an array of Vector. But I don't know how to declare and initialize it. I tried this, but it complains:
class Mesh {
public:
Vector vertices[];
int verticesCount;
Mesh();
virtual ~Mesh();
};
Mesh::Mesh() {
verticesCount = 4;
vertices = new Vector[verticesCount]; // error: expected primary-expression before ']' token
vertices[0] = new Vector(0, 0, 0);
vertices[1] = new Vector(1, 0, 0);
vertices[2] = new Vector(1, 1, 0);
vertices[3] = new Vector(0, 1, 0);
}
Mesh::~Mesh() {
delete vertices;
}
Edit
Trying to correct, applying your tips, I reach this:
Vector* vertices;
//...
vertices = new Vector[verticesCount];
vertices[0] = Vector(0, 0, 0);
vertices[1] = Vector(1, 0, 0);
vertices[2] = Vector(1, 1, 0);
vertices[3] = Vector(0, 1, 0);
//...
delete[] vertices;
And it worked. But is it ok?

You declare vertices as an unspecified array of Vector, then you try to allocate memory for each entry in the array.
First C++ doesn't support empty arrays (if I remember correct), and the data type of the array is not pointers to Vector which means you can not use the new expressions.
If you want to use a dynamic array of vectors, please use std::vector instead:
std::vector<Vector> vertices;
And in the constructor:
vertices.push_back(Vector(0, 0, 0));
// etc.

Array members in C++ must have a compile-time fixed size.
If you want to specify the size of the array at runtime, you must either use a pointer, or preferably a std::vector<Vector>.

Corrections:
Vector* vertices;
[..]
delete[] vertices;

You are declaring an array with an undefined size, compilers might also complain to this by issuing a field has incomplete type error.
Arrays are not pointers. Since an array is included in the memory footprint of the class the size must be known at compile time.
You can either:
use a std::array if size is known a priori
use a std::vector to have a variable sized array
use a pointer Vector *vertices (or Vector **vertices since you are instantiating single vertices)

Related

How to index arrays of descriptors in glsl?

I have array of buffer descriptors, and each of them holds an array of different structs. What I need to do is pick each individually and tell what is inside. I, however, have barely any idea how glsl syntax works, and couldn't find anything online. Currently what I do is
struct myData
{
/.../
};
layout(set = 0, binding = 0)buffer a
{
uint count;
myData data[];
};
//or this
layout(set = 0, binding = 0)buffer a
{
uint count;
myData data[];
} A[2];
//And what I want is
layout(set = 0, binding = 0, index = 0)buffer a
{
uint countMyData;
myData data[];
};
layout(set = 0, binding = 0, index = 1)buffer b
{
uint countIndices;
uint indices[];
};
I have array of buffer descriptors, and each of them holds an array of different structs.
You have contradicted yourself. If you have an array of something, then each element is the same kind of something. Each element of an array of ints may have a different value, but each array element is an int.
That's what "array" means.
This is an either/or situation. Either the elements of a descriptor array use the same descriptor definition, or you create two different descriptor definitions, with two different binding locations, that represent two distinct descriptors.
Now, unsized descriptors that are arrayed can have different sizes for different array elements. But they're all going to use the same descriptor definition.

How to append the content of arrays to a std::vector?

I have two arrays
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
float normals[] = {
0.0, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f
};
I first want to add the vertices array to a std::vector<float> and after that insert the normals array to the vector.
What I can think of is running a loop based on the sizeof(Array) and push back individual element to the vector.
Would that guarantee that the values inserted are in the correct sequence when I access them back?
You can std::vector::insert the elements in the end of the vector.
#include <iterator> // std::cbegin, std::cend, std::size
#include <vector>
std::vector<float> vec;
// reserve the memory for unwated reallocations.
vec.reserve(std::size(vertices) + std::size(normals));
vec.insert(vec.end(), std::cbegin(vertices), std::cend(vertices));
vec.insert(vec.end(), std::cbegin(normals), std::cend(normals));
[...] running a loop based on the sizeof(Array) and push back
individual element to the vector. Would that guarantee that the values
inserted are in the correct sequence when I access them back?
Yes, indeed. Remember you can use std::size to find the size of the array, if you have access to c++17 or later compiler.
You may want to append those two arrays to an existing std::vector or create a new one out of them. In both cases, you very likely want to reserve all the memory needed for the insertions in one go to avoid additional unnecessary reallocations.
For the former case, you can use the following function template:
template<size_t... Ns>
void append_to_vector(std::vector<float>& vec, float (&...arrs)[Ns]) {
constexpr auto num_elements_to_append = sizeof...(Ns);
vec.reserve(vec.size() + num_elements_to_append);
(vec.insert(vec.end(), std::cbegin(arrs), std::cend(arrs)),...);
}
Appending vertices and normals to an existing std::vector<float> becomes:
std::vector<float> vec; // your existing vector
// ...
append_to_vector(vec, vertices, normals);
For the latter case – i.e., you want to create a new vector out of those two arrays – you can use the following function template, create_vector, that in turn calls append_to_vector:
template<size_t... Ns>
std::vector<float> create_vector(float (&...arrs)[Ns]) {
std::vector<float> vec;
append_to_vector(vec, arrs...);
return vec;
}
Creating a new vector from the arrays vertices and normals comes down to a single line:
auto vec = create_vector(vertices, normals);
You are not limited to two arrays. You can actually provide an arbitrary number of arrays to these function templates thanks to their variadic nature, e.g.:
auto vec = create_vector(vertices, vertices, normals, vertices, normals);
The line above works as you may expect, i.e., it creates a new vector resulting from the concatenation of the elements in vertices, vertices (again), normals, vertices and normals.
In any case, for each call to append_to_vector(), only one reallocation will be performed at most because the call to std::vector::reserve() ensures that the memory needed to insert the new elements is available before the insertions.
To insert elements into a std::vector of floats, you can use std::copy:
std::vector<float> my_vector;
my_vector.reserve(std::size(vertices) + std::size(normals)); // optional
std::copy(std::begin(vertices), std::end(vertices), std::back_inserter(my_vector));
std::copy(std::begin(normals), std::end(normals), std::back_inserter(my_vector));
The order of elements in my_vector will be the same as in vertices and normals.
Do we need to reserve the memory before inserting the Arrays ?
Technically, no. But reserving memory might help to avoid unnecessary reallocations.
Edit.
The Notes section for std::vector::reserve at cppreference reads:
When inserting a range, the range version of insert() is generally preferable as it preserves the correct capacity growth behavior, unlike reserve() followed by a series of push_back()s.
The recipe in my answer works, but that in JeJo's one should be preferred.

How to initialize a vector in a struct in c++

I am having a problem with vectors in c++. I am new to c++ so keep this in mind please.
So I have the following struct in the begining of my program:
typedef struct grupo
{
float transX, transY, transZ, rotX, rotY, rotZ, rotAngle, scaleX, scaleY, scaleZ;
char **modelos;
struct grupo** grupos;
int nNomes = 0, nGrupos=0;
std::vector<float> vertices;
};
struct grupo *grupo;
And I just start the main like this:
grupo = (struct grupo*)malloc(sizeof(struct grupo));
grupo->modelos = (char **)malloc(sizeof(1));
grupo->grupos = (struct grupo **)malloc(sizeof(struct grupo));
And in the middle of main I am tryting to use this:
grupo->vertices.push_back(x);
But it keeps crashing the program. I am using visual studio, and using debugging mode. I can see that is when pushing X to the vector that crashes (x has a value). I've tried to change the vector to an array of floats. So the problem i can imagine is with the initialization of the vector... Can you help with what am I missing?
Thank you :)
You're using C++, you don't need the "new" or malloc keyword unless for specific reasons. You don't need the typedef for structs.
I'm not sure what your char** is supposed to be, but you can use std::string for strings.
Maybe what you're trying to do is this:
struct Grupo
{
float transX, transY, transZ, rotX, rotY, rotZ, rotAngle, scaleX, scaleY, scaleZ;
std::string modelos;
int nNomes = 0, nGrupos=0;
std::vector<float> vertices;
};
In main:
Grupo grupo;
grupo.vertices.push_back( ... );
Then I would advise you to read a bit more about what is exactly the C++ language and how it's not C.
Since you're using C++; if you want to create a grupo dynamically, you should use the new operator, with the constructor:
grupo = new grupo();
malloc does not properly initialize C++ objects such as std::vector.
P.S. I am not sure what the grupo->modelos and grupo->grupos are supposed to be, but I'd use proper C++ types for them (perhaps modelos should be std::string, etc). Additionally, I suspect that you've got one * too much for both modelos and grupos.
C++ does not need the typedef in the declaration.
To properly initialize a structure, you should write a ctor to replace the compiler provided ctor (which generally does nothing).
Something like the following (with just a few attributes):
struct grupo
{
float transX, transY;
// ...
int nNomes;
int nGrupos;
std::vector<float> vertices;
// I prefer initialization list form
grupo() : transX(0.0),
transY(1.0),
// ...
nNomes(0),
nGrupos(0)
// vertices default ctor is ok, creates empty vector
{
// use vertices.push_back(...); to fill vertices
}
};
grupo grupo;
Next you will want to write a more useful ctor, one with parameters to use (instead of the literal constants), such that you could build multiple grupo.
grupo grupo1(1.0, 2.0, 3, 4);
grupo grupo2(3.0, 4.0, 5, 6);
// etc.

error declaration is incompatible c++ (Array)

Hi I've been asked to set my arrays that contain 3d cube information to "null" so that they can be used to take in the data that is required to draw them from a txt file but have run int this error.
Any help would be appreciated.
cube header file
class Cube
{
private:
static int numVertices, numColours, numIndices;
static Vertex indexedVertices[];
static Color indexedColors[];
static GLushort indices[];
GLfloat _rotation;
GLfloat _CubeX;
GLfloat _CubeY;
GLfloat _CubeZ;
Cube cpp file
Vertex *Cube::indexedVertices = nullptr;
Color *Cube::indexedColors[] = nullptr;
GLushort *Cube::indices[] = nullptr;
The error appears under indexedVertices , indexedColors and indices
Arrays can't be null.
Also, you didn't specify the size of the arrays in their definitions.
Also, your definitions don't match your declarations! Compare:
static Vertex indexedVertices[]; // declares an array of Vertexes
Vertex *Cube::indexedVertices = nullptr; // defines a pointer to a Vertex
Also compare:
static Color indexedColors[]; // declares an array of Colors
Color *Cube::indexedColors[] = nullptr; // defines an array of pointers to Colors
Arrays are not pointers. Sometimes the language will "helpfully" convert arrays to pointers for you (e.g. indexedVertices is converted to &indexedVertices[0] when used in an expression), but they are not the same thing!

a pointer to `std::list` of Eigen datas

I'm using Eigen3 2-dimensional vector as 2D point for opengl drawing, storing them in a list:
typedef Eigen::Vector2d Vec2D;
std::list<Vec2D> points;
Now, I need an array of GLfloat to pass the entire data structure of raw float coordinate value to the graphic card:
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
_vertex = new GLfloat[points.size()*2];
_colors = new GLfloat[points.size()*4];
std::list<Vec2D>::const_iterator it;
int i=0, j=0;
for(it=points.begin(); it!=points.end(); ++it) {
_vertex[i] = it->x()+2;
_vertex[i+1] = it->y()+2;
i+=2;
_colors[j] = getRed(j/4.0f, it);
_colors[j+1] = getGreen(j/4.0f, it);
_colors[j+2] = getBlue(j/4.0f, it);
_colors[j+3] = getAlpha(j/4.0f, it);
j+=4;
}
glColorPointer(4, GL_FLOAT, 0, _colors);
glVertexPointer(2, GL_FLOAT, 0, _vertex);
glDrawArrays(GL_LINE_STRIP, 0, points.size());
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
delete _vertex;
delete _colors;
Is there a more efficent way for creating the arrays to pass to the graphic cards? like pass points.begin() and find out what is the offset and avoid to loop through all the points?
I mean.. in the memory the x and y coordinates of the Eigen::Vector2d has to be stored in some consecutive space.. so.. I think I can pass it directly to the graphic card.. but I can't unserstand how.
std::list does not hold it's data in contiguous memory, you need std::vector for that(or std::array if you know the size at compile time, but you probably don't). Vector has a method data() which returns a pointer to underlying data. However if you store internally Eigen::vec2d you can't pass it to openGl, since it's a dynamic structure and your data will be all over your memory. You need structure that keeps data in place(and btw is more readable than vec2d, which is kinda odd in this context). For example:
struct VertexData
{
GLfloat x;
GLfloat y;
GLfloat red;
GLfloat green;
GLfloat blue;
GLfloat alpha;
}
And then use glVertexPointer to pass it to openGL using sizeof(VertexData) as stride
To delete new'ed arrays you need to use
delete [] _vertex;
normal delete will only free the first element. Or even better, you could use smart pointers, std::unique_ptr would be best in this case
std::unique_ptr<GLfloat[]> _vertex(new GLfloat[points.size() * sizeof(GLfloat)]);
It will automatically free the memory when it goes out of scope(at the end of the block)
points is a std::list so it doesn't contain an contiguous array of data.
If you use std::vector instead however, then in C++11 you can access the array used internally with points.data(). This means you don't need _vertex anymore.
Know that, as an alternative, you can even go further and have a class (named for instance Vertex) which contains both the vertex position and its color and then use the data() method on your std::vector<Vertex> in combination with glInterleavedArrays (with stride=sizeof(Vertex)).