Sign in to follow this  
noodleBowl

C++ Scope of local variables and their destructors

Recommended Posts

Its been a while since I have done C++ and I was wondering if someone could help me understand local variables and what happens they go out of scope

So I have this method for creating a vertex shader and it returns a custom class called VertexShader. But before it returns the VertexShader it places it into a std::unsorted_map called vertexShaders

//Declared at the ShaderModule class level. Map to store VertexShaders in
std::unordered_map<LPCSTR, VertexShader> vertexShaders;

//Method to create a vertex shader. Returns a VertexShader
VertexShader ShaderModule::createVertexShader(LPCWSTR fileName, LPCSTR id, LPCSTR entryPoint, LPCSTR targetProfile)
{
	ID3DBlob *shaderCode = compileShaderFromFile(fileName, entryPoint, targetProfile);

	D3D11_INPUT_ELEMENT_DESC inputElementDescription[] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};

	VertexShader vertexShader(id); //Create a new VertexShader
	device->CreateVertexShader(shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), NULL, &vertexShader.shader);
	device->CreateInputLayout(inputElementDescription, 2, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), &vertexShader.inputLayout);
	shaderCode->Release();

	vertexShaders[id] = vertexShader; //Place into the VertexShader map. Places copy?
	return vertexShader;
}

Now at the end of the method, right as the return is done, I see that the destructor for my variable vertexShader gets called. And this might sound ridiculous, but I really dont understand why?

Shouldn't it live on in memory since it was placed into the vertexShaders map and only when the vertexShaders map is destroyed then the deconstructor for my VertexShader is called?  By doing vertexShaders[id] = vertexShader am I really just making a copy and then eventually another deconstructor for the VertexShader placed into the map will be called?

 

Also when using the method the destructor is called twice? VertexShader being returned from the method now also getting destroyed?

//Declaration of the vertex shader. Done at the class level
VertexShader vShader;

//Method that uses the createVertexShader
void GraphicsSystem::initialize(HWND windowHandle)
{
	/* Other init code*/

	vShader = shaderModule.createVertexShader(L"default.shader", "VertexShader", "VShader", "vs_4_0");
}

 

Edited by noodleBowl

Share this post


Link to post
Share on other sites

Placing your variable into the std map dose not give it a scope as it does in C# for example. C++ dosent know if an object is in scope or out of it, this would be the task for a memory/garbadge collector system you would have to write by yourself. This line

VertexShader vertexShader(id); //Create a new VertexShader

creates a local variable of type VertexShader on the stack that lives as long as you remain inside stack scope. And when calling

return vertexShader;

it returns a new copy of your stack scoped variable to the recipient that called your setup function and after that cleans up the stack by removing your local instance from memory. The same is true for your unordered map since it isnt a pointer, the insert function of the std map creates a copy of your object inside the memory std map manages.

The only chance you have here is to use either some kind of ref pointer that takes responsability for keeping your instance alive as long as there are references to it alive or use a raw pointer so you need to take responsability to delete it after you were done so memory could be released properly (destructor wont be called since delete). Otherwise you would have two different copies and in the most anyoing case (as yours occuring) it will happen that your class gest destructed and data you previously attached gets lost

Edited by Shaarigan

Share this post


Link to post
Share on other sites
2 hours ago, Shaarigan said:

it returns a new copy of your stack scoped variable to the recipient that called your setup function and after that cleans up the stack by removing your local instance from memory.

The copy is not guaranteed due to C++'s Return Value Optimization.

2 hours ago, Shaarigan said:

class gest destructed

The object or class instance gets destructed, not the class itself.

3 hours ago, noodleBowl said:

it returns a custom class called VertexShader

Idd.: An object or class instance is returned, not the class itself.

3 hours ago, noodleBowl said:

By doing vertexShaders[id] = vertexShader am I really just making a copy and then eventually another deconstructor for the VertexShader placed into the map will be called?

Yes, the copy constructor is used resulting in a copy of vertexShader.

Share this post


Link to post
Share on other sites
4 hours ago, noodleBowl said:

Also when using the method the destructor is called twice?

Take a look at the Return Value Optimization example on Wikipedia. It is possible that the copy assignment operator is invoked twice (which also implies that the destructor is invoked twice). Although, I really doubt that any C++11/14 compiler would use a copy assignment in the presence of move semantics?

Edited by matt77hias

Share this post


Link to post
Share on other sites
2 hours ago, Shaarigan said:

it returns a new copy of your stack scoped variable to the recipient that called your setup function and after that cleans up the stack by removing your local instance from memory. The same is true for your unordered map since it isnt a pointer, the insert function of the std map creates a copy of your object inside the memory std map manages.

The only chance you have here is to use either some kind of ref pointer that takes responsability for keeping your instance alive as long as there are references to it alive or use a raw pointer so you need to take responsability to delete it after you were done so memory could be released properly (destructor wont be called since delete). Otherwise you would have two different copies and in the most anyoing case (as yours occuring) it will happen that your class gest destructed and data you previously attached gets lost

