This question already has answers here:
How to enforce move semantics when a vector grows?
(3 answers)
Closed 8 years ago.
I want to use C++11 move semantics. And I wrote the following class:
class ColorM
{
public:
ColorM(float _r, float _g, float _b, float _a){
qDebug()<<"Constructor";
r = _r;
g = _g;
b = _b;
a = _a;
m = new float[16];
}
ColorM(const ColorM &other){
qDebug()<<"Copy Constructor";
}
~ColorM(){
if (m != nullptr)
{
qDebug()<<"Deleting resource.";
// Delete the resource.
delete[] m;
}
}
// Move constructor.
ColorM(ColorM&& other)
{
qDebug()<<"Move Constructor";
r = other.r;
g = other.g;
b = other.b;
a = other.a;
m = other.m;
other.m = nullptr;
}
float r;
float g;
float b;
float a;
float *m;
private:
};
When I try to:
std::vector<ColorM> vec;
vec.push_back(ColorM(0.1, 0.6, 0.3, 0.7));
vec.push_back(ColorM(0.2, 0.6, 0.3, 0.7));
vec.push_back(ColorM(0.3, 0.6, 0.3, 0.7));
I got copy constructor calls. What I doing wrong?
I have used this as example. And compile it with g++.
Here is QT project I used for my tests: http://wikisend.com/download/261514/MoveConstructor.zip
You can use std::vector<T>::emplace_back function
std::vector<ColorM> vec;
vec.emplace_back(0.1, 0.6, 0.3, 0.7); // Will call
vec.emplace_back(0.2, 0.6, 0.3, 0.7); // ColorM::ColorM(float _r, float _g, ..) ctor
vec.emplace_back(0.3, 0.6, 0.3, 0.7);
Or (use ColorM move constructor)
std::vector<ColorM> vec;
vec.emplace_back(ColorM(0.1, 0.6, 0.3, 0.7)); // Should be equivalent to your code
vec.emplace_back(ColorM(0.2, 0.6, 0.3, 0.7));
vec.emplace_back(ColorM(0.3, 0.6, 0.3, 0.7));
Note: I disagree with the answer by user2485710. Since the ColorM(0.1, 0.6, 0.3, 0.7) argument to push_back is a temporary, I expect the compiler to recognize it as an r-value reference and apply the move constructor, without using std::move, since push_back has an overload for r-value references.
In other words, the emplace_back taking a ColorM object should avoid copies like push_back; in the case of emplace_back, you will necessarily construct in place.
With the second version above, you should get better diagnostic messages compared to push_back, i.e. a failure at compile time instead of this subtle unexpected use of the copy constructor.
That's because of the signatures for your constructors and the argument given to your push_back() operation.
In a nutshell T() generates a new instance of an object of type T, if this instance it's not related to a label/variable, the instance remains unnamed, but his doesn't change its own type, it's always "something" of type T ( it could be even considered as T& or const T& but that's not the argument of this topic).
To reach for the T&& signature you need to cast to T&& and in C++11 this is a job for std::move which is also an unconditional kind of cast, so it's quite easy to use pretty much anywhere.
You can add std::move to your constructor inside your class or inside the push_back, your call.
Related
I'm wondering why I can't initialize an array with an integer index. In shadertoy it seems to work but it doesn't work when I use this pixel shader via three.js:
void main(void) {
vec2 p[1];
p[0] = vec2(0.0, 0.0); // works
int i = 0;
p[i] = vec2(0.0, 0.0); // doesn't work glsl doesn't run
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
Any ideas?
The issue is GLSL 1.0 only supports constant integer expressions for array axis or loops based on constant integer expressions.
See the spec
void main(void) {
vec2 p[1];
p[0] = vec2(0.0, 0.0); // works
int i = 0;
p[i] = vec2(0.0, 0.0); // doesn't work. i is not constant
const int j = 0;
p[j] = vec2(0.0, 0.0); // works
vec2 q[2];
for (int k = 0; k < 2; ++k) { // 2 is a constant int so this works
p[k] = vec2(0); // works
}
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
Note that the rules are complex. For example your code is ok in a vertex shader but not in a fragment shader. Except for arrays of samplers even in vertex shaders the index must follow the same restricted rules.
WebGL2 supports GLSL ES 3.00 which allows non-constant integer array access in more places.
Shadertoy optionally uses WebGL2 though it tries to do it auto-magically. You don't have to tell it your shader is using GLSL ES 3.0, it just guesses some how. Maybe it compiles the shader both ways and whichever one works is the one it uses. I have no idea, I just know it does support both.
THREE.js has a WebGL2 version
Given a toy struct with a default constructor like this one:
struct RGB {
unsigned char r, g, b;
RGB()
:r(0), g(0), b(0) {}
};
How do I initialise one to a specific colour, assuming I don't have access to the source code to add my own constructor.
I don't understand fully why these don't work:
// OK, I can sort-of accept this one
RGB red = {255, 0, 0};
// Not shorthand for green.r=0, green.g=255, green.b=0;?
RGB green = {.r = 0, .g = 255, .b = 0};
// I seem to be missing a constructor that accepts a list?
RGB blue{0, 0, 255};
Is there any other C++11 way to shorten the good old-fashioned:
RGB yellow;
yellow.r = 255;
yellow.g = 255;
yellow.b = 0;
Furthermore, how could I minimally modify the struct declaration to support any of the above, as well as having a default initialisation method.
If you have no possibility to add default arguments to the struct's constructor, how about a helper function:
RGB makeRGB(unsigned char r, unsigned char g, unsigned char b)
{
RGB result;
result.r = r;
result.g = g;
result.b = b;
return result;
}
Which can be used like so:
RGB red = makeRGB(255, 0, 0);
Return value optimization will take care of the temporary and provide a no-overhead solution unless you are using a terrible compiler.
The ideal solution would be modifying the default constructor to take optional arguments:
struct RGB {
unsigned char r, g, b;
explicit RGB(unsigned char r, unsigned char g, unsigned char b)
:r(r), g(g), b(b) {}
RGB() : RGB(0, 0, 0) {}
};
Which can be used like you would expect:
RGB red(255, 0, 0);
RGB green{0, 255, 0};
RGB blue;
blue.b = 255;
Live demo here.
Given
struct color
{
color(std::initializer_list<float> list) = delete;
float r, g, b;
};
color c = {1,2,3} and color c {1,2,3} are the same.
syntax like color c = {.r = 0, .g = 255, .b = 0}; is specific to C programming language, not C++.
color c = {1,2,3}; is perfectly fine in C++11. It is called aggregate initialization. And it does even override explicit constructor from initializer_list. Link describes how to explicitly delete it.
Constructor from initializer_list can be explicitly called by color c({1,2,3})
Current Error: vec4 has a user-defined constructor or non-trivial default constructor.
Hello,
I looked up a few things on this bug, by going into what a non-trivial default constructor is and got no where. The code is currently this
union
{
float elements[4 * 4];
vec4 columns[4];
};
To my current knowledge as long as i was to flag the constructor as default i would be fine which i did here.
vec4() = default;
vec4(const float& x, const float& y, const float& z, const float& w);
If anyone has any knowledge on what is going on here, or can help can help me reach a conclusion that would be great!
Thanks
Is there an easy way to add shadows in opengl-es 1.x? Or only in 2.0?
For projecting a shadow on a plane there's a simple way (not very efficient, but simple).
This function is not mine, I forget were I found it. What it does is create a matrix projection that maps everything you draw onto a single plane.
static inline void glShadowProjection(float * l, float * e, float * n)
{
float d, c;
float mat[16];
// These are c and d (corresponding to the tutorial)
d = n[0]*l[0] + n[1]*l[1] + n[2]*l[2];
c = e[0]*n[0] + e[1]*n[1] + e[2]*n[2] - d;
// Create the matrix. OpenGL uses column by column
// ordering
mat[0] = l[0]*n[0]+c;
mat[4] = n[1]*l[0];
mat[8] = n[2]*l[0];
mat[12] = -l[0]*c-l[0]*d;
mat[1] = n[0]*l[1];
mat[5] = l[1]*n[1]+c;
mat[9] = n[2]*l[1];
mat[13] = -l[1]*c-l[1]*d;
mat[2] = n[0]*l[2];
mat[6] = n[1]*l[2];
mat[10] = l[2]*n[2]+c;
mat[14] = -l[2]*c-l[2]*d;
mat[3] = n[0];
mat[7] = n[1];
mat[11] = n[2];
mat[15] = -d;
// Finally multiply the matrices together *plonk*
glMultMatrixf(mat);
}
Use it like this:
Draw your object.
glDrawArrays(GL_TRIANGLES, 0, machadoNumVerts); // Machado
Suply it with a light source position, a plane where the shadow will be projected and the normal.
float lightPosition[] = {383.0, 461.0, 500.0, 0.0}
float n[] = { 0.0, 0.0, -1.0 }; // Normal vector for the plane
float e[] = { 0.0, 0.0, beltOrigin+1 }; // Point of the plane
glShadowProjection(lightPosition,e,n);
Ok, shadow matrix is applied.
Change the drawing color to something that fits.
glColor4f(0.3, 0.3, 0.3, 0.9);
Draw your object again.
glDrawArrays(GL_TRIANGLES, 0, machadoNumVerts); // Machado
That is why this is not efficient, the more complex the object the more useless triangles you waste just for a shadow.
Also remember that every manipulation you made to the unshadowed object needs to be done after the shadow matrix is applied.
For more complex stuff the subject is a bit broad, and depends a lot on your scene and complexity.
Projective texture mapped shadows like they were done with OpenGL-1.2 without shaders are possible. Look for older shadow mapping tutorials, written between 1999 and 2002.
If...
vec3 myVec3 = vec3(1.0, 0.0, 0.5); // myVec3 = {1.0, 0.0, 0.5}
vec3 temp = vec3(myVec3); // temp = myVec3
vec2 myVec2 = vec2(myVec3); // myVec2 = {myVec3.x, myVec3.y}
myVec4 = vec4(myVec2, temp, 0.0); // myVec4 = {myVec2.x, myVec2.y, temp.x, 0.0}
Then what does the following represent?
myVec4 = vec4(temp, myVec2, 0.0); // myVec4 =
Thanks .
If temp is indeed a vec3 as you’ve defined, both of the constructors for myVec4 are illegal, as both contain enough components in the first two arguments to initialize the entire vec4.
The way I would figure stuff like that out, assuming it compiles and runs, it to use the debugger or printf to see what you get.
On my xode 3.2.x - It does not compile. In fact vec2 myVec2 = vec2(myVec3); also does not compile.
Also: last line has an error which makes sense when you read it.
code.mm:73:0 code.mm:73: error: no matching function for call to
'Vector4<float>::Vector4(vec3&, vec2&, double)'
I have always found the constructor rules for C++ to be pretty complex. Let the compiler tell it like it is.