How to generate BlockingStub in C++ in Protobuf? - c++

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();
}

Related

How to save an asynchronous callback for later using node-addon-api / napi

I'm using AsyncWorkerto run an asynchronous task. The problem is that i have numerous tasks to be run, one after another, and the order is important. To keep order i'm using a queuing technique to make sure the AsyncWorkerobjects are created in the wright order, only once each task finishes. I'm storing the Callback in a vector<Function>, and pass that to the AsyncWorker, but i get the following error:
# Fatal error in v8::HandleScope::CreateHandle()
# Cannot create a handle without a HandleScope
Is there some other way of going about this? I also tried using Napi::Persistent, but i can't pass a Napi::FunctionReference variable to AsyncWorker
The caller functions:
Napi::Value BlockChainWrapper::genesis(const Napi::CallbackInfo& info) {
std::lock_guard<std::mutex> guard_ready_queue(ready_queue_mutex);
this->ready_queue_callback.push_back(info[1].As<Napi::Function>());
this->ready_queue_data.push_back(info[0].As<Napi::Object>());
this->ready_queue_func.push_back(BlockChainWrapperTypes::_genesis_ready);
this->ready_queue_env.push_back(info.Env());
return info.Env().Undefined();
}
void BlockChainWrapper::genesis_ready() {
AsyncBlockChainFunctions* asyncWorker = new AsyncBlockChainFunctions(this->ready_queue_callback.front(), 0, blockchain_obj, this->ready_queue_data.front());
asyncWorker->Queue();
}
AsyncWorker constructor:
AsyncBlockChainFunctions::AsyncBlockChainFunctions(Napi::Function& callback, int mode, std::shared_ptr<BlockChain> _blockchain, Napi::Object& resource) : AsyncWorker(callback), mode(mode) {};
EDIT 1
I implemented the PromiseWorker, but still ran into these errors:
BlockChainWrapper inherits ObjectWrap.
Napi::Object BlockChainWrapper::Init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
Napi::Function func = DefineClass(env, "BlockChainWrapper", {
InstanceMethod("genesis", &BlockChainWrapper::genesis)
});
constructor = Napi::Persistent(func);
constructor.SuppressDestruct();
exports.Set("BlockChainWrapper", func);
return exports;
}
# Fatal error in HandleScope::HandleScope
# Entering the V8 API without proper locking in place
Modified AsyncWorker constructor, class and resolve function:
class AsyncBlockChainFunctions : public PromiseWorker
AsyncBlockChainFunctions(Napi::Promise::Deferred const &d, std::shared_ptr<BlockChain> _blockchain, int mode, Napi::Object& resource) : PromiseWorker(d), mode(mode) {}
void Resolve(Napi::Promise::Deferred const &deferred) {
deferred.Resolve(Napi::String::New(deferred.Env(), this->block_as_json_string));
};
Caller function:
Napi::Value BlockChainWrapper::genesis(const Napi::CallbackInfo& info) {
std::lock_guard<std::mutex> guard_ready_queue(ready_queue_mutex);
this->ready_queue_data.push_back(info[0].As<Napi::Object>());
this->ready_queue_func.push_back(BlockChainWrapperTypes::_genesis_ready);
this->ready_queue_env.push_back(info.Env());
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
std::cout << "genesis" << std::endl;
return deferred.Promise();
}
Genesis ready called from another queue management thread
void BlockChainWrapper::genesis_ready() {
Napi::Env env = ready_queue_env.front();
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
Napi::Object input_obj = this->ready_queue_data.front().As<Napi::Object>();
auto *x = new AsyncBlockChainFunctions(std::ref(deferred), this->blockchain_obj, 0, input_obj);
x->Queue();
}
I am not sure if I understood you correctly or able to help your original problem, but you should not depend on the order of the execution of AsyncWorker. However the error you mentioned just sounds like you constructed AsyncWorker wrong, i.e. the CallbackInfo might be faulty, i.e. the napi environment its based on.
Promises
However I strongly recommend to use AsyncWorker to just handle the lifetime as I was designed to do, and to switch to the promise pattern.
Based on https://github.com/nodejs/node-addon-api/issues/231#issuecomment-528986145
I recommend you use this as a base class:
#include <napi.h>
class PromiseWorker : public Napi::AsyncWorker {
public:
PromiseWorker(Napi::Promise::Deferred const &d) : AsyncWorker(get_fake_callback(d.Env()).Value()), deferred(d) {}
virtual void Resolve(Napi::Promise::Deferred const &deferred) = 0;
void OnOK() override {
Resolve(deferred);
}
void OnError(Napi::Error const &error) override {
deferred.Reject(error.Value());
}
private:
static Napi::Value noop(Napi::CallbackInfo const &info) {
return info.Env().Undefined();
}
Napi::Reference<Napi::Function> const &get_fake_callback(Napi::Env const &env) {
static Napi::Reference<Napi::Function> fake_callback
= Napi::Reference<Napi::Function>::New(Napi::Function::New(env, noop), 1);
fake_callback.SuppressDestruct();
return fake_callback;
}
Napi::Promise::Deferred deferred;
};
You just then would have to subclass it, override Resolve and Execute, and save the stuff you need in your member privates, and you are done.
Update: I made a full working example on how to use this promises: https://github.com/Superlokkus/spielwiese/tree/napi_promise_example
Note the Promise Method:
#include <napi.h>
#include "promise_worker.hpp"
struct PromiseMethodWorker : PromiseWorker {
PromiseMethodWorker(Napi::Promise::Deferred const &d, int input)
: PromiseWorker(d), input_{std::move(input)} {}
void Resolve(Napi::Promise::Deferred const &deferred) override {
deferred.Resolve(create_js(deferred.Env()));
}
void Execute() override {
output_ = input_ * 5;
}
private:
int input_;
int output_;
Napi::Number create_js(Napi::Env env) const {
Napi::Number js_value = Napi::Number::New(env, output_);
return js_value;
}
};
Napi::Promise PromiseMethod(const Napi::CallbackInfo &info) {
int input = info[0].ToNumber();
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
auto *wk = new PromiseMethodWorker(deferred, input);
wk->Queue();
return deferred.Promise();
}
Usage and Solution
So you could just use the promise returned in JS:
addon.PromiseMethod(42).then(value => add.PromiseMethod(value).then(...))
So you could easily chain these promises together, of wait for all via Promise::all. But so you avoid the so called callback hell. But again, your diffuse order requirement sounds like a XY problem to me.
So instead of many promises/callbacks make it to one, since AsyncWorker seem to make no guarantee about call order. Blocking in one promise, might stall everything. Order in your native code.
The following will work even for an array
async.eachSeries(/*array*/, function(item, nextItem) {
async.waterfall([
function(callback) {
//your logic
callback(null, data);
},
function(data, callback) {
//your logic
callback(null, data2);
},
function(data2, callback) {
//your logic
callback(null, 3); //The number is just for the argument purpose. Can be anything
}
//You can continue adding as many steps as you want
],nextItem)
});

Gmock object not being passed correctly into async function

I am trying to mock parts of the AWS SDK in C++ for testing. Specifically, I'm mocking a KMSClient and DecryptOutcome object. The MockKMSClient is set to pass in a MockDecryptOutcome into the DecryptResponseReceivedHandler.
Here are my mocked classes:
class MockDecryptOutcome : public Aws::KMS::Model::DecryptOutcome {
public:
Aws::Utils::Base64::Base64 _Base64;
MockDecryptOutcome(string request) {
if (request == SUCCESS) {
EXPECT_CALL(*this, IsSuccess()).WillRepeatedly(Return(true));
Aws::KMS::Model::DecryptResult result;
result.SetPlaintext(_Base64.Decode(SUCCESS));
EXPECT_CALL(*this, GetResult()).WillRepeatedly(Return(result));
} else {
EXPECT_CALL(*this, IsSuccess()).WillRepeatedly(Return(false));
if (request == GRANT_TOKEN_NEEDED) {
EXPECT_CALL(*this, GetError()).WillRepeatedly(Return(Aws::KMS::KMSErrors::ACCESS_DENIED));
} else if (request == ENDPOINT_ERROR) {
EXPECT_CALL(*this, GetError()).WillRepeatedly(Return(Aws::KMS::KMSErrors::NETWORK_CONNECTION));
}
}
}
virtual ~MockDecryptOutcome() {};
MOCK_METHOD0(IsSuccess, bool());
MOCK_METHOD0(GetResult, Aws::KMS::Model::DecryptResult());
MOCK_CONST_METHOD0(GetError, Aws::KMS::KMSErrors());
};
class MockKMSClient : public Aws::KMS::KMSClient {
public:
MockKMSClient() {
EXPECT_CALL(*this, DecryptAsync_impl(_, _)).WillRepeatedly(Invoke(this, &MockKMSClient::do_DecryptAsync));
}
virtual ~MockKMSClient() {};
Aws::Utils::Base64::Base64 _Base64;
// Have to invoke Mocked method manually to discard optional parameter
void DecryptAsync(
const Aws::KMS::Model::DecryptRequest& request,
const Aws::KMS::DecryptResponseReceivedHandler& handler,
const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context = nullptr
) const
{
DecryptAsync_impl(request, handler);
}
MOCK_CONST_METHOD2(
DecryptAsync_impl,
void(
const Aws::KMS::Model::DecryptRequest& request,
const Aws::KMS::DecryptResponseReceivedHandler& handler
)
);
void do_DecryptAsync(
const Aws::KMS::Model::DecryptRequest& request,
const Aws::KMS::DecryptResponseReceivedHandler& handler
)
{
const MockDecryptOutcome& outcome(_Base64.Encode(request.GetCiphertextBlob()));
cout << &outcome << endl;
handler(this, request, outcome, nullptr);
}
};
The handler is defined in the AWS SDK here: http://sdk.amazonaws.com/cpp/api/LATEST/namespace_aws_1_1_k_m_s.html#a6bb4999b2fbc6cd499913779e42421b3
Here is the callback function:
void KmsCallback::on_decrypt_callback(
const Aws::KMS::KMSClient* client,
const Aws::KMS::Model::DecryptRequest&,
const Aws::KMS::Model::DecryptOutcome& outcome,
const std::shared_ptr<const Aws::Client::AsyncCallerContext>&
)
{
cout << &outcome << endl;
}
Finally, here is where the async function is called:
kms_client->DecryptAsync(
decrypt_request,
std::bind(
&KmsCallback::on_decrypt_callback,
this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3,
std::placeholders::_4
)
);
Running this in a test prints out two different memory addresses for the DecryptOutcome variables:
0x7f03b6064dc0
0x7f03b6064dc8
I've tried using the "new" operator, removing "const", and many other combinations to get this to work without success. Any suggestions would be appreciated.

