refactor lambda to be called by another class: while keeping caller's code to be still short - c++11

How to professionally refactor lambda function to be called by another class WHILE make caller's code still short?
My attempt shows that for changing a lambda function to a normal function, I have to capture variables manually, thus the new normal function requires more parameters (to compensate automatic capture ability).
As a result, the new function is more tedious to use, and can cause more bug.
Example
Here is my original code, using lambda.
void Turret::registerFullversion(int gameObjectId, PhysicObject* phyO){//utility
//.... something a bit complex .......
}
void Turret::createTurret(int typeOfTurret){
int gameObjectId=createNewGameObjectId();
auto registerEasy=[&]( PhysicObject* phyO){
//^ served as a short hand version of "registerFullversion"
// 1 parameter is more comfortable than 2
registerFullversion(gameObjectId,phyO);
}
switch(typeOfTurret){
case 1:{ //this part will be moved into another class (###)
PhysicObject* phy=PhysicSystem::createNewPhysicObject();
registerEasy( phy);
//^ equivalent to "registerFullversion(gameObjectId,phy)"
// but it is very concise (1 parameter), nice!
};break;
//..... a lot of case ....
}
//... do something about "gameObjectId"
}
I want to move a part of function (###) from Turret into another class (TurretLaser).
It works, but the result is that caller have to capture gameObjectId and pass it manually :-
void Turret::createTurret(int typeOfTurret){
int gameObjectId=createNewGameObjectId();
switch(typeOfTurret){
case 1:{ //this part have to be move into another class
TurretLaser::createTurret(gameObjectId)
};break;
//..... a lot of case ....
}
}
void TurretLaser::createTurret(int gameObjectId){ //(###)
PhysicObject* phy=PhysicSystem::createNewPhysicObject();
Turret:registerFullversion(gameObjectId,phy);
//^ it is not as short as before (now = 2 parameters)
}
Note
In real case, all above functions are non-static function, and all functions are far more complex.
Performance is the first priority. Thus, std::bind and std::function are not allowed.
This question asks about how to omit the captured parameters rather than "Please fix my code", so a valid solution can also just provide a new example with its own fix instead of showing modification of my code.
My attempt
I will manually capture the related data (gameObjectId) and cache it (using a new variable CACHE_gameObjectId):-
void Turret::registerEasy(PhysicObject* physicO){
registerFullversion(CACHE_gameObjectId,physicO);
//int "CACHE_gameObjectId" is a new field of "Turret"
};
void Turret::createTurret(int typeOfTurret){
int gameObjectId=createNewGameObjectId();
Turret::CACHE_gameObjectId=gameObjectId;
switch(typeOfTurret){
case 1:{ //this part have to be move into another class
TurretLaser::createTurret(gameObjectId)
};break;
//..... a lot of case ....
}
}
void TurretLaser::createTurret(int gameObjectId){ //(###)
PhysicObject* phy=PhysicSystem::createNewPhysicObject();
Turret:registerEasy(phy);
//^ short as before, nice
}
Disadvantage of my solution: dirty, look dangerous (not so automatic, thus can cause more bug) , seem to be less thread-safe (?)

Related

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.

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

Are constant references still best practice in c++11 and later?

I recently read an article about the new move semantics in C++. It was about the confusion how to best implement a return value for a large object. The conclusion was, just implement it like return by copy and let the compiler decide if a move works best.
Now I wondered if this is true for function parameters as well meanwhile.
Currently I use const references like this:
void setLargeObject(const LargeObject &obj) {
_obj = obj;
}
Instead of the simple copy:
void setLargeObject(LargeObject obj) {
_obj = obj;
}
Are parameters, to copy large objects, passed by const reference still be the best practice in C++11 and later?
If setting the property requires taking ownership of the value, then pass by value. It will be copied if necessary before the function call, when the parameter is initialized. Inside the function, move it into place.
void setLargeObject(LargeObject obj) {
_obj = std::move(obj);
}
If LargeObject doesn't support move semantics (so having std::move changes nothing), then you might use const& to limit the performance hit to one copy instead of two. However, the best solution is to add movability, not to stay with const&.

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.

C++ memory management patterns for objects used in callback chains

A couple codebases I use include classes that manually call new and delete in the following pattern:
class Worker {
public:
void DoWork(ArgT arg, std::function<void()> done) {
new Worker(std::move(arg), std::move(done)).Start();
}
private:
Worker(ArgT arg, std::function<void()> done)
: arg_(std::move(arg)),
done_(std::move(done)),
latch_(2) {} // The error-prone Latch interface isn't the point of this question. :)
void Start() {
Async1(<args>, [=]() { this->Method1(); });
}
void Method1() {
StartParallel(<args>, [=]() { this->latch_.count_down(); });
StartParallel(<other_args>, [=]() { this->latch_.count_down(); });
latch_.then([=]() { this->Finish(); });
}
void Finish() {
done_();
// Note manual memory management!
delete this;
}
ArgT arg_
std::function<void()> done_;
Latch latch_;
};
Now, in modern C++, explicit delete is a code smell, as, to some extent is delete this. However, I think this pattern (creating an object to represent a chunk of work managed by a callback chain) is fundamentally a good, or at least not a bad, idea.
So my question is, how should I rewrite instances of this pattern to encapsulate the memory management?
One option that I don't think is a good idea is storing the Worker in a shared_ptr: fundamentally, ownership is not shared here, so the overhead of reference counting is unnecessary. Furthermore, in order to keep a copy of the shared_ptr alive across the callbacks, I'd need to inherit from enable_shared_from_this, and remember to call that outside the lambdas and capture the shared_ptr into the callbacks. If I ever wrote the simple code using this directly, or called shared_from_this() inside the callback lambda, the object could be deleted early.
I agree that delete this is a code smell, and to a lesser extent delete on its own. But I think that here it is a natural part of continuation-passing style, which (to me) is itself something of a code smell.
The root problem is that the design of this API assumes unbounded control-flow: it acknowledges that the caller is interested in what happens when the call completes, but signals that completion via an arbitrarily-complex callback rather than simply returning from a synchronous call. Better to structure it synchronously and let the caller determine an appropriate parallelization and memory-management regime:
class Worker {
public:
void DoWork(ArgT arg) {
// Async1 is a mistake; fix it later. For now, synchronize explicitly.
Latch async_done(1);
Async1(<args>, [&]() { async_done.count_down(); });
async_done.await();
Latch parallel_done(2);
RunParallel([&]() { DoStuff(<args>); parallel_done.count_down(); });
RunParallel([&]() { DoStuff(<other_args>); parallel_done.count_down(); };
parallel_done.await();
}
};
On the caller-side, it might look something like this:
Latch latch(tasks.size());
for (auto& task : tasks) {
RunParallel([=]() { DoWork(<args>); latch.count_down(); });
}
latch.await();
Where RunParallel can use std::thread or whatever other mechanism you like for dispatching parallel events.
The advantage of this approach is that object lifetimes are much simpler. The ArgT object lives for exactly the scope of the DoWork call. The arguments to DoWork live exactly as long as the closures containing them. This also makes it much easier to add return-values (such as error codes) to DoWork calls: the caller can just switch from a latch to a thread-safe queue and read the results as they complete.
The disadvantage of this approach is that it requires actual threading, not just boost::asio::io_service. (For example, the RunParallel calls within DoWork() can't block on waiting for the RunParallel calls from the caller side to return.) So you either have to structure your code into strictly-hierarchical thread pools, or you have to allow a potentially-unbounded number of threads.
One option is that the delete this here is not a code smell. At most, it should be wrapped into a small library that would detect if all the continuation callbacks were destroyed without calling done_().

Resources