Hi,
I would like to create in C++ a Notifier class that I will use in other objects to notify various holders when the object gets destroyed.
template <class Owner>
class Notifier<Owner> {
public:
Notifier(Owner* owner);
~Notifier(); // Notifies the owner that an object is destroyed
};
class Owner;
class Owned {
public:
Owned(Owner* owner);
private:
Notifier<Owner> _notifier;
};
My point is that as I have a dense and complicated object graph, I'd like to avoid storing the address of the owned object in the notifier. Is there a way to change my notifier class so that it can deduce the owned object's address from its own address and an offset that would be computed at compile time?
Note also that any object may have to notify several 'owners', possibly from the same class.
Thanks.
-
Part of the solution would be to have Owned inherit from Notifier. This way, the address of the destroyed object is simply 'this'...
class Owned : public Notifier<Owner> { public: Owned(Owner* owner) : Notifier<Owner>(owner) {} };
But how to handle multiple 'owners' from the same class? How can one inherit several times from the 'same class'?
Thanks to fa's answer, here is the solution I was looking for:
#include <iostream> template <class Owner, class Owned, int = 0> class Notifier { public: Notifier(Owner* owner) : _owner(owner) {} ~Notifier() { _owner->remove(owned()); } Owned * owned(){ return static_cast< Owned * >( this ); } private: Owner* _owner; }; class Owner { public: void remove(void* any) { std::cout << any << std::endl; } }; class Owned : public Notifier<Owner,Owned,1>, Notifier<Owner,Owned,2> { public: Owned(Owner* owner1, Owner* owner2) : Notifier<Owner,Owned,1>(owner1) , Notifier<Owner,Owned,2>(owner2) {} }; int main() { std::cout << sizeof(Owned) << std::endl; Owner owner1; Owner owner2; Owned owned(&owner1, &owner2); std::cout << "Owned:" << (void*)&owned << std::endl << std::endl; }
Thanks!
Luc Touraille : In the notifier, can't you store a list of owners instead of a single one?rmeador : Your use case really allows you to know all of this at compile time? And you're ok with the code bloat from instantiating so many templates?Xavier Nodet : The use case is to implement the relations between classes in a Business Object Model. E.g. a Demand is linked to a Customer: I know all about those classes, and I know that each demand has exactly 1 customer, and that each customer has 0 to n demands... -
Take a look at the GoF Observer Design Patter.
Xavier Nodet : I'm indeed looking for a way to implement a one-to-many relationship. It can very well be used for an Observer pattern, but not necessarily.Mykola Golubyev : @Xavier: I am not sure I understand you. "very well be used for an Observer". Observer is the way you can implement your notification.Xavier Nodet : "The Observer defines a one-to-many relationship so that when one object changes state, the others are notified and updated automatically". But I want the many to notify the one (in a memory-efficient way). -
I highly doubt it. There is no way for the Notifier to know that it has been used in composition. What if I do
class Foo { private: Notifier _a, _b, _c; }
I'd love to be proven wrong though, but I really doubt it's doable without explicitly giving more information to the Notifier.
Xavier Nodet : Any template (or other) trick to give this information at compile-time?BenoƮt : There is no way templates can help you here. -
It would be a nasty hack and probably not guaranteed to work, but here's a thought I don't recommend this.
Suppose you have your layout like you described like this:
template <class Owner> class Notifier<Owner> { public: Notifier(Owner* owner); ~Notifier(); // Notifies the owner that an object is destroyed }; class Owner; class Owned { public: Owned(Owner* owner); private: Notifier<Owner> _notifier; };
If
_notifier
knows its name, it could calculateOwned
's address like this (which is executed in theNotifier
's constructor):Owned *p = reinterpret_cast<Owned *>(reinterpret_cast<char *>(this) - offsetof(Owned, _notifier));
basically, the assumption is that _notifier is at some fixed offset within the Owned class. Therefore the address of Owned is equal to
_notifier
's address minus that same offset.Once again, this is undefined behavior which I wouldn't recommend, but could possibly work.
-
Or something like this :
Inherit from your notifier and add Owned as template parameter. Then you can have a owned method available inside the notifier :
template < class Owner , class Owned > class Notifier { public: Notifier(Owner* owner) {} Owned * owned() { return static_cast< Owned * >( this ); } ~Notifier() { // notify owner with owned() } }; class Owner {}; class Owned : public Notifier< Owner , Owned > { public: Owned( Owner * owner ) : Notifier< Owner , Owned >( owner ) {} };
Luc Touraille : +1. That's in my opinion the best solution. However, it doesn't allow multiple owners of the same type.Xavier Nodet : @Luc: Yes, it does allow multiple owners of the same type with just a bit more work: adding e.g. an int template parameter... See my answer.rstevens : This solution is only applicable if the object graph is nearly static (not more than one object of the same type). If you create instances dynamically in a tree-like manner you need the instances of the owner to be stored.Xavier Nodet : This is the Curiously Recurring Template Pattern (CRTP): http://c2.com/cgi/wiki?CuriouslyRecurringTemplate -
fa.'s answer is a good start. However, it does not resolve the problem of having multiple owners of the same type. One solution is to have the notifier store a list of owners instead of a single one. Here is a quick implementation, to show the idea:
template <typename Owner, typename Owned> class Notifier { protected: Notifier() {} // Constructor taking a single owner Notifier(Owner & o) { owners.push_back(&o); } // Constructor taking a range of owners template <typename InputIterator> Notifier(InputIterator firstOwner, InputIterator lastOwner) : owners(firstOwner, lastOwner) {} ~Notifier() { OwnerList::const_iterator it = owners.begin(); OwnerList::const_iterator end = owners.end(); for ( ; it != end ; ++it) { (*it)->notify(static_cast<Owned*>(this)); } } // Method for adding a new owner void addOwner(Owner & o) { owners.push_back(&o); } private: typedef std::vector<Owner *> OwnerList; OwnerList owners; };
You can use it this way:
class Owner; class Owned : public Notifier<Owner, Owned> { typedef Notifier<Owner, Owned> base; //Some possible constructors: Owned(Owner & o) : base(o) { } Owned(Owner & o1, Owner & o2) { base::addOwner(o1); //qualified call of base::addOwner base::addOwner(o2); //in case there are other bases } Owned(std::list<Owner*> lo) : base(lo.begin(), lo.end()) { } };
In the case where you have many different types of Owners, this solution can become rather difficult to use. In this case, you might want to look at the boost metaprogramming libraries (MPL, Fusion), with which you could end up with a code that let you do stuffs like that:
class Owned : public Notifier<Owned, OwnerType1, OwnerType1, OwnerType2> { Owned(OwnerType1 & o1, OwnerType1 & o2, OwnerType2 & o3) : base(o1,o2,o3) };
However, implementing this solution would be a little longer than the previous one.
Xavier Nodet : This is very interesting if one needs a dynamic list of owners. In my case, I know beforehand how many owners I have of each type (usually one, sometimes two, never more).
0 comments:
Post a Comment