How can I move a shared_ptr's data? - c++11

I have an easy question about shared pointers and move semantics. Imagine that I have a class with a private member variable like this:
class C
{
private:
std::shared_ptr<std::vector<uint8_t>> buffer;
}
I need to provide public getters and setters. The getter seems obvious:
std::shared_ptr<std::vector<uint8_t>> C::GetBuffer()
{
return buffer;
}
However, being new to C++ I'm having trouble writing the setter. I could do something like this:
void C::SetBuffer(std::shared_ptr<std::vector<uint8_t>> input)
{
buffer = input;
}
However that results in a copy of input to buffer, but I don't really want the caller to have shared ownership. Instead I want to move the data. I tried to solve this with:
void C::SetBuffer(std::shared_ptr<std::vector<uint8_t>>& input)
{
buffer(std::move(input));
}
But this is an error: "call of an object of a class type without appropriate operator() or conversion functions to pointer-to-function type."
Can somebody help me understand:
1. What is going on here?
2. How to best implement the setter?

You can fix the error you're getting by writing this:
void C::SetBuffer( std::shared_ptr<std::vector<uint8_t> > &input ) {
buffer = move(input);
}
This will call shared_ptr's move-assignment operator, which will pilfer input. However, this won't really stop the caller from having shared ownership. Once you accept (or dispense) a shared_ptr from/to an unknown client, you don't have much in the way of control about who shares ownership. Even if input is pilfered, there's no reason to expect that input was the only copy of the shared_ptr you just received. If, for example, the function that called SetBuffer() took whatever became input from its caller by value, that higher-level copy of the pointer will continue to share ownership.
Note that your getter has a similar issue. You're returning a shared_ptr to your own internal object (and what's more, it's a shared_ptr-to-non-const, so the client can modify the shared state) and wherever that shared_ptr gets passed around after you provide it, those copies will also share (mutable) ownership.
If you really want to ensure you have exclusive ownership, you can hold a unique_ptr instead of a shared_ptr and have your getter pass back a const-reference, and your setter take either a unique_ptr or a value.

If your goal is to allow a caller to pass sole ownership of a buffer to your object, you should accept it by unique_ptr instead of shared_ptr:
void C::SetBuffer(std::unique_ptr<std::vector<uint8_t>> input)
{
buffer = std::move(input);
}
Rvalue unique_ptr is convertible to shared_ptr for exactly this purpose.

Related

Why one may need a shared_from_this instead of directly using this pointer?

Look at the second answer here:
What is the need for enable_shared_from_this?
it says:
"Short answer: you need enable_shared_from_this when you need to use inside the object itself existing shared pointer guarding this object.
Out of the object you can simply assign and copy a shared_ptr because you deal with the shared_ptr variable as is."
and later down in the last line it says:
"And when and why one can need a shared pointer to this instead of just this it is quite other question. For example, it is widely used in asynchronous programming for callbacks binding."
Here in this post I want to ask exactly this other question. What is an use case for "enable_shared_from_this" and "shared_from_this"?
A simple use-case would be to ensure this survives till the end of some asynchronous, or delayed operation:
class My_type : public std::enable_shared_from_this<My_type> {
public:
void foo() {}
void perform_foo() {
auto self = shared_from_this();
std::async(std::launch::async, [self, this]{ foo(); });
}
};
boost::asio uses this technique a lot in their examples:
https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/example/cpp11/allocation/server.cpp

Casting std::shared_ptr<T> to void*

A library requires binary data to be shared as void *.
The data to be shared is available as shared_ptr<T>.
Is there a way to cast shared_ptr<T> to void * ?
PS: Static casting does not work:
error: invalid static_cast from type ‘std::shared_ptr<DataPacket>’ to type ‘void*’
static_cast<void *>(binData);
You're going about this wrong. Your problem is not "I need to interpret a shared_ptr<T> as a void*" — your problem is "I have a smart pointer to an object, and I need a dumb pointer to that object".
shared_ptr<T> has a function that does exactly that.
shared_ptr<T> smart;
// ... some code here points smart at an object ...
T *dumb1 = smart.get(); // creates a dumb pointer to the object managed by smart
void *dumb2 = smart.get(); // dumb pointers automatically convert to void*
Do be careful to note that the dumb pointer this creates does not participate in the shared ownership scheme, so you have to take care to ensure the lifetime of the object lasts as long as you need it to.

