简体   繁体   中英

Obtain a Pointer to the Calling Object in C++

My question is reasonably simple: How do I obtain a pointer to the object calling a function, without passing this ?

The reason I am asking, however, is slightly more complex.

I am led to ask this because I am writing a class (named SubordnateRegistry ), which contains a heterogeneous list (of lists) of function delegates --- callable objects with different signatures.

Whichever classes wish to have delegates called must register themselves, of course, with the type of data which they wish to receive, and either a pointer to the object or an already-constructed delegate. A call from inside a class Bar might look like this:

ARegistry.Subscribe <Foo> (& Bar:: BarFunction, this);

Or like this:

using namespace std:: placeholders;
auto Func = std:: bind (& Bar:: BarFunction, this, _1);
ARegistry.Subscribe <Foo> (Func);

Because std:: function is more versatile (you can bind any callable, including lambdas), I would prefer to use that over raw pointers.

My problem lies in the fact that I have to specify this all the time. If I pass an invalid pointer, or end up registering an object which is not related at all, then I will end up invoking UB at some indeterminate point in the future with no idea where it came from or where the root of the error is. Nasty!

Now, if you're wondering what happens if the caller is not an object (a free function), then the element will simply remain registered forever, as I will never automatically remove (unregister) the callable, as it never destructs.

Any ideas, or is this impossible as I was led to believe? Is preprocessor magic an option? Is there a better way?

Of course, any questions are welcome. Thanks for your time :).

You could use a #define to include this automatically, for instance.

#define REG_SUBSCRIBE(t,f) ARegistry.Subscribe <t> (f, this)

Of course that could turn ugly fast, so I don't know if I would recommend it.

If you are worried about keeping track and releasing them, then I would recommend returning an object with a destructor which unsubscribes the thing. Alternatively (or in addition) providing a method call to do that.

I gave it a shot using the curiously recurring template pattern. The idea is to encapsulate the registration of a method in a class whose destructor will automatically unregister the client object on destruction. The Client derives from this class. This way the destructor will be called automatically, and the registration class has implicit access to the clients this pointer. Such that it is not necessary to pass it explicitely.

The Visitor class

Throughout this answer I will assume the following visitor class:

// The visitor class. Clients can register methods here.
class Visitor {
  public:
    // Expects void(void) methods.
    template <class T>
    using method_t = void (T::*)();

    // Register a new method of a client.
    template <class T>
    void subscribe(method_t<T> method, T* object);

    // unsubscribe all methods of a given client.
    template <class T>
    void unsubscribe(T* object);
};

A registration of a method

The registration class looks like this:

// CRTP registration class.
// Will automatically subscribe and unsubscribe its base class.
template <class Derived>
class Registration {
  public:
    // The constructor takes a reference to the visitor,
    // and a pointer to the method that should be registered.
    // It will then register this particular method.
    Registration(Visitor &visitor, Visitor::method_t<Derived> method)
        : visitor_(visitor) {
        visitor_.subscribe(method, static_cast<Derived *>(this));
    }

    // The destructor calls the visitors unsubscribe method unregistering
    // all methods of this particular client.
    virtual ~Registration() {
        visitor_.unsubscribe(static_cast<Derived *>(this));
    }

  private:
    Visitor &visitor_;
};

A client

An actual client would then look like this.

// A client using the registration class.
// Has to inherit publically, otherwise the `this` pointer cast will fail.
class Client : public Registration<Client> {
  public:
    Client(Visitor &visitor)
        : Registration<Client>(visitor, &Client::method) {}

    void method();
};

You can find an example usage here .

A few notes:

  • Since the registration class has non-trivial destructor, you need to consider the rule of five, and need to decide what to do about the copy-/move- constructor/assignment.
  • This pattern needs to be changed if you want to register multiple methods of one object. Consider this a rough sketch for how you could solve your problem. The Registration class could register methods in a publically available member function instead of the constructor. If, for some reason, you need to inherit from Registration multiple times, consider factoring out the reference to the visitor and base class chaining.
  • The registration could also store the method pointer, if the unsubscribe method requires that one as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM