-
I wanted to avoid needing a background thread, and was able after some experimenting to make this work by calling First, I'd like to know - is this approach correct, or am I inviting big trouble ? Second, I spent a few hours tracking down an annoying little problem with the loop. I found that my code would refuse to work after I had done what I thought to be some simple and straightforward restructuring. I worked around the problem by having the ctor of a static helper class instance call Ideas? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 4 replies
-
That's an okay solution. You don't need to worry about loop::get. Initializing it like you do is intended. Loop::get is lazily initialized so you can even put your initialization in main because nothing with call it before you do. |
Beta Was this translation helpful? Give feedback.
-
In case it could be useful for someone else, here's my current class: (It's called DISCLAIMER: this is an early version and far from production-ready. Header file: #pragma once
#include <string_view>
#include <functional>
#include <vector>
#include <uWebSockets/App.h>
#include <libusockets.h>
class RC_Channel {
public:
using Incoming_Message_Handler = std::function<void(std::string_view)>;
RC_Channel();
auto listen(int port) -> RC_Channel&;
void close();
auto on_incoming_message(Incoming_Message_Handler) -> RC_Channel&;
auto send_message(std::string_view) -> RC_Channel&;
void run_once();
private:
struct Per_Connection_Data { };
using websocket_t = uWS::WebSocket<false, true, Per_Connection_Data>;
uv_loop_t *_uv_loop = nullptr;
Incoming_Message_Handler _incoming_message_handler;
us_listen_socket_t *listen_socket = nullptr;
uWS::TemplatedApp<false> ws_app;
uWS::Loop *ws_loop = nullptr;
websocket_t *connection = nullptr;
}; Source file: #include <cassert>
#include <uWebSockets/App.h>
#include <uv.h>
#include "./rc_channel.h"
static class Initializer {
public:
Initializer() {
(void) uWS::Loop::get(uv_default_loop());
}
} initializer;
RC_Channel::RC_Channel():
ws_loop{ uWS::Loop::get(uv_default_loop()) },
ws_app{
uWS::App().ws<Per_Connection_Data>("/*", {
.open = [&](auto* ws) {
std::cout << "Opening connection" << std::endl;
assert(!connection);
connection = ws;
},
.message = [this](auto* ws, std::string_view message, uWS::OpCode opCode) {
// std::cout << "Incoming message: " << message << std::endl;
if (_incoming_message_handler)
_incoming_message_handler(message);
},
.drain = [](auto* /*ws*/) {
/* Check ws->getBufferedAmount() here */
},
.ping = [](auto* /*ws*/, std::string_view) {
/* Not implemented yet */
std::cout << "Ping" << std::endl;
},
.pong = [](auto* /*ws*/, std::string_view) {
/* Not implemented yet */
std::cout << "Pong" << std::endl;
},
.close = [&](auto* ws, int /*code*/, std::string_view /*message*/) {
/* You may access ws->getUserData() here */
std::cout << "Connection closed" << std::endl;
assert(connection);
connection = nullptr;
}
})
}
{
}
auto RC_Channel::listen(int port) -> RC_Channel&
{
ws_app
.listen(port,
[&](auto* listen_socket_) {
if (listen_socket_) {
std::cout << "Listening on port " << port << std::endl;
listen_socket = listen_socket_;
}
else {
std::cout << "No listen socket!" << std::endl;
}
});
return *this;
}
void RC_Channel::close()
{
if (connection) {
ws_loop->defer([&]() { connection->close(); });
}
us_listen_socket_close(0, listen_socket);
}
auto RC_Channel::on_incoming_message(Incoming_Message_Handler handler) -> RC_Channel&
{
assert(!_incoming_message_handler);
_incoming_message_handler = handler;
return *this;
}
auto RC_Channel::send_message(std::string_view msg) -> RC_Channel&
{
if (connection) {
connection->send(msg, uWS::TEXT, false);
}
return *this;
}
void RC_Channel::run_once()
{
uv_run(uv_default_loop(), UV_RUN_NOWAIT);
} |
Beta Was this translation helpful? Give feedback.
That's an okay solution. You don't need to worry about loop::get. Initializing it like you do is intended. Loop::get is lazily initialized so you can even put your initialization in main because nothing with call it before you do.