0

on more than one occasion I felt the need to define class methods that get called in a manner similar to constructors or destructors.

A specific example would be; in a program I had a very complex network of nodes of different types that depended mutually on each other in a very irregular fashion (The network did not resemble a tree at all). When a node needed to be destroyed, it started a complex chain of destructions in the network. Much like a spider web being torn apart, but more complex.

During the execution of this chain, the control came back to the methods of the initiator (or one of the intermediate elements in the chain), so that the actual destruction had to take place when the chain had settled, and that's why I couldn't use destructors for this purpose. However, along the class hierarchy of my nodes, I needed a "destructor like", i.e. a ladder way of calling my non-destructing pre-destruct function (for exactly the same reasons why an actual destructor is also called that way, namely, every step in the class hierarchy needed to contribute to the chain in a different way).

I ended up coding the ladder by hand. Namely, the class nodeBase has a method called "preDestroyNodeBase" which does its job and calls the virtual method "preDestroyNode" and so on until the leaf (I know, this way it looks like a constructor, but it was -comparatively- more elegant that way, since you can just call the "preDestroy" of the most base class).

You can imagine how error prone this approach is, not to mention ugly. Is there a cleaner way of emulating constructor or destructor way of calling methods? Some kind of template magic or even macro magic! Because hand coding it is too error-prone, even for a single programmer, so I cannot imagine exposing this kind of behavior to the clients of a library.

Maybe I'm missing a fundamental programming concept that obsoletes the need for such functions. If that is the case, I'd be glad if you pointed how else that network of nodes example could be handled.

Thanks a lot!

enobayram
  • 4,650
  • 23
  • 36
  • After reading your question, I had no idea what your destructors should actually achieve. The problem is: If you can't explain the semantics of your node network clearly in the context of a SO question (and have to resort to vague expressions like "destructor like"), the design is probably very unclear. If it is unclear to you, it will be a jungle to others. So, can you state clearly in few sentences what function you want realized? If not, consider changing your design to something simpler and less elegant. – thiton Jan 19 '12 at 14:34
  • Thanks for your comment; I'm sorry for having to use vague expressions, but I'm really unable to find a term for it. I've been searching for an answer to this question for a long time now. If you are not interested in the example or the details of why I am asking this question, the question is very simple. "What is the most elegant way of defining functions which call their overriding functions (or the other way round) in a chain across the class hierarchy?". I've used the constructors and the destructors as examples since they are called this way by design. – enobayram Jan 19 '12 at 14:58
  • If I understand you right, you just need to call a virtual 'preDestroy' function, which will go to the most-derived class, and then just make sure every handler of 'preDestroy' is calling `__super::preDestroy`, this will go from the most-derived to the next most, and so on until it gets to the base class that defined the original virtual method. – Nic Foster Jan 19 '12 at 15:16
  • That's exactly what I want, but: (1) Will __super::preDestroy work correctly for virtual functions, especially if __super doesn't override it. -It doesn't have to...- (2) Is there a way to enforce this, rather than hoping that people will abide by the documentation. – enobayram Jan 19 '12 at 15:24

3 Answers3

2

When you declare a virtual function it will call to the most-derived handler of the function, and then from there you have each handler call NextMostDerivedClass::preDestroy and the calls will go to the next-most-derived handler of the function, and you can again call NextMostDerivedClass::preDestroy, and so on. This is the same as the path a virtual destructor takes, except that you don't have to call anything manually with destructors, it's automated. If you were to put the cout statements from the below code sample into your destructors, you could see the same output as the sample below provides.

#include <iostream.h>

class Foo
{
    public:
    virtual void PreDestroy()
    {
        cout << "Foo preDestroy";
    }
}

class Bar : public Foo
{
    public:
    void PreDestroy()
    {
        cout << "Bar preDestroy\n\n";

        Foo::PreDestroy();
    }
}

class MostDerived : public Bar
{
    public:
    void PreDestroy()
    {
        cout << "MostDerived preDestroy\n\n";

        Bar::PreDestroy();
    }
}

