Search code examples
c++enet

Specify callback function in enet


The ENet library has packets that can be send, and includes a callback function once it has finished sending that specific packet.

http://enet.bespin.org/structENetPacket.html#ad602d6b6b35ef88b2b2e080fa5c9dc3d

typedef struct _ENetPacket
{
   size_t                   referenceCount;  /**< internal use only */
   enet_uint32              flags;           /**< bitwise-or of ENetPacketFlag constants */
   enet_uint8 *             data;            /**< allocated data for packet */
   size_t                   dataLength;      /**< length of data */
   ENetPacketFreeCallback   freeCallback;    /**< function to be called when the packet is no longer in use */
   void *                   userData;        /**< application private data, may be freely modified */
} ENetPacket;

The callback itself:

typedef void (ENET_CALLBACK * ENetPacketFreeCallback) (struct _ENetPacket *);

Now I want to create a class that holds the host used to send those packets, and also to keep track how many packets were successfully sent.

template<typename T>
class Sender {
    public:
        explicit Sender() { }
        void send(T* data, int32_t length)
        {
            ENetPacket* packet = enet_packet_create(data, length, ENET_PACKET_FLAG_RELIABLE);
            packet->freeCallback = callbackPacket;
            enet_host_broadcast(m_server, 0, packet);
        }
        
        void callbackPacket(ENetPacket* packet)
        {
            --m_counter_packets_active;
        }
};

This does not compile: Error C3867 Sender<int32_t>::callbackPacket': non-standard syntax; use '&' to create a pointer to member

When I try

packet->freeCallback = &this->callbackPacket;

I get Error C2440 '=': cannot convert from 'void (Sender<int32_t>::* )(ENetPacket *)' to 'ENetPacketFreeCallback'

I just don't understand what the proper code would be for the packet calling the Sender object's method when the packet is done with.


Solution

  • Okay, this is pretty common. First, you can't call a non-static member method this way, not directly. Pain in the ass.

    But that callback structure has a userData field. And that's what we're going to use.

     void send(T* data, int32_t length) {
         ENetPacket* packet = enet_packet_create(data, length, ENET_PACKET_FLAG_RELIABLE);
         packet->freeCallback = &Sender::myStaticMethod;
         packet->userData = this;  // This is part of the magic
         enet_host_broadcast(m_server, 0, packet);
     }
    
     static void myStaticMethod(ENetPacket * packet) {
         Sender * sender = static_cast<Sender *>(packet->userData);
         sender-> callbackPacket(packet);
     }
    

    In other words -- store this as your user data. Use a static method for the callback, and have him turn it around to call your real callback.