Why one may need a shared_from_this instead of directly using this pointer? - c++11

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

Related

When does c++ right value destruct in this scenario?

Here is the code:
class SomeType {
public:
SomeType() {}
~SomeType() {}
std::string xxx;
}
bool funtion_ab() {
SomeType(); // This is a right val;
// The right val destructs here when I test the code. I want to make sure that it would always destructs here.
int a = 0, b = 10;
....// other code
return true;
}
Please tell me if you know the truth. Thank you!
What you have is called a temporary object. From §6.7.7,
Temporary objects are created
when a prvalue is converted to an xvalue
or, more specifically,
[Note 3: Temporary objects are materialized:
...
when a prvalue that has type other than cv void appears as a discarded-value expression ([expr.context]).
— end note]
and, on the lifetime, the same section has this to say
Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created.
You can read more about the expression semantics, but in your case "full-expression" is fairly unambiguous.
SomeType();
The "full-expression" containing your constructor call is... the constructor call itself. So the destructor will be called immediately after evaluating the constructor. There are some exceptions to this rule (such as if the temporary object is thrown as an exception or is bound as a reference), but none of those apply here.
As noted in the comments, compilers are free to inline your constructor and destructor calls and then are free to notice that they do nothing and omit them entirely. Optimizers can do fun stuff with your code, provided it doesn't change the semantics. But a strict reading of the standard states that the destructor is called exactly where you suggested.

Removing a std::function<()> from a vector c++

I'm building a publish-subscribe class (called SystermInterface), which is responsible to receive updates from its instances, and publish them to subscribers.
Adding a subscriber callback function is trivial and has no issues, but removing it yields an error, because std::function<()> is not comparable in C++.
std::vector<std::function<void()> subs;
void subscribe(std::function<void()> f)
{
subs.push_back(f);
}
void unsubscribe(std::function<void()> f)
{
std::remove(subs.begin(), subs.end(), f); // Error
}
I've came down to five solutions to this error:
Registering the function using a weak_ptr, where the subscriber must keep the returned shared_ptr alive.
Solution example at this link.
Instead of registering at a vector, map the callback function by a custom key, unique per callback function.
Solution example at this link
Using vector of function pointers. Example
Make the callback function comparable by utilizing the address.
Use an interface class (parent class) to call a virtual function.
In my design, all intended classes inherits a parent class called
ServiceCore, So instead of registering a callback function, just
register ServiceCore reference in the vector.
Given that the SystemInterface class has a field attribute per instance (ID) (Which is managed by ServiceCore, and supplied to SystemInterface by constructing a ServiceCore child instance).
To my perspective, the first solution is neat and would work, but it requires handling at subscribers, which is something I don't really prefer.
The second solution would make my implementation more complex, where my implementation looks as:
using namespace std;
enum INFO_SUB_IMPORTANCE : uint8_t
{
INFO_SUB_PRIMARY, // Only gets the important updates.
INFO_SUB_COMPLEMENTARY, // Gets more.
INFO_SUB_ALL // Gets all updates
};
using CBF = function<void(string,string)>;
using INFO_SUBTREE = map<INFO_SUB_IMPORTANCE, vector<CBF>>;
using REQINF_SUBS = map<string, INFO_SUBTREE>; // It's keyed by an iterator, explaining it goes out of the question scope.
using INFSRC_SUBS = map<string, INFO_SUBTREE>;
using WILD_SUBS = INFO_SUBTREE;
REQINF_SUBS infoSubrs;
INFSRC_SUBS sourceSubrs;
WILD_SUBS wildSubrs;
void subscribeInfo(string info, INFO_SUB_IMPORTANCE imp, CBF f) {
infoSubrs[info][imp].push_back(f);
}
void subscribeSource(string source, INFO_SUB_IMPORTANCE imp, CBF f) {
sourceSubrs[source][imp].push_back(f);
}
void subscribeWild(INFO_SUB_IMPORTANCE imp, CBF f) {
wildSubrs[imp].push_back(f);
}
The second solution would require INFO_SUBTREE to be an extended map, but can be keyed by an ID:
using KEY_T = uint32_t; // or string...
using INFO_SUBTREE = map<INFO_SUB_IMPORTANCE, map<KEY_T,CBF>>;
For the third solution, I'm not aware of the limitations given by using function pointers, and the consequences of the fourth solution.
The Fifth solution would eliminate the purpose of dealing with CBFs, but it'll be more complex at subscriber-side, where a subscriber is required to override the virtual function and so receives all updates at one place, in which further requires filteration of the message id and so direct the payload to the intended routines using multiple if/else blocks, which will increase by increasing subscriptions.
What I'm looking for is an advice for the best available option.
Regarding your proposed solutions:
That would work. It can be made easy for the caller: have subscribe() create the shared_ptr and corresponding weak_ptr objects, and let it return the shared_ptr.
Then the caller must not lose the key. In a way this is similar to the above.
This of course is less generic, and then you can no longer have (the equivalent of) captures.
You can't: there is no way to get the address of the function stored inside a std::function. You can do &f inside subscribe() but that will only give you the address of the local variable f, which will go out of scope as soon as you return.
That works, and is in a way similar to 1 and 2, although now the "key" is provided by the caller.
Options 1, 2 and 5 are similar in that there is some other data stored in subs that refers to the actual std::function: either a std::shared_ptr, a key or a pointer to a base class. I'll present option 6 here, which is kind of similar in spirit but avoids storing any extra data:
Store a std::function<void()> directly, and return the index in the vector where it was stored. When removing an item, don't std::remove() it, but just set it to std::nullptr. Next time subscribe() is called, it checks if there is an empty element in the vector and reuses it:
std::vector<std::function<void()> subs;
std::size_t subscribe(std::function<void()> f) {
if (auto it = std::find(subs.begin(), subs.end(), std::nullptr); it != subs.end()) {
*it = f;
return std::distance(subs.begin(), it);
} else {
subs.push_back(f);
return subs.size() - 1;
}
}
void unsubscribe(std::size_t index) {
subs[index] = std::nullptr;
}
The code that actually calls the functions stored in subs must now of course first check against std::nullptrs. The above works because std::nullptr is treated as the "empty" function, and there is an operator==() overload that can check a std::function against std::nullptr, thus making std::find() work.
One drawback of option 6 as shown above is that a std::size_t is a rather generic type. To make it safer, you might wrap it in a class SubscriptionHandle or something like that.
As for the best solution: option 1 is quite heavy-weight. Options 2 and 5 are very reasonable, but 6 is, I think, the most efficient.

