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.
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;
}
How to call telnet_init
from a class?
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);
};