I’m designing classes for my application (network tool). I this base class:
class Descriptor
{
// ...
public:
virtual void data_read (void);
virtual void data_write (void);
virtual void data_error (void);
protected:
int socket_descriptor;
// ...
}
class TcpClient :
public Descriptor
{
// ...
}
Many classes are based on Descriptor. I monitor sockets’ events using epoll. When I want to look for events on TcpClient object I add the object’s socket and pointer to this object to epoll, code:
epoll_event descriptor_events;
descriptor_events.events = EPOLLIN;
descriptor_events.data.fd = this->socket_descriptor;
descriptor_events.data.ptr = this;
epoll_ctl (epoll_descriptor, EPOLL_CTL_ADD, this->socket_descriptor, &descriptor_events);
I dispatch epoll events in separate thread in this way:
Descriptor *descriptor (NULL);
// ...
return_value = epoll_wait (epoll_descriptor, events, 64, -1);
while (i < return_value)
{
descriptor = (Descriptor *) (events [i]).data.ptr;
if ((events [i]).events & EPOLLOUT)
descriptor->data_write ();
if ((events [i]).events & EPOLLIN)
descriptor->data_read ();
if ((events [i]).events & EPOLLERR)
descriptor->data_error ();
i++;
}
Program is going to handle a lot of data in the epoll thread, so it mean, that virtual functions will be called many times there. I’m wondering about runtime cost of this solution.
I’m also thinking about two other implementations (however I’m not sure if the’re much faster):
typedef void (*function) (Descriptor *) EventFunction;
class Descriptor
{
// ...
public:
EventFunction data_read;
EventFunction data_write;
EventFunction data_error;
protected:
Descriptor (EventFunction data_read,
EventFunction data_write,
EventFunction data_error);
int socket_descriptor;
// ...
}
or use CRTP.
Maybe you have other idea of implementing this?
Unless proven otherwise, your original design looks fine to me.
The first rule of optimization is to measure first, then fix only hotspots that really exist. You’ll be surprised where your code spends its time. Dwelling on the distinction between virtual functions and function pointers is almost certainly premature optimization. In both cases the compiler will generate code to jump to a function pointer, though with virtual functions the compiler will have to look up the vtable first. Write idiomatic C++ code to do what you want, then profile it if you have performance problems.
(I do have one comment about your class
Descriptor: Unless you’re planning on having generic data_read(), data_write(), and data_error() methods I’d recommend making them pure virtual methods.)