Help me fix this code using std::shared_ptr

Started by
6 comments, last by King Mir 10 years, 4 months ago

I am starting to integrate smart pointers into my code. However, I am still getting stuck up on exactly how to use them. Here is the relevant code snippet:


bool DebugLayer::Init(
		DebugOptions options, 
		const std::shared_ptr<Renderer> &renderer, 
		const std::shared_ptr<const InputController> &inputController, 
		const std::shared_ptr<const Timer> &timer,
		const RECT& windowRect)
	{
		if(!inputController || !timer || !renderer || !inputController)
		{
			OutputDebugString("\nInvalid parameters passed to DebugLayer::Init()\n");
			return false;
		}

		this->debugOptions = options;
		this->renderer = std::make_shared<Renderer>(*renderer);
		this->inputController = std::make_shared<const InputController>(*inputController);
		this->timer = std::make_shared<const Timer>(*timer);

                ...
                ...

}

I am trying to pass const pointers to const objects into the function. For some reason, when I try and set my own class instance shared_ptr objects to what I pass into Init() I get the following errors from <memory>:

Error 8 error C2440: '<function-style-cast>' : cannot convert from 'std::shared_ptr<_Ty>' to 'std::shared_ptr<_Ty>'
Error 9 error C2228: left of '.swap' must have class/struct/union
Error 10 error C2440: '<function-style-cast>' : cannot convert from 'std::shared_ptr<_Ty>' to 'std::shared_ptr<_Ty>'
J.W.
Advertisement
Is your intent to copy the data passed to init or to share it? If copy, then you should not pass in shared_ptr's; just pass the parameters by value. If share, then just assign the shared pointer member the passed object; no need for make_shared or dereferencing the pointers.

make_shared allocates a new shared region from the pointer you pass in, i believe.

simply remove all the make_shared calls, and do a normal assignment and you're done.

devstropo.blogspot.com - Random stuff about my gamedev hobby

make_shared allocates a new shared region from the pointer you pass in, i believe.


No. std::make_shared passes whatever arguments it receives to the underlying class's constructor. The difference between
(1) someSharedPointer = std::shared_ptr<MyType>(new MyType(A, B, C, ...));
(2) someSharedPointer = std::make_shared<MyType>(A, B, C, ...);
is soley the fact, that there are two allocations done in (1) and only a single allocation in (2). If jdub intends to copy construct a new Renderer/InputController/Timer instance in a new shared pointer, what he does is completely correct.

jdub: the key problem is that the error messages you posted are incomplete. Unfortunately the MSVC Error List omits a lot important information. The Output is much more descriptive, including information what types the templates have in the problematic areas. You are also not saying in which lines the errors are happening.

You should seriously consider using plain references instead of shared_ptr. Shared ownership is almost certainly not what you want. Your DebugLayer doesn't own a renderer, it's just referencing one own by another object. If you need a nullable reference, use a raw pointer.

I can't stress how much time I've spent debugging a the bajillion problems caused by over-zealous use of shared_ptr in production engine code. Stay far away from it excepting those very rare cases where you actually for-sure need multiple actual owners (and you can't redesign your interface to not need them).

Sean Middleditch – Game Systems Engineer – Join my team!

make_shared allocates a new shared region from the pointer you pass in, i believe.


No. std::make_shared passes whatever arguments it receives to the underlying class's constructor. The difference between

(1) someSharedPointer = std::shared_ptr<MyType>(new MyType(A, B, C, ...));
(2) someSharedPointer = std::make_shared<MyType>(A, B, C, ...);
is soley the fact, that there are two allocations done in (1) and only a single allocation in (2). If jdub intends to copy construct a new Renderer/InputController/Timer instance in a new shared pointer, what he does is completely correct.

jdub: the key problem is that the error messages you posted are incomplete. Unfortunately the MSVC Error List omits a lot important information. The Output is much more descriptive, including information what types the templates have in the problematic areas. You are also not saying in which lines the errors are happening.

You're right, it uses variadic templates to forward arguments to the std::shared_ptr constructor, and the allocation (whether it's one or two allocs) happens inside the ctor.

So what happens when you have a shared_ptr<T> p, and pass a *p to a make_shared? *p returns a reference, so make_shared passes that reference to the shared_ptr ctor, but there is no ctor that accepts a T&, so wouldn't that be an error too? Or is something else happening that i'm not aware of?

Also, if you have a shared_ptr<T> p, and pass it in a function with p.get(), and that function passes the pointer to make_shared, does that mean two separate reference counts will monitor the same pointer? And deleting both shared pointers would cause either a crash or undefined behaviour?

devstropo.blogspot.com - Random stuff about my gamedev hobby

You're right, it uses variadic templates to forward arguments to the std::shared_ptr constructor, and the allocation (whether it's one or two allocs) happens inside the ctor.

Allocations don't happen inside constructors (unless the constructor itself again allocates different memory for its own purposes). Constructors are called on memory which has just been allocated.

So what happens when you have a shared_ptr<T> p, and pass a *p to a make_shared? *p returns a reference, so make_shared passes that reference to the shared_ptr ctor, but there is no ctor that accepts a T&, so wouldn't that be an error too? Or is something else happening that i'm not aware of?

This use case would require a valid copy constructor to work. Without knowing more about jdub's design, it's not possible to say whether that's intended or not. But there are scenarios where it is a completely valid and desired operation, though in this case I doubt it.

Also, if you have a shared_ptr<T> p, and pass it in a function with p.get(), and that function passes the pointer to make_shared, does that mean two separate reference counts will monitor the same pointer? And deleting both shared pointers would cause either a crash or undefined behaviour?

In general constructing two or more shared pointers from the same raw pointers will result in double deletion and as a consequence undefined behavior. Exceptions to this do exist though. boost::intrusive_ptr does not suffer from that problem if the class sticks to the contract. Custom deleters might be able to guarantee defined behavior as well.
The way jdub uses shared pointers (while still probably not what he intends) does not put him in danger of that though.
I think the actual cause of the compiler error here is that the objects passed in are not copyable. So again, the solution is to use assignment on the shared pointer.

This topic is closed to new replies.

Advertisement