Accessing object T in vector<unique_ptr<T>> without taking ownership

I have the following member variable in a class:
std::vector<std::unique_ptr<Object>> objects_;
I explicitly want the vector to maintain ownership at all times. I've seen suggestions that in order for a member function to access a pointer in the vector and make changes to the object T wrapped in the std::unique_ptr, we must move the pointer to calling code, i.e:
void foo(int i) {
auto object = std::move( vector.at( i ) ); // move object to caller (caller owns)
object->dosomething();
vector.at(i) = std::move(object); // move back into vector (vector owns)
}
Another method was to work with raw pointers:
void foo(int i) {
Object* object = vector.at( i ).get();
object->doSomething();
}
However, I've been working with this:
void foo(int i) {
auto& object = vector.at( i );
object->doSomething();
}
Which is the correct and most robust method for my case? Does this function ever take ownership of the data in the std::unique_ptr? Is there a way to access Object without playing with the std::unique_ptr?
(excuse me if my methods are incorrect, I hope I got the point across)
The first approach will not retain ownership of the object if object->dosomething() throws an exception (i.e. it is not exception safe) since the second std::move() statement will not be executed.
Assuming C++11, both of the other approaches are effectively equivalent, subject to the assumption that the owned pointer is not null. Under the same assumption, the code can be simplified to
void foo(int i)
{
vector.at(i)->doSomething();
}
which will work with all C++ standards (not just C++11 or later).
It is possible to access the object without monkeying with the unique_ptr - simply store the pointer elsewhere and use that. However, that does compromise the purpose of using std::unique_ptr in the first place. And is error-prone - for example, the std::unique_ptr can destroy the object, and leave those other pointers dangling.
If you are really that worried about the potential of your vector losing ownership, consider using a shared_ptr instead.

Code duplication for functions that take shared_ptr and unique_ptr