Thats not the case in modern C++ (>11) anymore. With a proper move-constructor/assignment operator, instead of creating a copy it will move the data of the local "vertexShader" variable into the target on the callers side. If you define VertexShader as non-copyable (deleted copy- ctor/assignment-operator) then you can make sure this is happening all the time, even before the recent http://en.cppreference.com/w/cpp/language/copy_elision. (which is different to RVO; but probably won't happen if you assign to a global like in the example-code)

However, one place this code certainly does create a copy is this:

4 hours ago, noodleBowl said:

vertexShaders[id] = vertexShader;

So the code as shown still is flawed in this way. But, you could easily do:

VertexShader& ShaderModule::createVertexShader(LPCWSTR fileName, LPCSTR id, LPCSTR entryPoint, LPCSTR targetProfile) //  returns reference
{
	ID3DBlob *shaderCode = compileShaderFromFile(fileName, entryPoint, targetProfile);

	D3D11_INPUT_ELEMENT_DESC inputElementDescription[] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};
  
    VertexShader& vertexShader = vertexShaders.emplace(fileName, id).first->second; // construct the shader inside the map => might need to adjust the call at the end, not sure about the iterator-syntax

	device->CreateVertexShader(shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), NULL, &vertexShader.shader);
	device->CreateInputLayout(inputElementDescription, 2, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), &vertexShader.inputLayout);
	shaderCode->Release();

	return vertexShader;
}

 

Share this post


Link to post
Share on other sites
Just now, matt77hias said:

A reference as data element in the collection?

Eh, forget that part, I had a brainfart and assumed std::unique_ptr would reallocate elements as it grows. See my edit, thats how I would do it.

Still I don't quite get your question. What i indendet was:

std::unordered_map<LPCSTR, std::unique_ptr<VertexShader>> vertexShaders;

