c++telnetstdbind

Error while binding telnet_event_handler_t using std::bind


I am trying to design telnet-client.c in CPP. While initializing the telnet client, an event handler having the structure telnet_event_handler_t must be passed as shown below (see implementation here):

static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
        void *user_data) {
    // process callback here
}

telnet = telnet_init(telopts, _event_handler, 0, &sock_fd);

In my CPP code, I am calling telnet_init inside a class constructor using std::bind as shown below:

telnet = telnet_init(telnetOpts,
                     std::bind(&TelnetClient::telnetEvent, this, _1, _2, _3),
                     0, &sock_fd);

However, std::bind throws compilation errors shown below:

Scanning dependencies of target client
[ 33%] Building CXX object CMakeFiles/client.dir/src/simple_telnet_client.cpp.o
/home/ravi/simple_telnet_client/src/simple_telnet_client.cpp: In constructor ‘simple_telnet_client::TelnetClient::TelnetClient(const string&, const string&, int)’:
/home/ravi/simple_telnet_client/src/simple_telnet_client.cpp:32:33: error: cannot convert ‘std::_Bind_helper<false, void (simple_telnet_client::TelnetClient::*)(telnet_t*, telnet_event_t*, void*), simple_telnet_client::TelnetClient*, const std::_Placeholder<1>&, const std::_Placeholder<2>&, const std::_Placeholder<3>&>::type’ {aka ‘std::_Bind<void (simple_telnet_client::TelnetClient::*(simple_telnet_client::TelnetClient*, std::_Placeholder<1>, std::_Placeholder<2>, std::_Placeholder<3>))(telnet_t*, telnet_event_t*, void*)>’} to ‘telnet_event_handler_t’ {aka ‘void (*)(telnet_t*, telnet_event_t*, void*)’}
   32 |                        std::bind(&TelnetClient::telnetEvent, this, _1, _2, _3),
      |                        ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                                 |
      |                                 std::_Bind_helper<false, void (simple_telnet_client::TelnetClient::*)(telnet_t*, telnet_event_t*, void*), simple_telnet_client::TelnetClient*, const std::_Placeholder<1>&, const std::_Placeholder<2>&, const std::_Placeholder<3>&>::type {aka std::_Bind<void (simple_telnet_client::TelnetClient::*(simple_telnet_client::TelnetClient*, std::_Placeholder<1>, std::_Placeholder<2>, std::_Placeholder<3>))(telnet_t*, telnet_event_t*, void*)>}
In file included from /home/ravi/simple_telnet_client/include/simple_telnet_client/simple_telnet_client.hpp:10,
                 from /home/ravi/simple_telnet_client/src/simple_telnet_client.cpp:5:
/usr/include/libtelnet.h:376:26: note:   initializing argument 2 of ‘telnet_t* telnet_init(const telnet_telopt_t*, telnet_event_handler_t, unsigned char, void*)’
  376 |   telnet_event_handler_t eh, unsigned char flags, void *user_data);
      |   ~~~~~~~~~~~~~~~~~~~~~~~^~
make[2]: *** [CMakeFiles/client.dir/build.make:63: CMakeFiles/client.dir/src/simple_telnet_client.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:76: CMakeFiles/client.dir/all] Error 2
make: *** [Makefile:84: all] Error 2

I am using GCC/G++ 9.4.0 on Ubuntu 20.04.4 LTS.

Update

Defining telnetEvent as friend seems to be working. BUT when using the friend, the send function fails by saying "send() failed: Socket operation on non-socket".

The telnetEvent is defined as shown below:

void telnetEvent(telnet_t *telnet, telnet_event_t *event, void *user_data) {
  TelnetClient *client = static_cast<TelnetClient *>(user_data);

  switch (event->type) {
    // data must be sent
    case TELNET_EV_SEND:
      client->send_all(event->data.buffer, event->data.size);
      break;
    // more cases here
  }
}

In header file:

friend void telnetEvent(telnet_t *telnet, telnet_event_t *event, void *user_data);

In class constructor:

sock_fd = get_socket();
telnet = telnet_init(telnetOpts, telnetEvent, 0, &sock_fd);

Implementation of send_all:

int TelnetClient::send_all(const char *buffer, size_t size) {
  int ret_val = -1;  // default value

  // get the current address
  char *current = (char *)buffer;

  // send data
  while (size > 0) {
    ret_val = send(sock_fd, buffer, size, 0);
    if (ret_val == 0 || errno == EINTR) {
      // try again
      continue;
    } else if (ret_val == -1) {
      fprintf(stderr, "send() failed: %s\n", strerror(errno));
      exit(EXIT_FAILURE);
    }

    // update pointer and size to see if we've got more to send
    buffer += ret_val;
    size -= ret_val;
  }
  return ret_val;
}

Question

How to call telnet_init from a class?


Solution

  • Do a trampoline to jump:

    class TelnetClient {
       static void trampoline(telnet_t *telnet, telnet_event_t *event, void *user_data) {
            TelnetClient *me = static_cast<TelnetClient *>(user_data);
            me->telnetEvent(telnet, event);
       }
    
        TelnetClient() {
           telnet_init(telnetOpts, &trampoline, 0, this);
        }
    
        void telnetEvent(telnet_t *telnet, telnet_event_t *event);
    };