Problem:
Let's assume I have an algorithm that takes a unique_ptr to some type:
void FancyAlgo(unique_ptr<SomeType>& ptr);
Now I have shared_ptr sPtr to SomeType, and I need to apply the same algorithm on sPtr. Does this mean I have to duplicate the algorithm just for the shared_ptr?
void FancyAlgo(shared_ptr<SomeType>& sPtr);
I know smart pointers come with ownership of the underlying managed object on the heap. Here in my FancyAlgo, ownership is usually not an issue. I thought about stripping off the smart pointer layer and do something like:
void FancyAlgo(SomeType& value);
and when I need to call it with unique_ptr:
FancyAlgo(*ptr);
likewise for shared_ptr.
1, Is this an acceptable style in PRODUCTION code?(I saw somewhere that in a context of smart pointers, you should NOT manipulate raw pointers in a similar way. It has the danger of introducing mysterious bugs.)
2, Can you suggest any better way (without code duplication) if 1 is not a good idea.
Thanks.
Smart pointers are about ownership. Asking for a smart pointer is asking for ownership information or control.
Asking for a non-const lvalue reference to a smart pointer is asking for permission to change the ownership status of that value.
Asking for a const lvalue reference to a smart pointer is asking for permission to query the ownership status of that value.
Asking for an rvalue reference to a smart pointer is being a "sink", and promising to take that ownership away from the caller.
Asking for a const rvalue reference is a bad idea.
If you are accessing the pointed to value, and you want it to be non-nullable, a reference to the underlying type is good.
If you want it to be nullable, a boost::optional<T&> or a T* are acceptable, as is the std::experimental "world's dumbest smart pointer" (or an equivalent hand-written one). All of these are non-owning nullable references to some variable.
In an interface, don't ask for things you don't need and won't need in the future. That makes reasoning about what the function does harder, and leads to problems like you have in the OP. A function that reseats a reference is a very different function from one that reads a value.
Now, a more interesting question based off yours is one where you want the function to reseat the smart pointer, but you want to be able to do it to both shared and unique pointer inputs. This is sort of a strange case, but I could imagine writing a type-erase-down-to-emplace type (a emplace_sink<T>).
template<class T>
using later_ctor = std::function<T*(void*)>;
template<class T, class...Args>
later_ctor<T> delayed_emplace(Args&&...args) {
// relies on C++1z lambda reference reference binding, write manually
// if that doesn't get in, or don't want to rely on it:
return [&](void* ptr)->T* {
return new T(ptr)(std::forward<Args>(args));
};
}
namespace details {
template<class T>
struct emplace_target {
virtual ~emplace_target() {}
virtual T* emplace( later_ctor<T> ctor ) = 0;
};
}
template<class T>
struct emplacer {
std::unique_ptr<emplace_target<T>> pImpl;
template<class...Args>
T* emplace( Args&&... args ) {
return pImpl->emplace( delayed_emplace<T>(std::forward<Args>(args)...) );
}
template<class D>
emplacer( std::shared_ptr<T, D>& target ):
pImpl( new details::emplace_shared_ptr<T,D>(&target) ) // TODO
{}
template<class D>
emplacer( std::unique_ptr<T, D>& target ):
pImpl( new details::emplace_unique_ptr<T,D>(&target) ) // TODO
{}
};
etc. Lots of polish needed. The idea is to type-erase construction of an object T into an arbitrary context. We might need to special case shared_ptr so we can call make_shared, as a void*->T* delayed ctor is not good enough to pull that off (not fundamentally, but because of lack of API hooks).
Aha! I can do a make shared shared ptr without special casing it much.
We allocate a block of memory (char[sizeof(T)]) with a destructor that converts the buffer to T then calls delete, in-place construct in that buffer (getting the T*), then convert to a shared_ptr<T> via the shared_ptr<T>( shared_ptr<char[sizeof(T)]>, T* ) constructor. With careful exception catching this should be safe, and we can emplace using our emplacement function into a make_shared combined buffer.

How to convert between shared_ptr<FILE> to FILE* in C++?

I am trying to use a FILE pointer multiple times through out my application
for this I though I create a function and pass the pointer through that. Basically I have this bit of code
FILE* fp;
_wfopen_s (&fp, L"ftest.txt", L"r");
_setmode (_fileno(fp), _O_U8TEXT);
wifstream file(fp);
which is repeated and now instead I want to have something like this:
wifstream file(SetFilePointer(L"ftest.txt",L"r"));
....
wofstream output(SetFilePointer(L"flist.txt",L"w"));
and for the function :
FILE* SetFilePointer(const wchar_t* filePath, const wchar_t * openMode)
{
shared_ptr<FILE> fp = make_shared<FILE>();
_wfopen_s (fp.get(), L"ftest.txt", L"r");
_setmode (_fileno(fp.get()), _O_U8TEXT);
return fp.get();
}
this doesn't simply work. I tried using &*fp instead of fp.get() but still no luck.
You aren't supposed to create FILE instances with new and destroy them with delete, like make_shared does. Instead, FILEs are created with fopen (or in this case, _wfopen_s) and destroyed with fclose. These functions do the allocating and deallocating internally using some unspecified means.
Note that _wfopen_s does not take a pointer but a pointer to pointer - it changes the pointer you gave it to point to the new FILE object it allocates. You cannot get the address of the pointer contained in shared_ptr to form a pointer-to-pointer to it, and this is a very good thing - it would horribly break the ownership semantics of shared_ptr and lead to memory leaks or worse.
However, you can use shared_ptr to manage arbitrary "handle"-like types, as it can take a custom deleter object or function:
FILE* tmp;
shared_ptr<FILE> fp;
if(_wfopen_s(&tmp, L"ftest.txt", L"r") == 0) {
// Note that we use the shared_ptr constructor, not make_shared
fp = shared_ptr<FILE>(tmp, std::fclose);
} else {
// Remember to handle errors somehow!
}
Please do take a look at the link #KerrekSB gave, it covers this same idea with more detail.

Resources