But since (I hope I've got it right now that) its safe to keep a reference to an element in an unordered_map, thats unnecessary.

Share this post


Link to post
Share on other sites

As a side note:

You can use a create a resource manager that constructs instances of a subclass of VertexShader (which should have a virtual destructor) by using variadic templates to pass the constructor arguments to the base class VertexShader, and that stores weak pointers to them. That way the resource manager is kind of notified on deletion.

Take a look at: SharedresourcePool in the DirectXTK.

Share this post


Link to post
Share on other sites
 

Let me see if I have this right

// VertexShader class
class VertexShader 
{
  public:
  LPCSTR id;
  VertexShader() {
  }
  VertexShader(LPCSTR id) {
    this->id = id;
  }
  ~VertexShader() {
  }
};

//Test method
VertexShader ShaderModule::test() {
  //Create a VertexShader instance in local scope called shaderLocal
  VertexShader shaderLocal("localScope");
  
  return shaderLocal; //Copy shaderLocal to send back with the return (Send_Back_Copy). shaderLocal's destructor is called
}

//Main method
void main() {
  //Call the test method
  VetexShader shade = shaderModule.test(); //Send_Back_Copy from return is copied and saved into shade. Sent_Back_Copy's destructor is called
}
//shade's destructor is called after main exits

Is that is what is happening? Or do I have this totally wrong?

Edited by noodleBowl

Share this post


Link to post
Share on other sites

Did you test to see whats happening? Eighter with a debugger or std::cout/printf-statements?

As has been pointed out, with full optimizations no copies or destructions should be happen here at all.

In any case:

8 minutes ago, noodleBowl said:

VetexShader shade = shaderModule.test(); //Send_Back_Copy from return is copied and saved into shade. Sent_Back_Copy's destructor is called

This is not happening, due to copy elision the temporary returned by "test()" is considered non-existant and instead the returned content is placed direclty into the "shade" variable. So this line should read:

VetexShader shade = shaderModule.test(); // no copy-ctor or destructor called at all here

 

Share this post


Link to post
Share on other sites

Try this:

#include <iostream>

struct Widget {
    
    Widget() : m_data(0) {
        std::cout << "Constructor" << std::endl;
    }
    Widget(const Widget &widget) : m_data(widget.m_data) {
        std::cout << "Copy Constructor" << std::endl; 
    }
    Widget(Widget &&widget) : m_data(widget.m_data) {
        std::cout << "Move Constructor" << std::endl; 
    }
    ~Widget() {
        std::cout << "Destructor" << std::endl; 
    }
    Widget &operator=(const Widget &widget) {
        std::cout << "Copy Assignment Operator" << std::endl; 
        m_data = widget.m_data;
        return *this;
    }
    Widget &operator=(Widget &&widget) {
        std::cout << "Move Assignment Operator" << std::endl; 
        m_data = widget.m_data;
        return *this;
    }
    
    int m_data;
};

const Widget Foobar() noexcept {
    std::cout << "Foobar" << std::endl; 
    const Widget widget;
    return widget;
}

int main() {
   std::cout << "Begin" << std::endl;
   Widget widget = Foobar();
   std::cout << "End" << std::endl;
}

Visual Studio C++ Web Compiler

Begin
Foobar
Constructor

Copy Constructor // presume Debug mode
End
Destructor

(GCC?) C++ Web Compiler

C++14:

Begin
Foobar
Constructor
End
Destructor

C++11:

Begin
Foobar
Constructor
End
Destructor

C++98 (without move semantics and noexcept):

Begin
Foobar
Constructor
End
Destructor

All due to RVO (Return Value Optimization) which already existed before introducing move semantics.

Edited by matt77hias

Share this post


Link to post
Share on other sites

Alright so I was able to go back and do some proper testing. And I got some questions

Now using my original code (VertexShader ShaderModule::createVertexShader) and placing std::out inside the constructors and destructors, I get this as the output

CREATED
Game constructor called
Vertex Shader constructor called. Id: VertexShader
Vertex Shader constructor called. No id
Vertex Shader destructor called. Id: VertexShader
Shader destructor called...
Vertex Shader destructor called. Id: VertexShader
Shader destructor called...
Vertex Shader Id Changed! id: BEFORE-PIXEL-SHADER

I still do not understand why the destructor is called twice??

//======== Shader.h
class Shader
{
public:
	LPCSTR id;

protected:
	Shader();
	Shader(LPCSTR id);
	~Shader();
};

//======= Shader.cpp
Shader::Shader()
{
}

Shader::Shader(LPCSTR id)
{
	this->id = id;
}

Shader::~Shader()
{
	std::cout << "Shader destructor called..." << std::endl;
}

//============= VertexShader.h
class VertexShader : public Shader
{
public:
	VertexShader();
	VertexShader(LPCSTR id);
	~VertexShader();
	ID3D11VertexShader *shader;
	ID3D11InputLayout *inputLayout;
};

//======== VertexShader.cpp
VertexShader::VertexShader()
{
	std::cout << "Vertex Shader constructor called. No id" << std::endl;
}

VertexShader::VertexShader(LPCSTR id) : Shader(id)
{
	std::cout << "Vertex Shader constructor called. Id: " << id << std::endl;
}

VertexShader::~VertexShader()
{
	std::cout << "Vertex Shader destructor called. Id: " << id << std::endl;
}

//====== In Shader Module
VertexShader ShaderModule::createVertexShader(LPCWSTR fileName, LPCSTR id, LPCSTR entryPoint, LPCSTR targetProfile)
{
  /*Compile the shader code and setup the input description*/

	VertexShader vertexShader(id); //First constructor call. Arg constructor where id is VertexShader
	device->CreateVertexShader(shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), NULL, &vertexShader.shader);
	device->CreateInputLayout(inputElementDescription, 2, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), &vertexShader.inputLayout);
	shaderCode->Release();

	vertexShaders[id] = vertexShader; //Second constructor called. No arg constructor. Because of copy into map?
	return vertexShader; //First destructor called. Assumed destructor of vertexShader as id is VertexShader. Base Shader destructor called too
}

//======== In Graphics System
void GraphicsSystem::initialize(HWND windowHandle)
{
	createDirectXSystem(windowHandle);
	createDirectXBackBuffer();
	createDirectXViewport(800, 600);
	
	shaderModule.initialize(device, deviceContext);
	rendererModule.initialize(device, deviceContext);

    //vShader is a private member of GraphicsSystem. Declared in GrpahicsSystem.cpp as: VertexShader vShader;
	vShader = shaderModule.createVertexShader(L"default.shader", "VertexShader", "VShader", "vs_4_0");
    //After the createVertexShader method runs and the VertexShader is returned Shader another destructor is hit?
  
	vShader.id = "BEFORE-PIXEL-SHADER";
	std::cout << "Vertex Shader Id Changed! id:" << vShader.id << std::endl;
	pShader = shaderModule.createPixelShader(L"default.shader", "PixelShader", "PShader", "ps_4_0");
}

 

Especially since the same ideology is used here and no double destructor occurs. Just like what you guys mentioned would happen

//Executed Output:
Constructor called. Id: localScope
Default constructor called. No id value.
Destructor called. Id: localScope
Complete!
Destructor called. Id: newId
Destructor called. Id: MapId
  

// VertexShader class
class VertexShader
{
public:
	LPCSTR id;
	VertexShader() {
		std::cout << "Default constructor called. No id value." << std::endl;
	}
	VertexShader(LPCSTR id) {
		this->id = id;
		std::cout << "Constructor called. Id: " << id << std::endl;
	}
	~VertexShader() {
		std::cout << "Destructor called. Id: " << id << std::endl;
	}
};

std::unordered_map<LPCSTR, VertexShader> shaderMap;

