Re: [ROOT] how to know what is the class of the object calling a member function of another class

From: Christian Holm Christensen (cholm@hehi03.nbi.dk)
Date: Fri Oct 31 2003 - 13:29:52 MET


Hi Frankland,

frankland@ganil.fr (Frankland John) wrote concerning
  [ROOT] how to know what is the class of the object calling a member function of another class [Thu, 30 Oct 2003 12:11:06 +0100] 
----------------------------------------------------------------------
> is there a way to know who has called a member function in a class ?

In C++, no.  However, you can use the `Visitor' [1] pattern [2] to get 
something similar.  The visitor pattern is a design, that allows
multiple classes to do various operations on a class.   Here's an
implementation that uses templates: 


  //____________________________________________________________________
  #ifndef __IOSTREAM__
  # include <iostream>
  #endif
  
  //____________________________________________________________________
  /** @class visitor_base 
      @brief base class for all visitors.  Needed for the
      `dynamic_cast' below. 

      User classes that needs to visit hosts, must derive from this
      class. 
    */
  class visitor_base 
  {
  public:
    virtual ~visitor_base() {}
  };
  
  //____________________________________________________________________
  /** @class basic_visitor
      @brief Class template to implement a visitor of class @a Host. 
      @param Host   The hosting class (class to visit)
      @param Return The return type of member function @c visit 

      User classes that wants to visit a host should derive from this
      class, passing the appropriate Host type as argument to the
      template.  The user class should then override the visit member
      function for each type of host it wants to visit. 
   */
  template <typename Host, typename Return=void> 
  class basic_visitor
  {
  public:
    /** The return type */
    typedef Return return_type; 
    /** Visit an arbitrary object of type @a Host
        @param host The host object to visit 
        @return an object of return_type */
    return_type visit(Host& host1) {}
  };
  
  //____________________________________________________________________
  /** @class basic_host
      @brief Class template for visitable (host) classes 
      @param Return The return type of the host member function.
      
      User class that should host visitors, must derive from this
      class.  The user class should also use the VISITABLE
      preprocessor macro in a public section to override the abstract
      member function host.  
   */
  template <typename Return=void>
  class basic_host
  {
  public:
    /** The return type of the host member function */
    typedef Return return_type;
  protected:
    /** Dynamically figure out the visitor class 
        @param Host The class of the hosting the visitor.
        @param h The object hosting the visitor
        @param v The visitor object
        @return object of return_type */
    template<typename Host>
    static return_type do_host(Host& h, visitor_base& v) 
    {
      if (basic_visitor<Host,Return>* vp 
  	= dynamic_cast<basic_visitor<Host,Return>*>(&v)) 
        return return_type(vp->visit(h));
      return return_type();
    }
  public:
    /** Hosting member function 
        @param v The visitor to host
        @return An object of the @a Result type */
    virtual return_type host(visitor_base& v) = 0;
  };
  
  #define VISITABLE \
    virtual return_type host(visitor_base& v) { return do_host(*this,v); } 


