Reputation: 56038
I'm creating a class that will be part of a DAG. The constructor will take pointers to other instances and use them to initialize a dependency list.
After the dependency list is initialized, it can only ever be shortened - the instance can never be added as a dependency of itself or any of its children.
::std::shared_ptr
is a natural for handling this. Reference counts were made for handling DAGs.
Unfortunately, the dependencies need to know their dependents - when a dependency is updated, it needs to tell all of its dependents.
This creates a trivial cycle that can be broken with ::std::weak_ptr
. The dependencies can just forget about dependents that go away.
But I cannot find a way for a dependent to create a ::std::weak_ptr
to itself while it's being constructed.
This does not work:
object::object(shared_ptr<object> dependency)
{
weak_ptr<object> me = shared_from_this();
dependency->add_dependent(me);
dependencies_.push_back(dependency);
}
That code results in the destructor being called before the constructor exits.
Is there a good way to handle this problem? I'm perfectly happy with a C++11-only solution.
Upvotes: 9
Views: 3747
Reputation: 363547
Instead of a constructor, use a function to build the nodes of your graph.
std::shared_ptr<Node> mk_node(std::vector<std::shared_ptr<Node>> const &dependencies)
{
std::shared_ptr<Node> np(new Node(dependencies));
for (size_t i=0; i<dependencies.size(); i++)
dependencies[i].add_dependent(np); // makes a weak_ptr copy of np
return np;
}
If you make this a static
member function or a friend
of your Node
class, you can make the actual constructor private
.
Upvotes: 9
Reputation: 46770
This has been driving me nuts as well.
I considered adopting the policy of using pointers to break cycles... But I'm really not found of this because I really like how clear the intent of the weak_ptr is when you see it in your code (you know it's there to break cycles).
Right now I'm leaning toward writing my own weak_ptr class.
Upvotes: 0
Reputation: 490108
It sounds to me like you're trying to conflate to somewhat different items: a single object (a node in the DAG), and managing a collection of those objects.
class DAG {
class node {
std::vector<std::weak_ptr<node> > dependents;
public:
node(std::vector<weak_ptr<node> > d) : dependents(d) {}
};
weak_ptr<node> root;
};
Now, it may be true that DAG will only ever hold a weak_ptr<node>
rather than dealing with an instance of a node
directly. To the node itself, however, this is more or less irrelevant. It needs to maintain whatever key/data/etc., it contains, along with its own list of dependents.
At the same time, by nesting it inside of DAG (especially if we make the class definition of node
private to DAG), we can minimize access to node
, so very little other code has to be concerned with anything about a node
. Depending on the situation, you might also want to do things like deleting some (most?) of the functions in node that the compiler will generate by default (e.g., default ctor, copy ctor, assignment operator).
Upvotes: 1
Reputation: 106530
Unfortunately, the dependencies need to know their dependents. This is because when a dependency is updated, it needs to tell all of its dependents. And there is a trivial cycle. Fortunately, this cycle can be broken with ::std::weak_ptr. The dependencies can just forget about dependents that go away.
It sounds like a dependency can't reference a dependent unless the dependent exists. (E.g. if the dependent is destroyed, the dependency is destroyed too -- that's what a DAG is after all)
If that's the case, you can just hand out plain pointers (in this case, this
). You're never going to need to check inside the dependency if the dependent is alive, because if the dependent died then the dependency should have also died.
Just because the object is owned by a shared_ptr doesn't mean that all pointers to it themselves must be shared_ptrs or weak_ptrs - just that you have to define clear semantics as to when the pointers become invalidated.
Upvotes: 1
Reputation: 49231
Maybe this will help:
inherit from enable_shared_from_this which basically holds a weak_ptr
.
This will allow you to use this->shared_from_this();
shared_ptr's know to look if the class inherits from the class and use the classes weak_ptr when pointing to the object (prevents 2 shared pointers from counting references differently)
More about it: cppreference
Upvotes: -1
Reputation: 1
I'm understanding your question as conceptually related to garbage collection issues.
GC is a non-modular feature: it deals with some global property of the program (more precisely, being a live data is a global, non-modular, property inside a program - there are situations where you cannot deal with that in a modular & compositional way.). AFAIK, STL or C++ standard libraries does not help much for global program features.
A possible answer might be to use (or implement yourself) a garbage collector; Boehm's GC could be useful to you, if you are able to use it in your entire program.
And you could also use GC algorithms (even copying generational ones) to deal with your issue.
Upvotes: 1
Reputation: 18964
You can't.
The best I've come up with is to make the constructor private and have a public factory function that returns a shared_ptr
to a new object; the factory function can then call a private method on the object post-construction that initialises the weak_ptr
.
Upvotes: 1
Reputation: 143071
Basically, you can't. You need a shared_ptr
or weak_ptr
to make a weak_ptr
and obviously self can only be aware of of its own shared_ptr
only in form of weak_ptr
(otherwise there's no point in counting references). And, of course, there could be no self-shared_ptr
when the object isn't yet constructed.
Upvotes: 5