I am working at a program which runs a custom webserver that should output some active HTML content:
// This is the webserver library...
class myWebServer
{
public:
myWebServer() {}
~myWebServer() {}
// ...
void sendPageToClient()
{
// ... "client" is the TCP socket
// ... "html" should contain the output of myMainProgram::ProcessASP
client->send(html);
}
void runServer()
{
while (1)
{
// listens to TCP socket
client->listen();
// receive query from browser
// send HTML using sendPageToClient()
// ...
sendPageToClient();
}
}
};
// This is the main program class...
class myMainProgram
{
public:
myMainProgram() {}
~myMainProgram() {}
// ...
string ProcessASP(string query)
{
return
"<html>The query string you have passed contains:<br>"
+ query +
"</html>";
}
void runProgram()
{
// do something
}
};
// This is a multi-threaded application
void main()
{
myMainProgram myProgram;
myWebServer myServer;
myProgram.runProgram();
myServer.runServer();
};
How can I set up a callback function that from the class myWebServer calls myMainProgram::ProcessASP passing parameters and receiving its output?
You probably want to use a std::function<std::string(std::string)>:
class myWebServer {
// not really a "callback"?
std::function<std::string(std::string)> callback;
public:
template <typename F>
void setCallback(F&& f) { callback = std::forward<F>(f); }
void runServer() {
// ...
std::string foo = callback("hello");
// do something with foo
}
};
And then, you can do:
myServer.setCallback([&](std::string query){
return myProgram.ProcessASP(query);
});
Related
I am going from C development to C++ on the STM32 platform and simply cant find a suitable solution for my problem.
Please have a look at the simplified example code attached to this post.
#include <iostream>
#include <functional>
#include <list>
using namespace std;
class Pipeline {
public:
std::list<std::function<void(Pipeline*)>> handlers;
//add handler to list --> works fine
void addHandler(std::function<void(Pipeline*)> handler) {
this->handlers.push_front(handler);
}
void ethernetCallback(void) {
//handle received data and notify all callback subscriptions --> still works fine
// this callback function is normally sitting in a child class of Pipeline
int len = handlers.size();
for (auto const &handler : this->handlers) {
handler(this);
}
}
void removeHandler(std::function<void(Pipeline*)> handler) {
// Here starts the problem. I can not use handlers.remove(handler) here to
// unregister the callback function. I understood why I can't do that,
// but I don't know another way of coding the given situation.
}
};
class Engine {
public:
void callback(Pipeline *p) {
// Gets called when new data arrives
cout<<"I've been called.";
}
void assignPipelineToEngine(Pipeline *p) {
p->addHandler(std::bind(&Engine::callback, this, std::placeholders::_1));
}
};
int main()
{
Engine *e = new Engine();
Pipeline *p = new Pipeline();
e->assignPipelineToEngine(p);
// the ethernet callback function would be called by LWIP if new udp data is available
// calling from here for demo purposes only
p->ethernetCallback();
return 0;
}
The idea is that when the class "Pipeline" receives new data over ethernet, it informs all registered callback functions by calling a method. The callback functions are stored in a std::list. Everything works fine till here, but the problem with this approach is that I can't remove the callback functions from the list, which is required for the project.
I know why I can't simply remove the callback function pointers from the list, but I don't know another approach at the moment.
Probably anybody could give me a hint where I could have a look for solving this problem. All resources I've researched don't really show my specific case.
Thank you all in advance for your support! :)
One option would be to have addHandler return some sort of identifier that can later be passed to removeHandler. For example:
class Pipeline {
public:
std::map<int, std::function<void(Pipeline*)>> handlers;
int nextId = 0;
//add handler to list --> works fine
void addHandler(std::function<void(Pipeline*)> handler) {
handlers[nextId++] = handler;
}
void ethernetCallback(void) {
for (auto const& entry : handlers) {
entry.second(this);
}
}
void removeHandler(int handlerToken) {
handlers.erase(handlerToken);
}
};
class Engine {
public:
void callback(Pipeline *p) {
// Gets called when new data arrives
cout<<"I've been called.";
}
void assignPipelineToEngine(Pipeline *p) {
handlerToken = p->addHandler(
std::bind(
&Engine::callback,
this,
std::placeholders::_1
)
);
}
void unregisterPipelineFromEngine(Pipeline *p) {
p->removeHandler(handlerToken);
}
private:
int handlerToken;
};
Perhaps you could attach an ID to each handler. Very crude variant would just use this address as an ID if you have at most one callback per instance.
#include <functional>
#include <iostream>
#include <list>
using namespace std;
class Pipeline {
public:
using ID_t = void *; // Or use integer-based one...
struct Handler {
std::function<void(Pipeline *)> callback;
ID_t id;
// Not necessary for emplace_front since C++20 due to agreggate ctor
// being considered.
Handler(std::function<void(Pipeline *)> callback, ID_t id)
: callback(std::move(callback)), id(id) {}
};
std::list<Handler> handlers;
// add handler to list --> works fine
void addHandler(std::function<void(Pipeline *)> handler, ID_t id) {
this->handlers.emplace_front(std::move(handler), id);
}
void ethernetCallback(void) {
// handle received data and notify all callback subscriptions --> still
// works fine
// this callback function is normally sitting in a child class of
// Pipeline
int len = handlers.size();
for (auto const &handler : this->handlers) {
handler.callback(this);
}
}
void removeHandler(ID_t id) {
handlers.remove_if([id = id](const Handler &h) { return h.id == id; });
}
};
class Engine {
public:
void callback(Pipeline *p) {
// Gets called when new data arrives
cout << "I've been called.";
}
void assignPipelineToEngine(Pipeline *p) {
//p->addHandler(std::bind(&Engine::callback, this, std::placeholders::_1), this);
//Or with a lambda
p->addHandler([this](Pipeline*p){this->callback(p);},this);
}
void removePipelineFromEngine(Pipeline *p) { p->removeHandler(this); }
};
int main() {
Engine *e = new Engine();
Pipeline *p = new Pipeline();
e->assignPipelineToEngine(p);
// the ethernet callback function would be called by LWIP if new udp data is
// available calling from here for demo purposes only
p->ethernetCallback();
return 0;
}
You might also consider std::map<ID_t,std::function<...>> instead of list, not sure how memory/performance constrained you are.
Obligatory: do not use new, use std::unique_ptr, or better use automatic storage whenever you can. Although in this case a pointer is appropriate for e as you need stable address due to this capture/bind/ID.
std::functions are not comparable as there isn't a good generic way how to define this comparison.
when I push fifoGroundEvtEntry data inside list_fifoGroundEvt from another thread using sender::GetInstance()->getDataCollector()->pushGroundEventFifo(entry); and when I debug puting one breakpoint inside pushGroundEventFifo function I can see the correct value of grdEvt.x and grdEvt.y inside list_fifoGroundEvt.
then when I call test method inside transmit methode and I pute breakpoint inside test. I see wrong values inside list_fifoGroundEvt-> grdEvt.y = 0x00F12751 for entry.y = 2 !
PS: transmit() is a thread and I start it using sender::GetInstance()->start() (I didn't put all functions I put only those who have a link with the problem )
the thread is starting after pushing entries inside list_fifoGroundEvt
#include "stdafx.h"
#include <iostream>
#include <list>
struct fifoEvtEntry {
virtual ~fifoEvtEntry() {}
int x;
};
struct fifoGroundEvtEntry : fifoEvtEntry
{
int y;
};
class collector {
public:
void pushGroundEventFifo(fifoEvtEntry& entry) {
if (fifoGroundEvtEntry* grdEvt = dynamic_cast<fifoGroundEvtEntry*>(&entry))
{
list_fifoGroundEvt.push_back(grdEvt);
}
}
void test() {
if (fifoGroundEvtEntry* grdEvt = dynamic_cast<fifoGroundEvtEntry*>(list_fifoGroundEvt.front()))
{
std::cout << grdEvt ->y << std::endl;
}
list_fifoGroundEvt.pop_front();
}
private:
std::list<fifoEvtEntry*> list_fifoGroundEvt;
};
class sender {
public:
sender(collector* data):_data(data) {};
~sender() {};
static void setInstance(collector* data) {
_instance = new sender(data);
}
static sender* GetInstance() {
return _instance;
}
void transmit() {
// this is a thread function
// ..
_data->test();
}
collector* getDataCollector(){
return _data;
}
static sender* _instance;
private:
collector* _data;
};
int main(){
return 0;
}
My intention is to design event callback model in network communication,the event callback includes "recv,send,accept,shutdown..."
class client
{
public:
template<typename F1>
void bind_recv(F1 && f)
{
// How to save the parameter of bind_recv function in _recv_callback member variable ?
_recv_callback = f;
}
// bind_send
// bind_accept
// bind_shutdown
// ...
protected:
void notify_recv()
{
_recv_callback();
}
// notify_send
// ...
// How to declare the type of these variable ?
F1 _recv_callback;
// F2 _send_callback;
// ...
};
int main()
{
client c;
c.bind_recv([](uint8_t * data, size_t len)
{
});
return(0);
}
I need to save callbacks and call them at the right time, but the parameters of each callback function are different, so how do I save them?
I guess maybe my thinking is wrong, so how can I change the design to meet this requirement?
Suppose I use a boost::asio::ssl::stream<boost::asio::ip::tcp::socket>:
asio::ssl::stream<asio::ip::tcp::socket> s;
asio::connect(s.lowest_layer(), endpointIterator);
s.handshake(asio::ssl::stream_base::client);
And so on. Then for whatever reason the connection fails, or even I disconnect. Is it possible to reuse it for future connections, or do I have to create a new object for each connection? I.e. wrap the stream, something like this:
class BetterSslConnection
{
public:
BetterSslConnection() : mSocket(new asio::ssl::stream<asio::ip::tcp::socket>())
{
}
~BetterSslConnection()
{
if (mSocket)
{
mSocket->shutdown();
mSocket->lowest_layer().close();
}
}
void connect(... endpointIterator)
{
if (mSocket)
{
mSocket->shutdown();
mSocket->lowest_layer().close();
}
mSocket.reset(new asio::ssl::stream<asio::ip::tcp::socket>());
asio::connect(mSocket->lowest_layer(), endpointIterator);
mSocket->handshake(asio::ssl::stream_base::client);
// and so on.
}
private:
unique_ptr<asio::ssl::stream<asio::ip::tcp::socket>> mSocket;
};
class BetterSslConnection
{
I'm having the following .proto for Protobuf (2.6.1 to be more detailed):
service InstallService {
rpc getWifiNetworks (WifiRequest) returns (WifiResponse);
}
I've generated java files and i'm having BlockingStub:
TestInstallService.BlockingInterface service = TestInstallService.newBlockingStub(channel);
and i can use if in blocking way (works good):
Wifi.WifiResponse response = service.getWifiNetworks(controller, request);
Now i'm creating C++ client which should work in blocking way too but i can't see any Blocking interfaces neither in proto nor in generated C++ code. How to generate BlockingStub in C++ in Protobuf? How can i pass closure if working in async way?
Generated C++ service file (.cpp):
class InstallService_Stub;
class InstallService : public ::google::protobuf::Service {
protected:
// This class should be treated as an abstract interface.
inline InstallService() {};
public:
virtual ~InstallService();
typedef InstallService_Stub Stub;
static const ::google::protobuf::ServiceDescriptor* descriptor();
virtual void getWifiNetworks(::google::protobuf::RpcController* controller,
const ::WifiRequest* request,
::WifiResponse* response,
::google::protobuf::Closure* done);
// implements Service ----------------------------------------------
const ::google::protobuf::ServiceDescriptor* GetDescriptor();
void CallMethod(const ::google::protobuf::MethodDescriptor* method,
::google::protobuf::RpcController* controller,
const ::google::protobuf::Message* request,
::google::protobuf::Message* response,
::google::protobuf::Closure* done);
const ::google::protobuf::Message& GetRequestPrototype(
const ::google::protobuf::MethodDescriptor* method) const;
const ::google::protobuf::Message& GetResponsePrototype(
const ::google::protobuf::MethodDescriptor* method) const;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(InstallService);
};
class InstallService_Stub : public InstallService {
public:
InstallService_Stub(::google::protobuf::RpcChannel* channel);
InstallService_Stub(::google::protobuf::RpcChannel* channel,
::google::protobuf::Service::ChannelOwnership ownership);
~InstallService_Stub();
inline ::google::protobuf::RpcChannel* channel() { return channel_; }
// implements InstallService ------------------------------------------
void getWifiNetworks(::google::protobuf::RpcController* controller,
const ::WifiRequest* request,
::WifiResponse* response,
::google::protobuf::Closure* done);
private:
::google::protobuf::RpcChannel* channel_;
bool owns_channel_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(InstallService_Stub);
};
It seems that no blocking code is generated by protoc so i had to use self-made blocking:
bool callbackFired = false;
void myCallback() {
// ...
callbackFired = true;
}
// run service method
service->myMethod(rpcController, request, response, NewCallback(&myCallback));
// block the thread until callback is invoked
while (!callbackFired);
...
C++ client usage example: https://github.com/4ntoine/protobuf-socket-rpc
The way you do this is to provide your own subclass of InstallService that overrides the methods you want to implement:
struct MyInstallService : public InstallService
{
void getWifiNetworks(::google::protobuf::RpcController* controller,
const ::WifiRequest* request,
::WifiResponse* response,
::google::protobuf::Closure* done) override
{
// do your work here
// fill up the response here
done->Run(); // this will trigger the response
}
};
client side:
Something like this
namespace detail {
template<class F>
struct simple_closure : google::protobuf::Closure {
simple_closure(F f)
: _f(std::move(f))
{}
void Run() override {
_f();
}
private:
F _f;
};
}
template<class F>
std::unique_ptr<detail::simple_closure<F>> make_closure(F&& f) {
return std::make_unique<detail::simple_closure<F>>(std::forward<F>(f));
}
std::unique_ptr<WifiResponse> syncGetWifiNetworks(InstallService_Stub & stub, const WifiRequest& req)
{
auto result = std::make_unique<WifiResponse>();
auto promise = std::promise<std::unique_ptr<WifiResponse>>;
auto future = promise.get_future();
auto controller = allocate_controller(); // you need to write this
auto closure = make_closure([&result, &promise]{
promise.set_value(std::move(result));
});
// assumes you already have an async comms queue - otherwise just
// dispatch this lambda to a std::async(std::launch::async, ...)
comms_queue.dispatch([&controller, &req, &stub, &response]{
stub.getWifiNetworks(controller, &req, response.get(), closure);
};
// HERE is where the current thread blocks until the promise is fulfilled
return future.get();
}