An example of using this pattern is given below. 
  
  //____________________________________________________________________
  /** @class foo 
      @brief class foo is a host class. 
    */
  class foo : public basic_host<>
  {
  private:
    int _foo; /** Data */
  public:
    /** Construtor
        @param f Value to set */
    foo(int f) : _foo(f) {}
    /** Destructor  */
    virtual ~foo() {}
    /** Get the data  
        @return the data */
    int get_foo() const { return _foo; }
    /** Set the data
        @param d the new value */
    void set_foo(int d) { _foo = d; }
    // MUST be used. 
    VISITABLE  
  };
  
  //____________________________________________________________________
  /** @class bar 
      @param bar is a host class
    */
  class bar : public basic_host<>
  {
  private:
    int _bar;  /** Data */
  public:
    /** Construtor
        @param b Value to set */
    bar(int b) : _bar(b) {}
    /** Destructor  */
    virtual ~bar() {}
    /** Get the data  
        @return the data */
    int get_bar() const { return _bar; }
    /** Set the data
        @param d the new value */
    void set_bar(int d) { _bar = d; }
    // MUST be used. 
    VISITABLE  
  };
  
  //____________________________________________________________________
  /** @class visitor 
      @brief A base class for visitors of foo and bar objects. 
      
      Note, that this is an abstract class.  It does not override the
      visit member functions.
    */  
  class visitor : public visitor_base, 
  		  public basic_visitor<foo, void>,
  		  public basic_visitor<bar, void> 
  {};
  
  //____________________________________________________________________
  /** @class print_visitor 
      @brief A visitor that prints the data stored in the hosts. 
    */
  class print_visitor : public visitor
  {
  public:
    /** Visit a foo object 
        @param f foo object to visit */
    void visit(const foo& f) 
    { 
      std::cout << "foo is " << f.get_foo() << std::endl;
    }
    /** Visit a bar object 
        @param b bar object to visit */
    void visit(const bar& b) 
    { 
      std::cout << "bar is " << b.get_bar() << std::endl;
    }
  };
  
  //____________________________________________________________________
  /** @class set_visitor 
      @brief A visitor that sets the data stored in the visited
      hosts. 
    */
  class set_visitor : public visitor
  {
  public:
    /** Visit a foo object 
        @param f foo object to visit */
    void visit(foo& f) 
    { 
      std::cout << "setting foo to " << 0 << std::endl;
      f.set_foo(0);
    }
    /** Visit a bar object 
        @param b bar object to visit */
    void visit(bar& b) 
    { 
      std::cout << "setting bar to " << 0 << std::endl;
      b.set_bar(0);
    }
  };
  
  
  //____________________________________________________________________
  /** @func main 
      @brief Example program that uses the above classes. 
    */
  int main(int argc, char** argv) 
  {
    foo           f(42);
    bar           b(10);
    print_visitor p;
    set_visitor   s;
  
    p.visit(f);
    p.visit(b);
    s.visit(f);
    s.visit(b);
    p.visit(f);
    p.visit(b);
    
    return 0;
  }
  
  //____________________________________________________________________
  
Put these to code fragments in a file (say `visit.cc'), and compile it
(say `g++ visit.cc -o visit').  Running the resulting program gives
you:

  foo is 42
  bar is 10
  setting foo to 0
  setting bar to 0
  foo is 0
  bar is 0

So, you see, to get some information on what kind of object called
a member function, you can use let the class that needs that
information be a visitor, and the classes that calls the member
functions be hosts. 

Another, more tricky solution is the so-called `Multi-methods', which
trigger not only on the calling context, but on both the caller and
callee context.  This is documented in detail in Andrei Alexandrescu's
excellent boot `Modern C++ Design' [3].  That book also explains the
`Visitor' patterns and extends it pretty far. 
      
> SomeClass::SomeFunction ( SomeArguments...){
> 
>     if ( 
> getClassOfCallingObjectHere->InheritsFrom("SomeOtherRootBasedClass") )
>     {
>         doSomethingSpecific();
>     }
> }

To do something like this, you'd need access to the call stack in the
API. 


> To be more specific, what I want to be able to do is to know if the
> dtor of one of my classes has been called by a class derived from
> TList or not, and do different things as a consequence.

I think this is sort of contrary to the collection design in ROOT.
Perhaps you'd be better of setting a flag in the object you may have
added to a container: 

  SomeClass c;
  TList     l;
  l.Add(&c);
  c.SetInContainer(kTRUE);

  SomeClass::~SomeClass() 
  { 
    if (IsInContainer()) 
      ...
  }

Hmm.  Perhaps some sort of smart pointer with policies usage in the
collection classes would be a good idea.  I believe that in the
up-coming C++ 0x standard, the SmartPtr of [3] will be incorporated in
the Standard Library. 

Yours, 

 ___  |  Christian Holm Christensen 
  |_| |	 -------------------------------------------------------------
    | |	 Address: Sankt Hansgade 23, 1. th.  Phone:  (+45) 35 35 96 91
     _|	          DK-2200 Copenhagen N       Cell:   (+45) 24 61 85 91
    _|	          Denmark                    Office: (+45) 353  25 404
 ____|	 Email:   cholm@nbi.dk               Web:    www.nbi.dk/~cholm
 | |

[1] http://patterndigest.com/patterns/Visitor.html
[2] http://www.hillside.net/patterns/
[3] http://www.moderncppdesign.com/



This archive was generated by hypermail 2b29 : Thu Jan 01 2004 - 17:50:16 MET