//Test method
VertexShader test() {
	//Create a VertexShader instance in local scope called shaderLocal
	VertexShader shaderLocal("localScope");
	shaderMap["MapId"] = shaderLocal;
	shaderMap["MapId"].id = "MapId";

	return shaderLocal; //Copy shaderLocal to send back with the return (Send_Back_Copy). shaderLocal's destructor is called
}

//Main method
void main() {
	//Call the test method
	VertexShader shade = test();
	//No destructor call happened?

	shade.id = "newId";
	std::cout << "Complete!" << std::endl;
}
//shade's destructor is called after main exits
//The VertexShader in the map has its destructor called

Both of the above were executed in debug mod and stepped through

 

When it comes including header files, if a class of the header file has a static member(s) and I have ShaderModule GraphicsSystem::shaderModule; in the implementation file. I am creating a instance right then and there aren't I?

//Inside GraphicsSystem.h
class GraphicsSystem {
public:
	GraphicsSystem();
	~GraphicsSystem();
	static ShaderModule shaderModule;

	/* Other methods and variables */

private:
	/* Other methods and variables */
};

//In implmentation file
ShaderModule GraphicsSystem::shaderModule; //Creaing instance here? See constructor being called

//Main program
#include "GraphicsSystem.h"

void main()
{
	std::cout << "Started the program..." <<std::endl;
}

 

Edited by noodleBowl

Share this post


Link to post
Share on other sites

Your code is only telling part of the story. Add the copy and move constructor, copy and move assignment operator as well. They will be equal to the default, but you can add some printing as well.

BTW: the content in your map is destructed as well in your second code listing.

Edited by matt77hias

Share this post


Link to post
Share on other sites
20 hours ago, matt77hias said:

Your code is only telling part of the story. Add the copy and move constructor, copy and move assignment operator as well

So I went back and added those parts

Output:

CREATED
Input Module constructor called!
Game constructor called!
(1) Vertex Shader constructor called. Id: VertexShader
(2) Vertex Shader constructor called. No arg constructor
(3) Vertex Shader copy assignment called. Id: VertexShader-COPY_ASSIGN | Other Id: VertexShader
(4) Vertex Shader move constructor called. Id: VertexShader-MOVE_CON | Other Id: VertexShader
(5) Vertex Shader destructor called. Id: VertexShader
    Shader destructor called...
(6) Vertex Shader move assignment called. Id: VertexShader-MOVE_CON-MOVE_ASSIGN | Other Id: VertexShader-MOVE_CON
(7) Vertex Shader destructor called. Id: VertexShader-MOVE_CON
    Shader destructor called...
    Vertex Shader Id Changed! id:BEFORE-PIXEL-SHADER

Code break down:

//In ShaderModule.cpp
VertexShader ShaderModule::createVertexShader(LPCWSTR fileName, LPCSTR id, LPCSTR entryPoint, LPCSTR targetProfile)
{
	/*Other shader code needed for compiling the shader and etc */

	//(1) Create a VertexShader instance using id: VertexShader
	VertexShader vertexShader(id);

	device->CreateVertexShader(shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), NULL, &vertexShader.shader);
	device->CreateInputLayout(inputElementDescription, 2, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), &vertexShader.inputLayout);
	shaderCode->Release();

	//(2) When vertexShaders[id], the operator[] triggers the creation of a VertexShader instance using the no arg constructor and its placed into the map
	//(3) the operator= triggers the copy assignment operator function of VertexShader. Where the variable vertexShader is the variable being copied. The copy replaces the value that was created in (2)
	vertexShaders[id] = vertexShader;

	//(4) On the return, a move constructor is called. I do not understand why? I assume its in preparation to return a copy of the variable vertexShader
	//(5) The destructor for the local scope variable vertexShader is called
	return vertexShader;
}

//In GraphicsSystem.cpp
void GraphicsSystem::initialize(HWND windowHandle)
{
	createDirectXSystem(windowHandle);
	createDirectXBackBuffer();
	createDirectXViewport(800, 600);	
	shaderModule.initialize(device, deviceContext);

	//Numbers 6 & 7 AFTER execution of the createVertexShader method. Once its returns
	//(6) the operator= triggers the move assignment operator function
	//(7) After (6) is completed, the destructor for the VertexShader instance the move constructor created is called
	vShader = shaderModule.createVertexShader(L"default.shader", "VertexShader", "VShader", "vs_4_0");

	vShader.id = "BEFORE-PIXEL-SHADER";
	std::cout << "Vertex Shader Id Changed! id:" << vShader.id << std::endl;
	pShader = shaderModule.createPixelShader(L"default.shader", "PixelShader", "PShader", "ps_4_0");
}

So for the most part it makes sense, but I do not understand the move constructor. Is that what just happens when you return an instance? A clone is made with the move constructor. What does a move constructor actually do / what's the purpose?

Edited by noodleBowl

Share this post


Link to post
Share on other sites
7 hours ago, noodleBowl said:

vertexShaders[id] = vertexShader;