Automatically call move constructors

I have the following code sample:
void MyClass::Register2(std::string name, std::string email)
{
m_name = std::move(name);
m_email = std::move(email);
}
void MyClass::Register1(std::string name)
{
Register2(std::move(name), "invalid_email");
}
My questions are:
Do I need to use std::move when calling Register2() from Register1()?
Do I need to call std::move() inside Register1()?
If the answer for question 2. is yes, would be possible to have a dedicated operator instead?
For example:
void MyClass::Register2(std::string name, std::string email)
{
m_name <= name; // perform move
m_email <= email; // perform move
}
Do I need to use std::move when calling Register2() from Register1()?
Yes, because name is an lvalue and you want to turn it into an rvalue.
Do I need to call std::move() inside Register1()?
Yes, for the same reason as above.
If the answer for question 2. is yes, would be possible to have a dedicated operator instead?
It would be possible, but I do not think it has been proposed. Also, if it were to be proposed, I do not think it would be accepted as it doesn't bring much value over std::move.
Yes
Yes
No
std::move looks something like this
template<typename T>
std::remove_reference_t<T>&& move(T&& value) {
return static_cast<std::remove_reference_t<T>&&>(value);
}
All it does is it casts the thing you pass as the argument to an rvalue. This step is essential because arguments, even if you pass an rvalue, are always lvalues (because they have names). Lvalues are not moveable, therefore if you don't cast them to an rvalue, move mechanics won't kick in and they will be simply copied.
Operators are functions and there are no exceptions in this case. Special move operator hasn't been proposed and is extremly unlikely to be because it would make the standard longer and more complex (compilers would also be heavily affected) for a feature that saves a couple chars.

std::unique_ptr<Object> and many viewers (Object*), is it good design?

Say I want to manage an Object with unique_ptr in a sort of master class. However, I'm in a situation where many other classes need to use this Object. I'm passing Object* to them. I don't think this is a good design, but I can't find a right solution.
class Gadget1 {
Object* obj_;
public:
Gadget1(Object* obj) : obj_(obj) {}
};
class Gadget2 {
// .. similar
};
class Worker {
std::unique_ptr<Object> obj_;
public:
void init() {
obj_ = std::make_unique<Object>(...);
createGadget1(obj_.get());
createGadget2(obj_.get());
...
}
};
What'd be a right and safe approach? Should Gadget have unique_ptr<Object>& instead of Object*?
Assume that the lifetime of Gadget1 is guaranteed to shorter than Worker.
Your design is perfectly fine: smart pointers for the owner(s), and raw pointers for everyone else.
If you cannot guarantee that the objects outlives the observers, either:
Notify the observers when an object dies so they can update their raw pointer, or
Give std::weak_ptrs instead of raw pointers to the observers so they can check.
In any case, you should not use std::unique_ptr<Object> &: observers should not care about how the object's lifetime is ensured.
Plus, this adds nothing over a raw pointer: if the object dies, it's because its owner died, so the std::unique_ptr is dead too, and the reference is dangling -- back to square one.

How can I move a shared_ptr's data?

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.

Resources