0

I am trying to figure out how to do this. I have 2 classes -

class Caller(){
//constructs Callee

    void onEventFired(){
        //need to call a function on an obj
        //which I dont have access to here

        //objptr->funcA
    }
};

class Callee(){
//it has access to an instance of caller object
private:
    void setup(){
        std::unique_ptr objptr = make_unique<SampleClass>....
        //create unique ptr of obj

        //can pass the objptr to Caller through a
        //separate function but probably not clean ??
    }
};

Chain of events - Caller creates the callee instance during its own construction, – later, callee's setup function is called which creates SampleClass pointer. at some point later, the periodic event starts to fire up thats when I want call SampleClass's funcA from within Caller

One way is to pass the raw SampleClass pointer to the Caller class through a separate function but ideally I don't want the class Caller to have access to that. Is there a way using some callbacks which I can do this cleanly.

code4fun
  • 2,661
  • 9
  • 25
  • 39
  • 1
    Typically Callee will implement an interface defined by Caller. Eg, `class Callee: public Caller::callback_client { ... };` or an interface defined elsewhere and visible to both. – user4581301 Dec 09 '21 at 19:48
  • Here's an example of what I describe above: [How to implement callbacks with interface](https://stackoverflow.com/questions/12387740/how-to-implement-callbacks-with-interface) – user4581301 Dec 09 '21 at 19:54
  • These days for a simple interface I'll use [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function) and a lambda expression that captures the instance of the Callee. – user4581301 Dec 09 '21 at 19:55
  • @jxh I have 2 classes. mistyped it :) – code4fun Dec 09 '21 at 19:57
  • I'm starting to think I misunderstood your first outline of who's calling who. :-) – Ted Lyngmo Dec 09 '21 at 20:09
  • @Ted Pretty sure you did. I was just copying your examples to demo one with `std::function` and things look cock-eyed. – user4581301 Dec 09 '21 at 20:12
  • @user4581301 :-) I bet. I got tricked by the `setup` function I think. What's the `std::unique_ptr objptr` supposed to be? ... deleting my answer ... – Ted Lyngmo Dec 09 '21 at 20:14
  • @code4fun Are you here to share some details about what the `setup` function is doing and what you'd like the call chain to be? – Ted Lyngmo Dec 09 '21 at 20:18
  • the setup function is just creating unique pointer of "SampleClass" and adds it to some vector. – code4fun Dec 09 '21 at 20:34
  • so "Callee" wasnt right name choice. I just have that unique pointer of "SampleClass" inside callee.I want to be able to call a function on that pointer from within Caller when a periodic event fires – code4fun Dec 09 '21 at 20:34
  • What are you trying to avoid, Caller holding a reference to Callee or Caller knowing that there is a Callee? I don't think there is a clean way to avoid holding a reference, but there are ways to armour it to avoid Callee going out of scope before Caller. – user4581301 Dec 09 '21 at 20:35
  • I want to avoid caller holding reference to the "pointer of SampleClass", – code4fun Dec 09 '21 at 20:37
  • Can it hold a pointer to an abstraction? Hack example: https://ideone.com/0PtrkH – user4581301 Dec 09 '21 at 20:38
  • So `Callee` owns a dynamically created object and `Caller` should call a member function in that object but `Caller` doesn't know anything about neither the `Caller` nor the `SampleClass` instance? I have a hard time figuring out how you want this to work. What's the chain of events? – Ted Lyngmo Dec 09 '21 at 20:46
  • You can have a container of types rather than a container of abstract instances, but you need some kind of container to iterate over everyone that wants a callback. – jxh Dec 09 '21 at 20:47
  • That looks in right direction. I can pass in a function-object created in Caller to Callee's constructor . now I want to tie that function to call SampleClass's funcA inside setup() – code4fun Dec 09 '21 at 20:47
  • @TedLyngmo Caller knows about the Callee instance, it creates the instance – code4fun Dec 09 '21 at 20:48
  • 1
    @code4fun Oh... Ok, it'd be nice to have details like that from the start... :-) – Ted Lyngmo Dec 09 '21 at 20:50
  • caller creates the callee instance during its own construction, – later, callee's setup function is called. at some point later, the periodic event starts to fire up thats when I want call SampleClass's funcA from within Caller – code4fun Dec 09 '21 at 20:51
  • 2
    That's not flexible, it means you have to change caller code whenever you want to add a new callee. With a registration, your callee can be in a totally different library, and still be able to get a callback. – jxh Dec 09 '21 at 20:51
  • @TedLyngmo sorry missed some details earlier – code4fun Dec 09 '21 at 20:52

2 Answers2

0

Your question is a little weak in motivation, so let's beef it up just a tad.

Suppose that Caller accepts registrations for things that want to be called back whenever EVENT_FIRED happens. So, the system has something like this:

    //... initialize all callees
    //... wait for event
    switch (event) {
    //...
    case EVENT_FIRED:
        //...
        //callback all interested callees
        Caller::instance().onEventFired();
        break;
    //...
    default:
        //...
        break;
    };

Typically, you will want the callees to register themselves with the Caller instance, so that they get notification of the event via their registered callback.

In order to accept registrations, you would use some kind of container in the caller to track them.

class Caller {
public:
    struct Callback {
        virtual ~Callback () = default;
        virtual void fire () = 0;
    };

    static Caller & instance () {
        static Caller one;
        return one;
    }

    template <typename CALLBACK, int EVENT>
    void subscribe () {
        std::unique_ptr<Callback> cb(std::make_unique<CALLBACK>());
        callbacks_[EVENT].push_back(std::move(cb));
    }

    //...
    void onEventFired () {
        for (auto &cb : callbacks_[EVENT_FIRED]) cb->fire();
    }

private:
    typedef std::list<std::unique_ptr<Callback>> CallbackList;

    std::unordered_map<int, CallbackList> callbacks_;

    Caller () = default;
    Caller (const Caller &) = delete;
    Caller & operator = (Caller) = delete;
    ~Caller () = default;
};

The Caller now implements the Callback interface, and makes its registration during setup.

class Callee : public Caller::Callback {
public:
    static void setup () {
        Caller::instance().subscribe<Callee, EVENT_FIRED>();
    }
    void fire () { std::cout << "You're fired!\n"; }
};

Try it online!

jxh
  • 69,070
  • 8
  • 110
  • 193
0

Here are 2 references may be what you're looking for. The Attorney-Client idiom, and pass-key pattern.

The Attorney-Client idiom is a method that add a proxy class. The proxy class is a friend of the class which needs access. [Callee] - [Proxy] - [Caller] relationship is built.

Pass-Key pattern is a relatively simple method to solve the problem. The main idea is same that uses friend keyword. However, it's using class template, rooted in template meta programming. For more sophisticated usage, take a look at this version. (the last answer)

ArtLife
  • 36
  • 3