As you noticed correctly, std::map::operator[] first creates a default Value if no Value is associated with the given Key. This results in unnecessary overhead (default constructor and copy assignment operator). Therefore, a better alternative is to use std::map::insert instead which will only invoke a move constructor.

Edited by matt77hias

Share this post


Link to post
Share on other sites
7 hours ago, noodleBowl said:

//(4) On the return, a move constructor is called. I do not understand why? I assume its in preparation to return a copy of the variable vertexShader //(5) The destructor for the local scope variable vertexShader is called

These two, (4) and (5), puzzle me as well.

(6) and (7) completely handle the passing of the returned VertexShader and the destruction of the local one.

(4) and (5) are basically no-ops in this context which just add another indirection by constructing some temporary out of the local variable and destructing that local variable. Maybe this is for correctness since you changed global state in your move constructor by writing to stdout? I find this strange since there is the Copy Elision optimization to avoid the unnecessary copying of objects.

7 hours ago, noodleBowl said:

A clone is made with the move constructor. What does a move constructor actually do / what's the purpose?

The move constructor does actually not clone something. If you clone a Widget, you have the original Widget and the clone of that Widget which both will "look" (depending on your implementation) the same while both still exist at the same time. A move constructor rather swaps the contents of the newly constructed Widget and the (rvalue) argument Widget (passed as rvalue reference). For more information: see this blog post for instance.

As a side note: To make things even more tricky, one uses the std::move method to cast to an rvalue reference: just a cast, not the actual movement of data.

Edited by matt77hias

Share this post


Link to post
Share on other sites
6 hours ago, matt77hias said:

These two, (4) and (5), puzzle me as well.

(6) and (7) completely handle the passing of the returned VertexShader and the destruction of the local one.

(4) and (5) are basically no-ops in this context which just add another indirection by constructing some temporary out of the local variable and destructing that local variable. Maybe this is for correctness since you changed global state in your move constructor by writing to stdout? I find this strange since there is the Copy Elision optimization to avoid the unnecessary copying of objects.

Contrary to what I originally hinted at and belived at the time, copy elision does not work on named temporaries. So without RVO, thats exactly what should be happening here.

Note that there's also a slight, but important misconception here: copy ellision is NOT an optimization, its a guarantee towards the compiler to entirely omit anything related to copying/moving a value. Thats means, under copy/move ellision, there doesn't even need to be a valid copy/move-ctor (can be deleted or inacessessible); and any compiler that assumes otherwise is broken.
Likewise, the difference between returning by value pre-c++ 11 and post-c++11 is important as well - with move-semantics, you can legitametly return a non-copyable class that has a valid move-ctor, while without move-semantics, you cannot do that; since RVO is an optional optimization that may or may not be present - thus a return-statement must be valid even without it present. With move-semantics, you can define classes as non-copyable and still return them by value; and you also have the guarantee to not have any copyies made i.e. in debug-builds.

Share this post


Link to post
Share on other sites
11 minutes ago, Juliean said:

Note that there's also a slight, but important misconception here: copy ellision is NOT an optimization, its a guarantee towards the compiler to entirely omit anything related to copying/moving a value.

