Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Callback with interfaces or function objects? [closed]

In OO, one usually implements callbacks with interfaces: (rough example)

class Message {}

class IMsgProcessor {
public:
     virtual void handle_msg(const Message& msg) = 0;
}

class RequestMsgProcessor : public IMsgProcessor {
     virtual void handle_msg(const Message& msg)  {
     // process request message
    }
}

class CustomSocket {
public:
   Socket(IMsgProcessor* p) : processor_(p) {}

   void receive_message_from_network(const Message& msg) {
       // processor_ does implement handle_msg. Otherwise a compile time error. 
       // So we've got a safe design.
       processor_->handle_msg(msg);
   }
private:
   IMsgProcessor* processor_;
}

So far so good. With C++11, another way to do this is to have CustomSocket just receive an instance of std::function object. It does not care where it is implemented or even if the object is a non-null value :

class CustomSocket {
public:
   Socket(std::function<void(const Message&)>&& f) : func_(std:forward(f)) {}

   void receive_message_from_network(const Message& msg) {
       // unfortunately we have to do this check for every msg.
       // or maybe not ...
       if(func_)
            func_(msg);
   }
private:
   std::function<void(const Message&)> func_;
}

Now here are the questions:
1. What about the performance impacts? I'm guessing a virtual function call is faster than calling a function object but how much faster? I'm implementing a fast messaging system and I'd rather avoid any unnecessary performance penalty.
2. In terms of software engineering practices, I have to say I like the second approach better. Less code, fewer files, less clutter: no interface class. More flexibility: you can only implement a subset of the interface by setting some of the function objects and leaving the others null. Or you can have different parts of the interface implemented in separate classes or by free functions or combination of both (instead of in a single subclass). Furthermore, CustomSocket can be used by any class not just subclasses of IMsgProcessor. This is a great advantage, in my opinion.
What do you say? Do you see any fundamental flaw in these argument ?

like image 595
PoP Avatar asked Nov 29 '12 14:11

PoP


People also ask

What is a callback interface?

Android Callback Listeners Example:Android maintains the interaction between the end-user and the application using the widely used Listener Design Pattern. All the UI components, like a button, inherit from the View class, which in turns implements the Callback interface from android.

What is true about a callback function?

A callback function is a function that is passed as an argument to another function, to be “called back” at a later time. A function that accepts other functions as arguments is called a higher-order function, which contains the logic for when the callback function gets executed.

What's the difference between callback and function?

The main difference between a normal function and a callback function can be summarized as follows: A normal function is called directly, while a callback function is initially only defined. The function is only called and executed once a specific event has occurred.

How do you call a callback function in C++?

In simple language, If a reference of a function is passed to another function as an argument to call it, then it will be called as a Callback function. In C, a callback function is a function that is called through a function pointer. In C++ STL, functors are also used for this purpose.


1 Answers

You can have the best of both worlds

template<class F>
class MsgProcessorT:public IMsgProcessor{
  F f_;
  public:
  MsgProcessorT(F f):f_(f){}
  virtual void handle_msg(const Message& msg)  {
      f_(msg);
 }

};
template<class F>
IMsgProcessor* CreateMessageProcessor(F f){
    return new MsgProcessor<T>(f);

};

Then you can either use like this

Socket s(CreateMessageProcessor([](const Message& msg){...}));

Or to make it even easier add another constructor to Socket

class Socket{
...
template<class F>
Socket(F f):processor_(CreateMessageProcessor(f){}


};

Then you could do

Socket s([](const Message& msg){...});

And still have the same efficiency as a virtual function call

like image 172
John Bandela Avatar answered Sep 28 '22 20:09

John Bandela