int main() 
{
    MostDerived testObj;

    testObj.PreDestroy();
}

Output should be:

MostDerived preDestroy

Bar preDestroy

Foo preDestroy

Nic Foster
  • 2,864
  • 1
  • 27
  • 45
  • 1
    Please note that `__super` is a feature of Microsoft C++, and is not likely to appear in other implementations of C++. – Robᵩ Jan 19 '12 at 15:29
  • Thanks, that is true, I will alter my example to conform to the C++ standard. – Nic Foster Jan 19 '12 at 15:36
  • Thank you very much for the answer, I didn't know virtual functions could be called that way, I thought a call to the __super ::PreDestroy() would still call the most derived. This solution is not as automated as I dreamed, but it solves %90 of the problem. – enobayram Jan 19 '12 at 15:38
  • @enobayam: You're welcome, but please not what Rob said in the first comment here, that you cannot use __super unless you're using Microsoft's C++, instead you must call the next most derived base class by name. I've changed the code sample to reflect this. – Nic Foster Jan 19 '12 at 15:40
  • 1
    BTW __super indeed doesn't work in GCC, and it's somewhat a problem, since the programmer could change the class hierarchy but forget to change the scope resolution inside PreDestroy(). Ideally, the base would be stated in only one place (which should be possible with a typedef I guess.). Thanks for the answer again, I'm accepting it, since searching for anything more complicated for perfect automation is probably an overkill... – enobayram Jan 19 '12 at 15:43
  • @enobayram See [this answer](http://stackoverflow.com/a/6041145/8747) an alternative in gcc. – Robᵩ Jan 19 '12 at 15:45
  • @enobayram: You can just `typedef TheBaseClass base;`, however you should not forget to retypedef in every derived class, otherwise you get holes in your `preDestroy` chain. – Xeo Jan 19 '12 at 15:48
  • @Rob,Xeo all these "should not forget"s scare me. Especially since they will result in very subtle bugs. At least in a class (as opposed to a struct), if you do it as the first thing, derived classes will not be able to use it, since it'll be private. – enobayram Jan 19 '12 at 16:21
0

I'd use a subscription mechanism, where if you want a to hold a pointer to another object, you also have to subscribe to their destruction event; on receiving such an event, cancel their subscription to your own event, and clean up your own structures (which may then lead to further destruction).

i.e. (C++1x syntax for brevity)

struct node {
    ~node() { for(auto i : subscribers) i->notify(this); }
    void subscribe(node *n) { subscribers.push_back(n); }
    void notify(node *n) { subscribers.remove(n); /* Handle other node being removed */ }

private:
    std::list<node *> subscribers;
}

To get access to the node's data members, you need to make them sub-objects of the node class, either by extending the node class itself, or by using a templated node class that inherits from the class actually containing the data.

Simon Richter
  • 28,572
  • 1
  • 42
  • 64
  • Thanks, this is actually similar to my initial approach, but the problem here is this: The function calls in a ladder fashion starts, only when the actual destructions are happening. But during that phase, I need the information on the objects that I had previously subscribed to. – enobayram Jan 19 '12 at 16:26
  • Take a look at the object lifetimes. The `node` object is being destructed (i.e. its lifetime has ended), but any sub-objects (superclasses and members) are still alive. It is thus unsafe to call (virtual) methods, but data members can be accessed, provided these are members of the `node` class. I'm extending my answer with an approach for polymorphic nodes... – Simon Richter Jan 19 '12 at 17:33
  • Erm, wait, polymorphic nodes are more difficult. Scratch that. :) – Simon Richter Jan 19 '12 at 17:39
0

You may make use of the visitor pattern to go through your nodes graph (I assume you are talking about a graph) and let the nodes put themselves into a kind of "to-be-deleted" list (if needed) which is hold by the visitor object. In a second iteration you can go through that list elements and call a kind of "destroy" method on them which in turn does the final cleanup. This approach needs to have all your nodes supporting the visitor pattern and containing that "destroy" method.

boto
  • 455
  • 2
  • 13