GoogleMock: How to SetArgReferee according to another input parameter?

I would like to use GoogleMock to mock a service as below:
class Request
{
int req_id;
int request;
};
class Response
{
int req_id;
int response;
};
int request(Response& res, const Request& req)
{
res.req_id = req.req_id;
res.response = 2 * req.request;
return 1;
}
EXPECT_CALL(mock_service, request(_,_)).WillOnce(DoAll(SetArgReferee<0>(/* what here? */), Return(1)));
How can I SetArgReferee for the Response& res according to the passed in const Request& req, i.e. res.req_id = req.req_id; and res.response = 2 * req.request; ?
Your code does not look like something that could be mocked - but I guess this is just illustration of the problem.
So assuming that you have mocked function reqest within mocked class MockService - then use Invoke Action with your request function as parameter:
EXPECT_CALL(mock_service, request(_,_)).WillOnce(Invoke(&request));
If you like to just call base class function - then do something like this:
class mock_service_class : public service_class
{
public:
MOCK_METHOD2(request, int(Response& res, const Request& req));
int baseRequest(Response& res, const Request& req)
{
return service_class::request(res, req);
}
};
And do your EXPECT_CALL in this way:
EXPECT_CALL(mock_service, request(_,_))
.WillOnce(Invoke(&mock_servide,
&mock_service_class::baseRequest));

C++ setting up a callback function

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);
});

How to find types of an object's ancestors?

