Marty,
As follow up to our discussion (and head scratching) last week.
Attached is an annotated example of the using wrapped/nested
shared_ptr to maintain a list of shared_ptr with automatic
cleanup.
Michael
As a note. This is _a_ way to keep the equivalent
of "list<weak_ptr<C> >". There are at least two other
ways to do this. I'm not going to say which is better
or worse.
1. "list<C*>" keep raw pointers and call "list.remove(this)"
from "C::~C".
2. Keep "list<weak_ptr<C> >" and occasionally (eg. when adding)
loop through and erase() any which have "weak_ptr::expired()".
/* Example of using wrapped/nested shared_ptr
* to maintain a list of "weak" references without
* actually using weak_ptr.
*
* The plain, unwrapped, shared_ptr<Channel> are called "internal" and
* will call the delete operator, and therefore the Channel destructor,
* when the "internal" ref count falls to zero.
*
* This is desirable in cases where std::enabled_share_from_this<Channel>
* would be preferred, but can not be as shared_from_this()
* can't be used in Channel destructor.
*
* The wrapped/nested shared_ptr<Channel> are called "external", and
* will call Channel::cleanup() when the "external" ref count calls to
* zero. The destroyer object associated with the "external" ref counter
* contains an "internal" reference. This ensures that the Channel
* object is not free'd until after cleanup() returns, and allows
* cleanup to (temporarily) create new "internal" references.
*
* g++ -O2 -o sharedmap -std=c++11 -g -Wall -Werror sharedlist.cpp && valgrind ./sharedlist
*/
#include <iostream>
#include <string>
#include <list>
#include <memory>
#include <cassert>
namespace {
struct Channel;
struct Context;
// our example Context is only a list of Channel references.
struct Context {
// This list contains only "internal" Channel references.
std::list<std::shared_ptr<Channel> > channels;
~Context() {
std::cout<<__func__<<'\n';
// Channels should be removed before they are destoryed.
// and Context shouldn't be destroyed until all Channels are destroyed.
assert(channels.empty());
}
void show_channels();
};
struct Channel {
const std::string name; // for display purposes only
const std::shared_ptr<Context> context;
// Can't use std::enable_shared_from_this<Channel> as it's magic
// interactions with the shared_ptr constructor conflict with
// the wrapping done in our build() below.
//
// This weak pointer is to our internal (unwrapped) reference.
std::weak_ptr<Channel> internal_self;
// we could also store a weak ref to our external (wrapped) ref.
// but this example doesn't need it.
Channel(const std::string& name,
const std::shared_ptr<Context>& context
) :name(name), context(context) {}
~Channel() {
std::cout<<__func__<<" "<<name<<'\n';
}
// cleanup() functions like a destructor, but only when all external
// references are released.
void cleanup() {
std::shared_ptr<Channel> SELF(internal_self);
std::cout<<__func__<<" "<<SELF->name<<'\n';
context->channels.remove(SELF);
}
// cleaner functor object.
// This object is stored along side the "external" ref counter.
// Thus it is not destoryed until all strong and _weak_ "external" refs are released.
struct cleaner {
std::shared_ptr<Channel> internal;
explicit cleaner(const std::shared_ptr<Channel>& internal) :internal(internal) {}
void operator()(Channel *ignore) {
std::shared_ptr<Channel> temp;
// consume stored reference.
// not strict needed in this example, as no weak "external" refs exist.
// but better to be in the habit then to be surprised by a "weak ref loop"
// which leaks the "internal" ref!
temp.swap(internal);
temp->cleanup();
// temp goes out of scope, which _may_ run Channel dtor provided no internal strong refs remain.
// Practical multi-threaded code could used cleanup() to wait for such refs to be released,
// and perhaps to join any worker threads which have them.
assert(temp.use_count()==1);
}
};
/* with c++11 "cleaner(internal)" can be replaced with
*
* [internal] (Channel* ignore) {
* std::shared_ptr<Channel> temp(std::move(internal));
* temp->cleanup();
* }
*/
static std::shared_ptr<Channel> build(const std::string& name,
const std::shared_ptr<Context>& context)
{
std::cout<<__func__<<" "<<name<<'\n';
std::shared_ptr<Channel> internal(new Channel(name, context)),
external(internal.get(), cleaner(internal));
external->internal_self = internal;
// could save weak_ptr to external here, but no reason in this example
context->channels.push_back(internal);
assert(external.use_count()==1);
return external;
}
};
void Context::show_channels() {
std::cout<<"Channels:\n";
for(auto& chan : channels) {
std::cout<<" "<<chan->name<<'\n';
}
}
} // namespace
int main(int argc, char *argv[])
{
try {
std::shared_ptr<Context> ctxt(new Context);
std::cout<<"Build channel \"bar\"\n";
std::shared_ptr<Channel> bar(Channel::build("bar", ctxt));
{
std::cout<<"Build channel \"foo\"\n";
std::shared_ptr<Channel> foo(Channel::build("foo", ctxt));
ctxt->show_channels(); // shows "foo" and "bar"
std::cout<<"channel foo external ref goes out of scope\n";
}
ctxt->show_channels(); // shows only "bar"
std::cout<<"channel bar external ref goes out of scope\n";
return 0;
}catch(std::exception& e){
std::cerr<<"Error: "<<e.what()<<'\n';
return 2;
}
}
- Navigate by Date:
- Prev:
Re: Problem with INSTALL_LOCATION and "make uninstall" in 7.0.1 J. Lewis Muir
- Next:
Re: Problem with INSTALL_LOCATION and "make uninstall" in 7.0.1 Michael Davidsaver
- Index:
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
<2018>
2019
2020
2021
2022
2023
2024
- Navigate by Thread:
- Prev:
Re: Q: Structured argument types for PVA RPC service call Ralph Lange
- Next:
Build failed in Jenkins: epics-7.0-windows » STATIC,win64 #45 APS Jenkins
- Index:
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
<2018>
2019
2020
2021
2022
2023
2024
|