Wikipedia defines it as an optimization (haven't had time to dig into the ISO book).

Furthermore, you define RVO as an optimization whereas (regular and named) RVO is one Copy Ellision technique.

Edited by matt77hias

Share this post


Link to post
Share on other sites
6 hours ago, matt77hias said:

(4) and (5) are basically no-ops in this context which just add another indirection by constructing some temporary out of the local variable and destructing that local variable. Maybe this is for correctness since you changed global state in your move constructor by writing to stdout? I find this strange since there is the Copy Elision optimization to avoid the unnecessary copying of objects.

[...] in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value.

Ok, the RVO is optional apparently. There is proposal for guaranteed Copy Ellision. This is also an interesting article about Copy Ellision.

@noodleBowl Are you using -fno-elide-constructors in Clang/GCC?

Edited by matt77hias

Share this post


Link to post
Share on other sites
6 minutes ago, matt77hias said:

Wikipedia defines it as an optimization (haven't had time to dig into the ISO book).

Appearently there are different meanings for copy ellision, you are right, the one I was refering to was the recent one, from the top of the cppreference-ink:
 

Quote

 

Under the following circumstances, the compilers are required to omit the copy- and move- construction of class objects even if the copy/move constructor and the destructor have observable side-effects. They need not be present or accessible, as the language rules ensure that no copy/move operation takes place, even conceptually:

  • In initialization, if the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object:

T x = T(T(T())); // only one call to default constructor of T, to initialize x
  • In a function call, if the operand of a return statement is a prvalue and the return type of the function is the same as the type of that prvalue.

T f() { return T{}; }
T x = f();         // only one call to default constructor of T, to initialize x
T* p = new T(f()); // only one call to default constructor of T, to initialize *p

Note: the rule above does not specify an optimization: C++17 core language specification of prvalues and temporaries is fundamentally different from that of the earlier C++ revisions: there is no longer a temporary to copy/move from.

 

 

9 minutes ago, matt77hias said:

Furthermore, you define RVO as an optimization whereas RVO is one Copy Ellision technique.

 

3 minutes ago, matt77hias said:

Ok, the RVO is optional apparently.

Seems you're right about that one, but yeah, as you see the compiler is only permitted to ignore the copy/move, while in the case of the new c++17 rule I was refering too, it is required to do so, making the following code legal (that was previously not):

struct NonCopyable
{
	NonCopyable()
    {
    }

    NonCopyable(const NonCopyable&) = delete;
    void operator=(const NonCopyable&) = delete;

    NonCopyable(NonCopyable&&) = delete;
    void operator=(NonCopyable&&) = delete;
}

NonCoypable factory(void)
{
    return NonCoypable(); // pre-c++17, this does not compile.
}

Its suprisingly hard to determine whats what here, but I think its important to realize that there are different things going on here conceptually.

Share this post


Link to post
Share on other sites

@matt77hias

I'm using visual studio 2017. My toolest is Visual Studio 2017 (v141) and the Windows SDK version is 10.0.14393.0

I'm just running the default project setup (empty project), where my build is x86 debug, so I assume there are no optimizations

I also when back and tested insert and emplace. Emplace and operator[] in terms of move/copy/etc look the same. Insert though seems to be doing a lot more work

Same code as the previous post, except the adding to the map part was changed to

//For insert method
std::pair<std::unordered_map<LPCSTR, VertexShader>::iterator, bool> result = vertexShaders.insert(std::pair<LPCSTR, VertexShader>(id, vertexShader));
if (result.second == false)
	std::cout << "key was already in use" <<std::endl;

//For emplace method
std::pair<std::unordered_map<LPCSTR, VertexShader>::iterator, bool> result = vertexShaders.emplace(id, vertexShader);
if (result.second == false)
	throw std::exception("key already used");

Output for when using insert:

CREATED
Input Module constructor called!
Shader Module constructor called!
Shader constructor called. No arg
Vertex Shader constructor called. No arg constructor
Graphics System constructor called!
Game constructor called!
Shader Module initing...
Shader constructor called. id: VertexShader
Vertex Shader constructor called. Id: VertexShader
Shader constructor called. No arg
Vertex Shader copy constructor called. Id: VertexShader-COPY-CON Other Id: VertexShader
Shader constructor called. No arg
Vertex Shader move constructor called. Id: VertexShader-COPY-CON-MOVE_CON Other Id: VertexShader-COPY-CON
Vertex Shader destructor called. Id: VertexShader-COPY-CON
Shader destructor called...
Shader constructor called. No arg
Vertex Shader move constructor called. Id: VertexShader-MOVE_CON Other Id: VertexShader
Vertex Shader destructor called. Id: VertexShader
Shader destructor called...
Vertex Shader move assignment called. Id: VertexShader-MOVE_CON-MOVE_ASSIGN Other Id: VertexShader-MOVE_CON
Vertex Shader destructor called. Id: VertexShader-MOVE_CON
Shader destructor called...

Output for when using emplace

CREATED
Input Module constructor called!
Shader Module constructor called!
Shader constructor called. No arg
Vertex Shader constructor called. No arg constructor
Graphics System constructor called!
Game constructor called!
Shader Module initing...
Shader constructor called. id: VertexShader
Vertex Shader constructor called. Id: VertexShader
Shader constructor called. No arg
Vertex Shader copy constructor called. Id: VertexShader-COPY-CON Other Id: VertexShader
Shader constructor called. No arg
Vertex Shader move constructor called. Id: VertexShader-MOVE_CON Other Id: VertexShader
Vertex Shader destructor called. Id: VertexShader
Shader destructor called...
Vertex Shader move assignment called. Id: VertexShader-MOVE_CON-MOVE_ASSIGN Other Id: VertexShader-MOVE_CON
Vertex Shader destructor called. Id: VertexShader-MOVE_CON
Shader destructor called...

Output for when using operator[]

CREATED
Input Module constructor called!
Shader Module constructor called!
Shader constructor called. No arg
Vertex Shader constructor called. No arg constructor
Graphics System constructor called!
Game constructor called!
Shader Module initing...
Shader constructor called. id: VertexShader
Vertex Shader constructor called. Id: VertexShader
Shader constructor called. No arg
Vertex Shader constructor called. No arg constructor
Vertex Shader copy assignment called. Id: VertexShader-COPY_ASSIGN Other Id: VertexShader
Shader constructor called. No arg
Vertex Shader move constructor called. Id: VertexShader-MOVE_CON Other Id: VertexShader
Vertex Shader destructor called. Id: VertexShader
Shader destructor called...
Vertex Shader move assignment called. Id: VertexShader-MOVE_CON-MOVE_ASSIGN Other Id: VertexShader-MOVE_CON
Vertex Shader destructor called. Id: VertexShader-MOVE_CON
Shader destructor called...

 

Edited by noodleBowl

Share this post


Link to post
Share on other sites
6 hours ago, noodleBowl said:

I'm just running the default project setup (empty project), where my build is x86 debug, so I assume there are no optimizations

Maybe you're right and need a Release configuration. You can also try Project Properties > C/C++ > Language > C++ Language Standard > C++17 or C++latest to see if this makes any differences.

Share this post


Link to post
Share on other sites
20 hours ago, matt77hias said:

Maybe you're right and need a Release configuration. You can also try Project Properties > C/C++ > Language > C++ Language Standard > C++17 or C++latest to see if this makes any differences.

Looking at my project that option is left blank. Personally I only have std:c++14 or std:c++latest available in the drop down. Seeing as blank is really a poor description of what standard I'm using I dug around in the Microsoft docs and found what they put as the default language standard version. According to that page visual studio 2017 uses std:c++14 by default

Switching to std:c++latest it does not seem do anything different when comparing the output of the operator[]

Now when I switch to a Release build  (language standard is left blank) things get interesting because my output for creating my VertexShader becomes:

Shader Module initing...
Shader constructor called. id: VertexShader
Vertex Shader constructor called. Id: VertexShader
Shader constructor called. No arg
Vertex Shader constructor called. No arg constructor
Vertex Shader copy assignment called. Id: VertexShader-COPY_ASSIGN Other Id: VertexShader
Vertex Shader move assignment called. Id: VertexShader-MOVE_ASSIGN Other Id: VertexShader
Vertex Shader destructor called. Id: VertexShader

Which is way more cleaner then the previous debug outputs I have been posting, so it seems a release build will definitely do some heavy lifting

Other then that thanks everyone for helping me out. This was a much needed refresher

 

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Forum Statistics

    • Total Topics
      627770
    • Total Posts
      2979002
  • Similar Content

    • By Mk_
      Hi!
      I'm toying with different ideas about how to script game logic. I'm well aware of different scripting languages, and note that most games use these to define game logic.
      But what about using C/C++ in DLLs to script a game?
      For simplicities sake, lets assume I'm just writing quest logic. Why not just create a metafile that lists all quests. The main .exe loads this list (and creates a bunch of function pointers) and then programmatically queries a bunch of DLLs for the game logic. My idea is to create a bunch of DLLs (for example DLL1.dll, DLL2.dll, DLL3.dll etc.) and resolve the function pointers from last to first (DLL3.dll to DLL1.dll), which allows code in higher numbered DLLs to supersede existing code in lower numbered DLLs. New code would be entered into higher numbered empty/nearly empty DLLs until finalised, and then be moved into DLL1.dll. This allows for very short compilation times, with the advantage of fully compiled logic.
      This seems so simple I'm sure I must be overlooking something!
      Does anyone have any views on my approach, especially it's short fallings?
       
    • By 7th_Continuum
      I'm trying to implement a frictional constraint using position based dynamics, but it is only half working. I am using the formulation in the paper "Unified Particle Physics for Real-Time Applications".
      Here is my implementation:
      Particle *np = particle->nbs[j].neighbour; vec3 r = particle->x - np->x; float r_length = glm::length(r); float distDiff = r_length - restDistance; if(distDiff >= 0 ) continue; //Frictional Constraint vec3 n = r/r_length; vec3 xxi = particle->x - xi[particle->getIndex()]; vec3 xxj = np->x - xi[np->getIndex()]; vec3 tangentialDisplacement = (xxi - xxj) - glm::dot(xxi - xxj, n) * n; float td_length = glm::length(tangentialDisplacement); float genMass = ( imass / (imass + imass) ); if(td_length < (staticFriciton * distDiff)){ particle->x += genMass * tangentialDisplacement; np->x += -genMass * tangentialDisplacement; }else{ float upper = kineticFriction * distDiff; particle->x += genMass * tangentialDisplacement * std::min(upper/td_length, 1.f); np->x += -genMass * tangentialDisplacement * std::min(upper/td_length, 1.f); }  
    • By ScyllaBus
      Using my loop based on this: https://gafferongames.com/post/fix_your_timestep/
      Trying to get my game to run at fixed 60FPS (both update and render) for all machines. Studied the link above and have been stuck on this game loop for weeks trying to get it to work smoothly to glide this image across the screen. I had dealt constantly with jittering and possible tearing. I can't recall what I did to fix it exactly, but I believe it may have something to do with not rounding a variable properly (such as delta).
       
      So yeah, currently the loop works but I'm afraid as I develop the game more and have to render more, eventually something I'm doing in my loop could cause slowdowns or larger CPU usage. Does the structure of the game loop below seem okay or is there something I can do to optimize it?
      The 2D game is a generic sidescroller. Not too heavy on physics, mainly just simple platformer physics. I feel as though I'm using way too much CPU.
       
      void Game::mainLoop() { double fps = 60.0f; int frameSkip = 5; int deltaSkip = frameSkip; double miliPerFrame = 1000.0 / fps; double xx = 0.0f; double playSpeed = 5; Uint64 previous = SDL_GetPerformanceCounter(); double accumulator = 0.0f; bool shouldRender = false; bool running = true; while(running){ Uint64 current = SDL_GetPerformanceCounter(); double elapsed = (current-previous) * 1000; elapsed = (double) (elapsed / SDL_GetPerformanceFrequency() ); previous = current; // handleEvents() handleEvents(); // when we press escape reset x to 0 to keep testing // when he goes off screen if(key_states[SDL_SCANCODE_ESCAPE]) xx = 0; accumulator+=elapsed; if(accumulator >= miliPerFrame * frameSkip) accumulator = 0; shouldRender = accumulator >= miliPerFrame; while(accumulator >= miliPerFrame){ // update() //cout << playSpeed << endl; double delta = ceil(elapsed); if(delta > deltaSkip) delta = 1; //if(elapsed >= 1) delta = elapsed; xx+= playSpeed * delta;// * (1 / fps); // /update() accumulator -= miliPerFrame; //get what's left over } if(shouldRender){ // render() SDL_SetRenderDrawColor(gameRenderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(gameRenderer); imageController.drawImage("colorkeytest", floor(xx), 0); SDL_RenderPresent(gameRenderer); // /render() } } }  
    • By SR D
      I've been learning how to do vertex buffers plus index buffers using Ogre, but I believe this is mostly the same across several engines. I have this question about using vertex buffers + index buffers.
      Using DynamicGeometryGameState (from Ogre) as an example, I noticed that when drawing the cubes, they were programmatically drawn in order within the createIndexBuffer() function like so ...
       
      const Ogre::uint16 c_indexData[3 * 2 * 6] = { 0, 1, 2, 2, 3, 0, //Front face 6, 5, 4, 4, 7, 6, //Back face 3, 2, 6, 6, 7, 3, //Top face 5, 1, 0, 0, 4, 5, //Bottom face 4, 0, 3, 3, 7, 4, //Left face 6, 2, 1, 1, 5, 6, //Right face };
      From the above, the front face is drawn using the vertices 0, 1, 2, 2, 3, 0. But when reading in thousands of vertices from a file, one obviously doesn't code an array specifying which vertices make up a face.
      So how is this done when working with a large number of vertices?
    • By Josheir
      I am working on a SFML c++ program that uses two rendering windows passed from main to the function drawmessage in a class textmessage.  I was using the second window for displaying debug information that is displayed because I could not get the appropriate information from the SFML object.
      With that said, here is the part of that function that works the first time through and does not on the second usage.  I really have changed the code to try and get it working.   For example I created the two objects locally here for testing.  I am sorry about the extra commented statements they help convey the message too.
      There is the same problem though, the statement :     string test =     message_holder10.getString(); is working and shows "asty" on every run.  On the first run of the program there is a display of the text correctly however on the second call there is no display of it.  (I am stepping through until the display command.)
      I feel like I have exhausted my tries so I am asking for help please.
      If it is the font I will just die, I really don't think it is.
       
       
       
                  sf::Text message_holder10;
                  sf::RenderWindow windowtype3(sf::VideoMode(700, 1000), "a");

                  if ((space_is_used && on_last_line) || (space_is_used && ((line_number) == (total_lines - 2))))
                  {

                      //string temp_string = message::Get_Out_Bound_String();
                      //int length_of_string = temp_string.length();
                      sf::Font Fontforscore;
                      if (gflag == 0)
                      {
                          gflag = 1;
                          
                          if (!Fontforscore.loadFromFile("ARIALBD.ttf"))
                          {
                              exit(1);
                          }

                          message_holder10.setFont(Fontforscore);
                          message_holder10.setCharacterSize(100);
                          message_holder10.setFillColor(sf::Color::Red);
                          message_holder10.setOrigin(0, 0);
                          message_holder10.setPosition(0, 0);
                          windowtype2.close();
                      }
                      message_holder10.setString("asty");
                          
                          //int y_for_space = display_y_setting + (total_lines - 2) * each_vertical_offset_is;
                          //int this_width = 0;
                          
                          //float x = message_holder.getLocalBounds().width;

                          
                          
                          //message_holder.setPosition( ( (first_width - x )/2), y_for_space);
                          
                  
                          //windowtype2.close();

                          string test =     message_holder10.getString();
                          
                          windowtype3.clear();
                          windowtype3.draw(message_holder10);
                          windowtype3.display();
                          
                          
                           
       
                          //windowtype.display();
                      

                       Wait_For_Space_Press();
                      
       
      /////////////////////////
       
      Before, the :      windowtype3.display()  without the clear was drawing other text in this call, just not this one particular text message with it!
       
      Thank you so much I am wondering what it can be,
       
      Josheir
  • Popular Now