Is it possible to get the captured values of a lambda without using std::function? I'm asking because I want to place the captured copies into my own memory, which std::function cannot do as they don't support custom allocators.
(I assume allocator support missing for std::function is due to a very good reason, perhaps the logic behind capturing values in a lambda is extremely difficult to implement? But if it's possible, I'd like to try it myself.)
Background: I'm asking to learn more about lambda in C++. I'd like to place the captured values and reference pointers in over-aligned memory for a thread pool system I'm writing as an academic exercise. I also really like the brevity writing lambdas with automatic captures, it'd make for a very easy "job" writing interface I think.
class MyFunction {
public:
// I want more than just the function pointer,
// I also want the captured value copies or references too
// I'm unsure how to really accomplish this though.
MyFunction & operator=( ??? ) {
???
}
};
int main(){
int captureThis = 1;
MyFunction func = [=]()->void {
printf("%i", captureThis);
};
}
Checking values of captured variables outside of lambda would not look good but at least you can use a factory function to produce it with its unique "lambda" template type (also with help of auto on the final type) so that you can do extra work between what thread calls and what you initialize:
#include <iostream>
#include <thread>
#include <vector>
template <typename F>
struct Callable
{
Callable(const F && lambda):func(std::move(lambda))
{
}
// this is what the std::thread calls
void operator()()
{
// you can check the captured variable
int out;
func(out,false);
std::cout<< "Callable successfully found out the value of captured variable: "<<out <<std::endl;
func(out,true);
}
const F func;
};
template<typename F>
Callable<F> CallableFactory(F&& lambda)
{
return Callable<F>(std::forward<F>(lambda)); // or std::move(lambda)
}
int main()
{
// variable to capture
int a=1;
auto callable = CallableFactory([&](int & outputCapturedValue, bool runNow){
// not looking good as its not possible from outside (because they are private variables & depends on implementation of C++)
// if checking the captured variables
if(!runNow)
{
outputCapturedValue = a;
std::cout << "inside the lambda: a=" << a <<std::endl;
}
else
{
std::cout<<"algorithm runs"<<std::endl;
}
});
std::vector<std::thread> threads;
threads.emplace_back(std::move(callable));
threads[0].join();
return 0;
}
output:
inside the lambda: a=1
Callable successfully found out the value of captured variable: 1
algorithm runs
If its only for having an array of lambdas processed by array of threads, you can use smart-pointers and an extra container struct to box/unbox them during work-distribution:
#include <iostream>
#include <thread>
#include <vector>
#include <memory>
struct ICallable
{
virtual void operator()()=0;
};
template <typename F>
struct Callable:public ICallable
{
Callable(const F && lambda):func(std::move(lambda))
{
}
// this is what the std::thread calls
void operator()() override
{
func();
}
const F func;
};
template<typename F>
std::shared_ptr<ICallable> CallablePtrFactory(F&& lambda)
{
return std::shared_ptr<ICallable>(new Callable<F>(std::forward<F>(lambda)));
}
struct CallableContainer
{
std::shared_ptr<ICallable> callable;
void operator()()
{
callable.get()->operator()();
}
};
int main()
{
// variable to capture
int a=1;
// simulating work pool
std::vector<std::shared_ptr<ICallable>> callables;
callables.push_back(CallablePtrFactory([&](){
std::cout<< "a="<<a<<std::endl;
}));
// simulating worker pool load-balancing
std::vector<std::thread> threads;
threads.emplace_back(CallableContainer{ callables[0] });
threads[0].join();
return 0;
}
output:
a=1
If you're after a custom-allocation for the container, you can just use a second parameter for the factory function. Following example uses placement-new on a stack buffer. But still the lambda itself has something else outside of it making container's size not changed by its lambda (just like a function-pointer):
#include <iostream>
#include <thread>
#include <vector>
#include <memory>
struct ICallable
{
virtual void operator()()=0;
};
template <typename F>
struct Callable:public ICallable
{
Callable(const F && lambda):func(std::move(lambda))
{
currentSize = sizeof(*this); std::cout<<"current size = "<<currentSize <<" (useful for alignement of next element?)" <<std::endl;
}
// this is what the std::thread calls
void operator()() override
{
func();
}
int currentSize;
const F func;
};
template<typename F>
std::shared_ptr<ICallable> CallablePtrFactory(F&& lambda, char * buffer)
{
return std::shared_ptr<ICallable>(
new (buffer) Callable<F>(std::forward<F>(lambda)),
[](ICallable *){ /* placement-new does not require a delete! */}
);
}
struct CallableContainer
{
std::shared_ptr<ICallable> callable;
void operator()()
{
callable.get()->operator()();
}
};
int main()
{
// variable to capture
int a=1;
char buffer[10000];
// simulating work pool
std::vector<std::shared_ptr<ICallable>> callables;
callables.push_back(
// observe the buffer for placement-new
CallablePtrFactory([&](){
std::cout<< "a="<<a<<std::endl;
},buffer /* you should compute offset for next element */)
);
// simulating worker pool load-balancing
std::vector<std::thread> threads;
threads.emplace_back(CallableContainer{ callables[0] });
threads[0].join();
return 0;
}
output:
current size = 24 (useful for alignement of next element?)
a=1
Considering the thread pool system you mention in comments then you could try a polymorphic approach:
class ThreadRoutine
{
protected:
virtual ~ThreadRoutine() { }
public:
virtual void run() = 0;
};
template <typename Runner>
class ThreadRoutineT
{
// variant 1:
Runner m_runner; // store by value;
// variant 2:
std::reference_wrapper<Runner> m_runner;
public:
void run() override { m_runner(); } // call operator()
};
Now you might store your thread routines in a std::vector (note: pointers to, by value would lead to object slicing; likely std::unique_ptr, possibly, depending on use case, a classic raw pointer might fit, too).
You could even implement both variants, your thread pool manager could provide an additional parameter in the thread creation function or maybe even more elegant distinguish by overloading that function (l-value reference: create the reference wrapper variant; r-value reference: create the value variant, move-construct it), e.g. like:
class ThreadManager
{
template <typename Runner>
void createThread(Runner& runner)
{
// assuming std::vector<std::unique_ptr<ThreadRoutine>>
m_runners.emplace_back
(
// assuming appropriate constructor
std::make_unique<ThreadRoutineRef>(runner)
);
// return some kind of thread handle or start thread directly?
// thread handle: could be an iterator into a std::list
// (instead of std::vector) as these iterators do not invalidate
// if elements preceding in the list are removed
// alternatively an id as key into a std::[unordered_]map
}
template <typename Runner>
void createThread(Runner&& runner)
{
m_runners.emplace_back
(
// assuming appropriate constructor
std::make_unique<ThreadRoutineVal>(std::move(runner))
);
}
}
About the alignment issue: The specific template instantiations would select the alignment appropriate for the template argument type, so you wouldn't have to consider anything particular.
Related
Let the code speak for itself first with naive approach:
int heavy_calc() // needed to be called once
{
// sleep(7500000 years)
return 42;
}
int main()
{
auto foo = [] {
// And cached for lambda return value
static int cache = heavy_calc();
return cache;
};
return foo() + foo();
}
I want to have lambda internal cached value calculated on the first call. An naive approach is to use static cache, but it increases binary size and refuses to be be inlined.
I came up with creating cache in capture list and marking lambda as mutable, what inlines without problems, but requires cache to start with default value, which may break class invariant.
auto foo = [cache=0] () mutable {
// And cached for lambda return value
if(!cache)
cache = heavy_calc();
return cache;
};
My third approach uses boost::optional in mutable lambda
auto foo = [cache=std::optional<int>{}] () mutable {
// And cached for lambda return value
if(!cache)
cache = heavy_calc();
return *cache;
};
It works properly, but looks for me as kind of capture list + mutable keyword hack. Also mutable affects all captured parameters, so makes lambda less safe in real use.
Maybe there is an better/more clean solution for this? Or just different approach which ends up with the very same effect.
EDIT, some background:
Lambda approach is chosen as I am modifying some callback lambda, which currently is used as:
[this, param]{this->onEvent(heavy_calc(param));}
I want to reduce heavy_calc calls without evaluating it in advance (only on first call)
To be honest, I don't see any reason to use lambda here. You can write a regular reusable class to cache calculation value. If you insist on using lambda then you can move value calculation to parameters so there will be no need to make anything mutable:
int heavy_calc() // needed to be called once
{
// sleep(7500000 years)
return 42;
}
int main()
{
auto foo
{
[cache = heavy_calc()](void)
{
return cache;
}
};
return foo() + foo();
}
online compiler
With a bit of template it is possible to write a class that will lazy evaluate and cache result of arbitrary calculation:
#include <boost/optional.hpp>
#include <utility>
template<typename x_Action> class
t_LazyCached final
{
private: x_Action m_action;
private: ::boost::optional<decltype(::std::declval<x_Action>()())> m_cache;
public: template<typename xx_Action> explicit
t_LazyCached(xx_Action && action): m_action{::std::forward<xx_Action>(action)}, m_cache{} {}
public: auto const &
operator ()(void)
{
if(not m_cache)
{
m_cache = m_action();
}
return m_cache.value();
}
};
template<typename x_Action> auto
Make_LazyCached(x_Action && action)
{
return t_LazyCached<x_Action>{::std::forward<x_Action>(action)};
}
class t_Obj
{
public: int heavy_calc(int param) // needed to be called once
{
// sleep(7500000 years)
return 42 + param;
}
};
int main()
{
t_Obj obj{};
int param{3};
auto foo{Make_LazyCached([&](void){ return obj.heavy_calc(param); })};
return foo() + foo();
}
online compiler
It works properly, but looks for me as kind of capture list + mutable keyword hack. Also mutable affects all captured parameters, so makes lambda less safe in real use.
There is the solution to roll your own, hand-made lambda:
#include <optional>
int heavy_calc() // needed to be called once
{
// sleep(7500000 years)
return 42;
}
int main()
{
struct {
std::optional<int> cache;
int operator()() {
if (!cache) cache = heavy_calc();
return *cache;
}
} foo;
return foo() + foo();
}
It's inlined the same way and you don't need to rely on the capture+mutable hack.
I do believe this is exactly the use case for mutable lambda. If you don't want to have all variables mutable I suggest just creating functor class with one mutable field. This way you get best of both worlds (ok, it isn't that concise). The additional benefit is that the operator() is const (which is quite right, as it always returns same value)
#include <optional>
int heavy_calc() {
// sleep(7500000 years)
return 42;
}
struct my_functor {
mutable std::optional<int> cache;
int operator()() const {
if (!cache) cache = heavy_calc();
return *cache;
}
}
int main() {
my_functor foo;
return foo() + foo();
}
I have a class with a function that takes a std::function and stores it. This part seems to compile ok (but please point out any issue if there are any)
#include <functional>
#include <iostream>
struct worker
{
std::function<bool(std::string)> m_callback;
void do_work(std::function<bool(std::string)> callback)
{
m_callback = std::bind(callback, std::placeholders::_1);
callback("hello world\n");
}
};
// pretty boring class - a cut down of my actual class
struct helper
{
worker the_worker;
bool work_callback(std::string str)
{
std::cout << str << std::endl;
return true;
}
};
int main()
{
helper the_helper;
//the_helper.the_worker.do_work(std::bind(&helper::work_callback, the_helper, std::placeholders::_1)); // <---- SEGFAULT (but works in minimal example)
the_helper.the_worker.do_work(std::bind(&helper::work_callback, &the_helper, std::placeholders::_1)); // <---- SEEMS TO WORK
}
I get a segfault, but I am not sure why. I have used this before, in fact, I copied this example from another place I used it. The only real difference that the member function was part of the class I called it from (i.e. this instead of the_helper).
So this is why I am also asking if there is anything else I am doing wrong in general? Like should I be passing the std::function as:
void do_work(std::function<bool(std::string)>&& callback)
or
void do_work(std::function<bool(std::string)>& callback)
As also noted by #Rakete1111 in comments, the problem probably was in this code:
bool work_callback(std::string str)
{
std::cout << str << std::endl;
}
In C++ if a non-void function does not return a value the result is undefined behavior.
This example will crash with clang but pass with gcc.
If helper::work_callback returns (e.g, true) the code works just fine.
I don't know why your code seg faults because I was spoiled and skipped std::bind straight to lambdas. Since you use C++11 you should really convert your code from std::bind to lambdas:
struct worker
{
std::function<bool(std::string)> m_callback;
void do_work(std::function<bool(std::string)> callback)
{
m_callback = callback;
callback("hello world\n");
}
};
Now with work_callback and calling do_work things need some analysis.
First version:
struct helper
{
worker the_worker;
bool work_callback(std::string)
{
return false;
}
};
int main()
{
helper the_helper;
the_helper.the_worker.do_work([&](std::string s) { return the_helper.work_callback(s); });
}
Now this version works with your toy example. However out in the wild you need to be careful. The lambda passed to do_work and then stored in the_worker captures the_helper by reference. This means that this code is valid only if the helper object passed as reference to the lambda outlives the worker object that stores the m_callback. In your example the worker object is a sub-object of the the helper class so this is true. However if in your real example this is not the case or you cannot prove this, then you need to capture by value.
First attempt to capture by value (does not compile):
struct helper
{
worker the_worker;
bool work_callback(std::string)
{
return false;
}
};
int main()
{
helper the_helper;
the_helper.the_worker.do_work([=](std::string s) { return the_helper.work_callback(s); });
}
This does not compile because the copy of the_helper stored in the lambda object is const by default and as such you cannot call work_callback on it.
A questionable solution if you can't make work_callback const is to make the lambda mutable:
struct helper
{
worker the_worker;
bool work_callback(std::string)
{
return false;
}
};
int main()
{
helper the_helper;
the_helper.the_worker.do_work([=](std::string s) mutable { return the_helper.work_callback(s); });
}
But you need to think if this is what you intended.
What would make more sense is to make work_callback const:
struct helper
{
worker the_worker;
bool work_callback(std::string) const
{
return false;
}
};
int main()
{
helper the_helper;
the_helper.the_worker.do_work([=](std::string s) { return the_helper.work_callback(s); });
}
The reason for getting SEGFAULT has been already mentioned in the comments.
However, I would like to point out that, you need to use neither std::bind nor std::function, here in your given case. Instead, simply having a lambda and a function pointer you can handle what you intend to do.
struct worker
{
typedef bool(*fPtr)(const std::string&); // define fun ptr type
fPtr m_callback;
void do_work(const std::string& str)
{
// define a lambda
m_callback = [](const std::string& str)
{
/* do something with string*/
std::cout << "Call from worker: " << str << "\n";
return true;
};
bool flag = m_callback(str);// just call the lambda here
/* do some other stuff*/
}
};
struct helper
{
worker the_worker;
bool work_callback(const std::string& str)
{
std::cout << "Call from helper: ";
this->the_worker.do_work(str);
return true; ------------------------>// remmeber to keep the promise
}
};
And use case would be:
int main()
{
helper the_helper;
the_helper.work_callback(std::string("hello world"));
// or if you intend to use
the_helper.the_worker.do_work(std::string("hello world"));
return 0;
}
see Output here:
PS: In the above case, if worker does not required m_callback for later cases(i.e, only for do_work()), then you can remove this member, as lambdas can be created and called at same place where it has been declared.
struct worker
{
void do_work(const std::string& str)
{
bool flag = [](const std::string& str)->bool
{
/* do something with string*/
std::cout << "Call from worker: " << str << "\n";
return true;
}(str); -------------------------------------> // function call
/* do other stuff */
}
};
I have designed a simple callback-keyListener-"Interface" with the help of a pure virtual function. Also I used a shared_ptr, to express the ownership and to be sure, that the listener is always available in the handler. That works like a charme, but now I want to implement the same functionality with the help of std::function, because with std::function I am able to use lambdas/functors and I do not need to derive from some "interface"-classes.
I tried to implement the std::function-variant in the second example and it seems to work, but I have two questions related to example 2:
Why does this example still work, although the listener is out of scope? (It seems, that we are working with a copy of the listener instead of the origin listener?)
How can I modify the second example, to achieve the same functionality like in the first example (working on the origin listener)? (member-ptr to std::function seems not to work! How can we handle here the case, when the listener is going out of scope before the handler? )
Example 1: With a virtual function
#include <memory>
struct KeyListenerInterface
{
virtual ~KeyListenerInterface(){}
virtual void keyPressed(int k) = 0;
};
struct KeyListenerA : public KeyListenerInterface
{
void virtual keyPressed(int k) override {}
};
struct KeyHandler
{
std::shared_ptr<KeyListenerInterface> m_sptrkeyListener;
void registerKeyListener(std::shared_ptr<KeyListenerInterface> sptrkeyListener)
{
m_sptrkeyListener = sptrkeyListener;
}
void pressKey() { m_sptrkeyListener->keyPressed(42); }
};
int main()
{
KeyHandler oKeyHandler;
{
auto sptrKeyListener = std::make_shared<KeyListenerA>();
oKeyHandler.registerKeyListener(sptrKeyListener);
}
oKeyHandler.pressKey();
}
Example 2: With std::function
#include <functional>
#include <memory>
struct KeyListenerA
{
void operator()(int k) {}
};
struct KeyHandler
{
std::function<void(int)> m_funcKeyListener;
void registerKeyListener(const std::function<void(int)> &funcKeyListener)
{
m_funcKeyListener = funcKeyListener;
}
void pressKey() { m_funcKeyListener(42); }
};
int main()
{
KeyHandler oKeyHandler;
{
KeyListenerA keyListener;
oKeyHandler.registerKeyListener(keyListener);
}
oKeyHandler.pressKey();
}
std::function<Sig> implements value semantic callbacks.
This means it copies what you put into it.
In C++, things that can be copied or moved should, well, behave a lot like the original. The thing you are copying or moving can carry with it references or pointers to an extrenal resource, and everything should work fine.
How exactly to adapt to value semantics depends on what state you want in your KeyListener; in your case, there is no state, and copies of no state are all the same.
I'll assume we want to care about the state it stores:
struct KeyListenerA {
int* last_pressed = 0;
void operator()(int k) {if (last_pressed) *last_pressed = k;}
};
struct KeyHandler {
std::function<void(int)> m_funcKeyListener;
void registerKeyListener(std::function<void(int)> funcKeyListener) {
m_funcKeyListener = std::move(funcKeyListener);
}
void pressKey() { m_funcKeyListener(42); }
};
int main() {
KeyHandler oKeyHandler;
int last_pressed = -1;
{
KeyListenerA keyListener{&last_pressed};
oKeyHandler.registerKeyListener(keyListener);
}
oKeyHandler.pressKey();
std::cout << last_pressed << "\n"; // prints 42
}
or
{
oKeyHandler.registerKeyListener([&last_pressed](int k){last_pressed=k;});
}
here we store a reference or pointer to the state in the callable. This gets copied around, and when invoked the right action occurs.
The problem I have with listeners is the doulbe lifetime issue; a listener link is only valid as long as both the broadcaster and reciever exist.
To this end, I use something like this:
using token = std::shared_ptr<void>;
template<class...Message>
struct broadcaster {
using reciever = std::function< void(Message...) >;
token attach( reciever r ) {
return attach(std::make_shared<reciever>(std::move(r)));
}
token attach( std::shared_ptr<reciever> r ) {
auto l = lock();
targets.push_back(r);
return r;
}
void operator()( Message... msg ) {
decltype(targets) tmp;
{
// do a pass that filters out expired targets,
// so we don't leave zombie targets around forever.
auto l = lock();
targets.erase(
std::remove_if( begin(targets), end(targets),
[](auto&& ptr){ return ptr.expired(); }
),
end(targets)
);
tmp = targets; // copy the targets to a local array
}
for (auto&& wpf:tmp) {
auto spf = wpf.lock();
// If in another thread, someone makes the token invalid
// while it still exists, we can do an invalid call here:
if (spf) (*spf)(msg...);
// (There is no safe way around this issue; to fix it, you
// have to either restrict which threads invalidation occurs
// in, or use the shared_ptr `attach` and ensure that final
// destruction doesn't occur until shared ptr is actually
// destroyed. Aliasing constructor may help here.)
}
}
private:
std::mutex m;
auto lock() { return std::unique_lock<std::mutex>(m); }
std::vector< std::weak_ptr<reciever> > targets;
};
which converts your code to:
struct KeyHandler {
broadcaster<int> KeyPressed;
};
int main() {
KeyHandler oKeyHandler;
int last_pressed = -1;
token listen;
{
listen = oKeyHandler.KeyPressed.attach([&last_pressed](int k){last_pressed=k;});
}
oKeyHandler.KeyPressed(42);
std::cout << last_pressed << "\n"; // prints 42
listen = {}; // detach
oKeyHandler.KeyPressed(13);
std::cout << last_pressed << "\n"; // still prints 42
}
I am toying with this idea for a while but cant seem to wrap my head around it.
Basically what I want to do is create a general Timer class that times all the functions that are passed to it. Averaging it when the same function is called multiple times so it has to store it somehow. It should therefore use the function name to store the task and average it when it occurs more than once.
Pseudoish code of what it should look like.
Class FunctionTaks
{
std::string d_name;
double d_execution_time;
}
Class Timer
{
private:
std::vector<FunctionTask> d_tasks;
public:
template <typename Function, typename ReturnType>
ReturnType time(Function f)
{
// check if function f is timed for first time
// start timer
// run function f
auto r = f.invoke();
// stop timer
// store function name and time, average if more than once
// return whatever f should return
return r;
}
void report() const;
}
I dont really know how to do this, especially when Function f has a different amount of arguments.
Timer t;
t.time(foo());
t.time(bar());
t.time(foo());
t.report();
I basically have a few core issues.
How to let a function wrapper return the same type that the injected code is suppose to return.
How to obtain the function name that is being injected.
The wrapper should not be limited by arguments passed on to the injected function. How to give the injected function the freedom of arguments.
On the other hand I dont really care about the arguments and return type, the wrapper should simply run the injected function as is and perform some timings and then return whatever the injected function is suppose to return.
C++11 but why templates? You need lambda expressions:
typedef void(*TimerFunction)();
void time(TimerFunction fun) {
// start timer
fun();
// stop timer
}
int fun1() { return 1; }
int fun2() { return 2; }
string fun3() { return string("Test"); }
main() {
int ret1, ret2;
string ret3;
t.time([&ret1]() { ret1 = fun1(); });
t.time([&ret2]() { ret2 = fun2(); });
t.time([&ret3]() { ret3 = fun3(); });
}
That's the concept. For details: C++ lambda with captures as a function pointer
With C++11 you can use variable template parameters:
class TimerFoo {
public:
template <class Foo, class... Args> TimerFoo(Foo foo, Args... args) {
// ... start timer
foo(args...);
// ... stop timer
}
};
And use e.g.:
TimerFoo tf = TimerFoo(foo, 1, 2, 3);
Ofcourse you need some field in TimerFoo that will store the measured time...
Edit:
To be able to return a value of your function using this approach you could change the above code to:
#include <iostream>
using namespace std;
class TimerFoo {
public:
template <class Foo, class... Args> auto run(Foo foo, Args... args) -> decltype(foo(args...)) {
// ... start timer
auto result = foo(args...);
// ... stop timer
return result;
}
};
int foo(int a, int b) {
return 2;
}
int main() {
TimerFoo tf;
cout << tf.run(foo, 1, 2) << endl; // output: 2
}
I have a class that stores weak_ptrs in a container and later does something if the weak_ptr is not expired:
class Example
{
public:
void fill(std::shared_ptr<int> thing)
{
member.push_back(thing);
}
void dosomething() const
{
for (const auto& i : member)
if (!i.expired())
;// do something. the weak_ptr will not be locked
}
private:
std::vector<std::weak_ptr<int>> member;
};
If Example is an object that lives forever and fill is used regularily, the vector allocates memory for elements continously, but they are never removed after they expired.
Is there any automatic C++ way to get rid of the expired weak_ptrs in the container or is there a better way to store a variable number of them?
My naive way would be to iterate over the container each time fill is called and remove all the expired weak_ptrs. In scenarios where Example has many elements in the container and fill is frequently called this seems to be very inefficient.
Since you clarified that you are actually using a std::map and not a std::vector, it might be easiest to remove the expired elements on-the-fly in doSomething(). Switch back from a range-based for loop to a normal iterator based design:
void dosomething() const
{
auto i = member.begin();
while( i != member.end() ) {
if( i->expired() ) { i = member.erase( i ); continue; }
;// do something. the weak_ptr will not be locked
++i;
}
}
Does the shared_ptr<int> have to be a shared_ptr<int>?
How about a shared_ptr<IntWrapper>?
#include <iostream>
#include <forward_list>
using namespace std;
class IntWrapper {
public:
int i;
static forward_list<IntWrapper*>& all() {
static forward_list<IntWrapper*> intWrappers;
return intWrappers;
}
IntWrapper(int i) : i(i) {
all().push_front(this);
}
~IntWrapper() {
all().remove(this);
}
};
void DoSomething() {
for(auto iw : IntWrapper::all()) {
cout << iw->i << endl;
}
}
int main(int argc, char *argv[]) {
shared_ptr<IntWrapper> a = make_shared<IntWrapper>(1);
shared_ptr<IntWrapper> b = make_shared<IntWrapper>(2);
shared_ptr<IntWrapper> c = make_shared<IntWrapper>(3);
DoSomething();
return 0;
}
I would rather use a custom deleter for the shared_ptr. But this implies here to change the interface of the Example class. The advantage using custom deleter is that there is no need to check for expired objects in the collection. The collection is directly maintained by the custom deleter.
Quick implementation :
#include <memory>
#include <iostream>
#include <set>
template <typename Container>
// requires Container to be an associative container type with key type
// a raw pointer type
class Deleter {
Container* c;
public:
Deleter(Container& c) : c(&c) {}
using key_type = typename Container::key_type;
void operator()(key_type ptr) {
c->erase(ptr);
delete ptr;
}
};
class Example {
public:
// cannot change the custom deleter of an existing shared_ptr
// so i changed the interface here to take a unique_ptr instead
std::shared_ptr<int> fill(std::unique_ptr<int> thing) {
std::shared_ptr<int> managed_thing(thing.release(), Deleter<containter_type>(member));
member.insert(managed_thing.get());
return managed_thing;
}
void dosomething() const {
// we don't need to check for expired pointers
for (const auto & i : member)
std::cout << *i << ", ";
std::cout << std::endl;
}
using containter_type = std::set<int*>;
private:
containter_type member;
};
int main()
{
Example example;
auto one = example.fill(std::unique_ptr<int>(new int(1)));
auto two = example.fill(std::unique_ptr<int>(new int(2)));
auto three = example.fill(std::unique_ptr<int>(new int(3)));
example.dosomething();
three.reset();
example.dosomething();
}