I'm creating a mechanism by which Receivers can tell a Sender that each Receiver is interested in Messages of a certain type. With my sample implementation below there exists a limitation where a Receiver that wants to receive all Messages of a certain base type only receives Messages that are explicitly of that type and will not receive Messages of a derived type (see main() for example).
A potential solution would be to register all of a Message's ancestors' types when registering that particular Message and use that information to route Messages properly.
What other solutions are there?
Note: In reality, I'd store the RTTI so a RTTI lookup wouldn't be required every time. There are also other things that I have skimped/skipped here, as well. I'm going for brevity w/ this example...
Example code below:
class Sender
{
typdef std::vector<Receiver const & > Receivers;
public:
void register(Receiver const & i_recv, typeinfo const & i_type)
{
m_routingMap[i_type].push_back(i_recv);
}
void send(BaseMsg const & i_msg)
{
Receivers receivers = m_routingMap.find(typeid(i_msg));
for (Receivers::iterator receiver = receivers.begin(); receiver != receivers.end(); ++receiver) {
receiver.receive(i_msg);
}
}
private:
std::map<typeinfo const &, Receivers> m_routingMap;
};
class Receiver
{
public:
void receiver(BaseMsg const & i_msg)
{
// React to expected messages here
}
};
class BaseMsg {};
class ChildMsg : public BaseMsg {};
int main()
{
Sender sndr;
Receiver recv1;
sndr.register(recv1, typeid(BaseMsg));
Receiver recv2;
sndr.register(recv2, typeid(ChildMsg));
BaseMsg baseMsg;
sndr.send(baseMsg); // I want only recv1 to receive this message
ChildMsg childMsg;
sndr.send(childMsg); // I want both Receivers to receive this message, but only recv2 will receive it
}
Update: here's a solution I'm getting up to:
// Note: implementation is based in gleaning from
// http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14
class BaseMsg
{
public:
typedef std::vector<TypeInfo const & > Types;
static TypeInfo const * getType()
{
TypeInfo static * ms_type = new TypeInfo(typeid(BaseMsg));
return ms_type;
}
static Types const * getAncestorTypes()
{
// The base class does not have an ancestor
// Static varible, will only be constructed once!
Types * ms_ancestorTypes = new Types();
return ms_ancestorTypes;
}
};
class ChildMsg
{
public:
static TypeInfo const * getType()
{
TypeInfo static * ms_type = new TypeInfo(typeid(ChildMsg));
return ms_type;
}
static Types const * getAncestorTypes()
{
// Add the parent type and all the parent's ancestor's types
Types const * ancestorTypes = BaseMsg::getAncestorTypes();
// Static variable, so it will only be constructed once!
Types * static ms_ancestorTypes = new Types(ancestorTypes->begin(), ancestorTypes->end());
// This push_back() will occur every time, but it's only one operation,
// so hopefully it's not a big deal!
ms_ancestorTypes->push_back(BaseMsg::getType());
return ms_ancestorTypes;
}
};
And the Sender:
# Python pseudo code
class Sender:
def send(self, i_msg):
types_to_check_for = [i_msg.getType()].extend(i_msg.getAncestorTypes())
for type_ in types_to_check_for:
for receiver in _routing_list[type_]:
receiver.receive(i_msg)
Perhaps consider using an observer pattern (http://en.wikipedia.org/wiki/Observer_pattern).
This way you sender has no knowledge of your receiver, and your observer can control the distribution of msgs.
Sender -> informs observer there is a message.
observer -> informs each interested party there is a new msg.
interested part -> does fun stuff.
This will require some sort of msg identification system. Perhaps all msgs could inherit from a msg type that has a type member and an id member. That way you can register for msgs using them.
Update:
A quick msg structure:
class Message
{
public:
size_t m_Type;
size_t m_Id;
protected:
Message(size_t type, size_t id) : m_Type(type), m_Id(id) {}
};
class Type1 : public Message
{
public:
static const size_t type = 1;
Type1(size_t id) : Message(type, id) {}
};
The subscriber means the person that wants to listen to the msg). The subscriber should have an interface to accept msgs based on both of these functions.
Class subscriber
{
virtual void receiveType(size_t type, char * data) = 0;
virtual void receiveMsg(size_t type, size_t id, char * data) = 0;
};
The observer should have a method to register for the msgs:
Class Observer
{
void registerForType(type, subscriber);
void registerForMsg(type, id, subscriber);
};
Another Update:
This is really just a rough proof-of-concept. One can do what you want without knowing the exact ancestor chain. Forgive the switching of the trigger and registrationEntry functions (I did it wrong at first, and that was the simplest correction, again proof-of-concept). Another downside of this sketch is that at least a msg has to be constructed to register. If you are looking for a real long term solution, I suggest you find a library, or framework, that has reflection in it already (QT for example has the metaobjects), these could be used to see superclasses. Or, you could use the signals/slots already there.
Output from the code below:
Starting C:\Users\David\Downloads\asdf-build-desktop-Qt_4_8_0_for_Desktop_-MinGW_Qt_SDK__Release\release\asdf.exe...
Base Register
Registration: BaseMsg
Child Register
Registration: Message
Base call
Trigger: BaseMsg
virtual void Subscriber1::newMessage(const BaseMsg&)
Der. call
Trigger: BaseMsg
virtual void Subscriber1::newMessage(const BaseMsg&)
Trigger: Message
virtual void Subscriber2::newMessage(const BaseMsg&)
C:\Users\David\Downloads\asdf-build-desktop-Qt_4_8_0_for_Desktop_-MinGW_Qt_SDK__Release\release\asdf.exe exited with code 0
#include <string>
#include <vector>
#include <map>
#include <stdio.h>
using namespace std;
class BaseMsg
{
public:
BaseMsg()
{
theRealInit();
}
//incase you don't want to go all the way down the rabbit hole.
//At the bottom they are the same
virtual vector<string> const & registrationEntries() const {return m_SubClassChain;}
virtual vector<string> const & triggerEntries() const {return m_SubClassChain;}
protected:
virtual void init() { printf("Should NOT CALL THIS HERE!");}
vector<string> m_SubClassChain;
private:
void theRealInit()
{
m_SubClassChain.push_back("BaseMsg");
}
};
class Message : public BaseMsg
{
public:
Message() : BaseMsg()
{
init(); //MUST BE CALLED from child
}
virtual vector<string> const & triggerEntries() const {return m_TriggerEntries;}
protected:
virtual void init()
{
//BaseMsg::init();
m_SubClassChain.push_back("Message");
m_TriggerEntries.push_back("Message");
}
private:
vector<string> m_TriggerEntries;
};
class Subscriber
{
public:
virtual void newMessage(BaseMsg const & i_msg)
{
printf("%s\n", __PRETTY_FUNCTION__);
}
};
class Subscriber2 : public Subscriber
{
public:
virtual void newMessage(BaseMsg const & i_msg)
{
printf("%s\n", __PRETTY_FUNCTION__);
}
};
class Subscriber1 : public Subscriber
{
public:
virtual void newMessage(BaseMsg const & i_msg)
{
printf("%s\n", __PRETTY_FUNCTION__);
}
};
class Sender
{
//typdef vector<Receiver const & > Receivers;
public:
void registerForMsg(Subscriber * someoneThatCares, BaseMsg const & msg)
{
vector<string> const & triggers = msg.triggerEntries();
vector<string>::const_iterator it = triggers.begin();
for(; it != triggers.end(); it++)
{
printf("Registration: %s\n", it->c_str());
m_routingMap.insert(pair<string, Subscriber *>(*it, someoneThatCares));
}
}
void send(BaseMsg const & msg)
{
vector<string> const & triggers = msg.registrationEntries();
vector<string>::const_iterator it = triggers.begin();
for(; it != triggers.end(); it++)
{
printf("Trigger: %s\n", it->c_str());
pair<multimap<string, Subscriber *>::iterator, multimap<string, Subscriber *>::iterator> ret;
//borrowed from: http://www.cplusplus.com/reference/stl/multimap/equal_range/
ret = m_routingMap.equal_range(*it);
multimap<string, Subscriber *>::iterator it1;
for (it1 = ret.first; it1 != ret.second; ++it1)
{
it1->second->newMessage(msg);
}
}
}
private:
multimap<string, Subscriber *> m_routingMap;
};
int main(int argc, char *argv[])
{
Sender sndr;
BaseMsg baseMsg;
Message message;
printf("Base Register\n");
Subscriber1 recv1;
sndr.registerForMsg(&recv1, baseMsg);
printf("Child Register\n");
Subscriber2 recv2;
sndr.registerForMsg(&recv2, message);
printf("Base call\n");
sndr.send(baseMsg); // I want only recv1 to receive this message
printf("Der. call\n");
sndr.send(message); // I want both Receivers to receive this message, but only recv2 will receive it
return 0;
}