Im building a protocol in c++ for a TCP socket communication.
When I catched the id of an incoming packet, I would like to make a generic code that find which known packet (in my protocol) that I received.
class NetworkMessage {
public:
virtual ~NetworkMessage();
virtual void serialize(NetworkBuffer &buffer) = 0;
virtual void deserialize(NetworkBuffer &buffer) = 0;
virtual int32_t getProtocolId() const = 0;
};
Example of message
class HelloConnectMessage: public NetworkMessage {
public:
static constexpr int32_t PROTOCOL_ID = 1;
void serialize(NetworkBuffer &buffer) override;
void deserialize(NetworkBuffer &buffer) override;
int32_t getProtocolId() const override;
int32_t getHelloVar() const;
private:
int32_t _hellovar;
};
I wanted to map the protocolId to the associated child class of NetworkMessage, but we can't hold class types like in Java.
I was about to make something like the following code, that can't compile since it's just an idea of what I really want.
std::unordered_map<int, class<? extends NetworkMessage>> messages;
My alternative for now, below:
static NetworkMessage build_packet(NetworkBuffer &buffer) {
int32_t id = buffer.readInt();
switch(id) {
case HelloConnectMessage::PROTOCOL_ID:
HelloConnectMessage msg;
msg.deserialize(buffer);
return msg;
default:
throw std::runtime_error("invalid packet");
}
}
But this is not enough generic.. I will waste time rewriting always the same code for each message.. Would appreciate any help, im not familiar to c++
Populate your map with factory functions. Something like this:
template <typename Msg>
std::unique_ptr<NetworkMessage> MakeMessage() { return std::make_unique<Msg>(); }
using Factory_t = std::unique_ptr<NetworkMessage>(*)();
std::unordered_map<int, Factory_t> messages = {
{HelloConnectMessage::PROTOCOL_ID, MakeMessage<HelloConnectMessage>},
...
};
std::unique_ptr<NetworkMessage> build_packet(NetworkBuffer &buffer) {
...
auto msg = messages[id]();
msg->deserialize(buffer);
return msg;
}
Related
I am writing an interface for several I/O classes.
There is a function that looks for information in different kinds of files (sometimes html, sdb, txt, ...):
bool Search(std::string file, std::string field)
However, one of these requires an additional parameter to complement the SQL query. In this case the sdb needs to specify in what table the field is located.
I am trying something like the following (it does not compile, I am aware):
class fileIO{
public:
virtual ~FileIO(){};
virtual bool Search(std::string file, std::string field,
std::string additional = 0 /* for sdb only */) = 0;
}
class readHTML : fileIO{
public:
bool Search(std::string file, std::string field); //does NOT override virtual method
Is there anything that can give me the behavior I am looking for?
Is such strategy according to C++ standards?
What else could I add to replace such enforcement on the interface?
I am sorry if the title is misleading, I am looking for an alternative with that behavior. I could not find it so far.
You don't need it, I'd say.
At the caller site, there is only two possibilities: you know your specific fileIO instance is a sdbIO or you don't. If you do, you can call an overloaded version of Search defined in sdbIO which takes this additional info. If you don't, you don't and sdbIO::Search should be defined in terms of its overloaded version.
struct fileIO
{
virtual bool Search(std::string file, std::string field) = 0;
}
struct sdbIO : fileIO
{
bool Search(std::string file, std::string field, std::string additional);
bool Search(std::string file, std::string field) override
{
Search(file, field, "");
}
};
At the caller site:
void f(fileIO& io)
{
// I know this is a sdb:
dynamic_cast<sdbIO&>(io).Search("/file", "text", "WHERE answer=42");
// I don't
io.Search("/file", "text");
}
notes: do you really need a copy of those strings?
You can hide the virtual function in the non-public interface and make the public interface (with the default argument) non-virtual.
struct Interface
{
...
// public interface calls the abstract members.
bool Search(string const&a, string const&b, string const&c = "")
{
if(c.empty() && need_third_string())
throw runtime_error("search requires an additional string argument");
return search(a,b,c);
}
protected:
virtual bool need_third_string() const = 0;
virtual bool search(string const&, string const&, string const&) const=0;
};
with obvious derivations:
struct A : Interface
{
protected:
bool need_third_string() const override
{ return false; }
bool search(string const&a, string const&b, string const&) const override
{ /* search ignoring third argument */ }
};
struct B : Interface
{
protected:
bool need_third_string() const override
{ return true; }
bool search(string const&a, string const&b, string const&c) const override
{ /* search ignoring using all 3 arguments */ }
};
I don't see any problem with above two way to handle things. Still, I have just one more.
#include<bits/stdc++.h>
#include <stdexcept>
using namespace std;
typedef struct
{
std::string arg1;
std::string arg2;
std::string arg3;
} Param;
class FileIO{
public:
virtual ~FileIO(){};
virtual void Search(Param param) = 0;
};
class ReadHTML : public FileIO{
public:
void Search(Param param)
{
if(param.arg3.length() > 0) // Some logic to handle things here.
search3(param.arg1, param.arg2, param.arg3);
else
throw std::runtime_error("Bad call with param");
}
private:
void search3(std::string arg1, std::string arg2, std::string arg3)
{
std::cout << " I am called with Html::Search3" << std::endl;
}
};
class ReadTxt : public FileIO{
public:
void Search(Param param)
{
if(param.arg1.length() && param.arg2.length()) // Some logic to handle things here.
search2(param.arg1, param.arg2);
else
throw std::runtime_error("Bad call with param");
}
private:
void search2(std::string arg1, std::string arg2)
{
std::cout << " I am called with Txt::Search2" << std::endl;
}
};
// Driver program to test above function
int main()
{
FileIO *io = new ReadHTML();
Param paramHtml = {"a", "b", "c"};
io->Search(paramHtml); // Put some try .. catch
Param paramTxt = {"a", "b"};
io = new ReadTxt(); // Put some try...catch
io->Search(paramTxt);
return 0;
}
I have a Message structure that I am using with a message bus, and I'd like to send data with messages. The problem is that the data will vary in type; maybe for one message I'll just want to send one int, but for another I'll want to send several ints, a string, maybe even a pointer to an object for example. I could do something like this:
struct Message {
std::map<int, int> intPayload;
std::map<int, std::string> strPayload;
short id;
};
But not only is this ugly and unclean, and probably wastes space, but that doesn't account for if I want to pass a relatively exotic data type like a pointer to an instance of a class for example. What should I be using for this?
A simple example using inheritance and polymorphism:
struct MessageBase
{
// The function to send *this* message to the receiver
virtual void send(ReceiverClass*) = 0;
};
struct MessageInt : MessageBase
{
int payload;
void send(ReceiverClass* receiver)
{
// Code to send this message type to the receiver...
}
};
struct MessageString : MessageBase
{
std::string payload;
void send(ReceiverClass* receiver)
{
// Code to send this message type to the receiver...
}
};
// ...
// Vector to store the messages
std::vector<MessageBase*> messages;
// Add a couple of messages
messages.push_back(new MessageInt{123});
messages.push_back(new MessageString{"Foobar"});
// Send the message to some receiver
for (auto const* message : messages)
message->send(some_reciver_object);
Any good book should be able to give you more information.
You can base your solution on the visitor pattern.
As a minimal, working example:
struct Listener;
struct Message {
virtual void accept(Listener &) = 0;
};
struct SimpleMessage: Message {
void accept(Listener &) override;
int i;
};
struct ComplexMessage: Message {
void accept(Listener &) override;
int i;
char c;
double d;
};
struct Listener {
void visit(SimpleMessage &) {}
void visit(ComplexMessage &) {}
void listen(Message &m) { m.accept(*this); }
};
void SimpleMessage::accept(Listener &l) { l.visit(*this); }
void ComplexMessage::accept(Listener &l) { l.visit(*this); }
struct Bus {
Bus(Listener *l): l{l} {}
void publish(Message &m) { l->listen(m); }
private:
Listener *l;
};
int main() {
Listener l;
Bus b{&l};
SimpleMessage sm;
ComplexMessage cm;
b.publish(sm);
b.publish(cm);
}
Set aside the fact that the implementation for the Bus is trivial, note that visit member functions in Listener can be virtual.
This way, all your listener can be derived from that class and override the desired methods.
The Bus will accept a set of Listeners, no matter what's the actual derived type, and a generic Message. On the other side, message will promote itself to the right derived type and pass a reference to the given listener.
The technique behind the visitor pattern is also known as double dispatching, if you want to explore it further.
There are many ways to do this. Here's an example with C++17's std::variant:
std::vector<std::variant<int, std::string>> vec1;
vec1.emplace_back(1);
vec1.emplace_back("hello"s);
doSomethingWithInt( std::get<int>(vec1[0]) );
doSomethingWithString( std::get<std::string>(vec1[1]) );
vec1 is a list of element that are either int or std::string.
You can also use a static visitor:
std::vector<std::variant<int, std::string>> vec2;
// ...
for(auto&& variant : vec1) {
variant.visit([](auto value){
using t = decltype(value);
if constexpr (std::is_same_v<t, int>) {
std::cout << "value is a int!" << std::endl;
} else if constexpr (std::is_same_v<t, std::string>) {
std::cout << "value is a string!" << std::endl;
}
});
}
(For simplicity's sake, serializer will be called write and deserializer will be called read)
I'm writing a C++ game serializer from scratch with no library allowed.
The main concern i have is to keep the read and write in sync (the read values must be the same as the written ones). So the Packer handles both tasks and is specified with an enum.
What i have
enum PackerType {
WRITE,
READ
}
template <PackerType PType>
class Packer {
char *buffer; // Packer will write here
uint32_t index;
template <typename T>
void Pack(T & value); // Calls appropriate functions depending on PType
}
What i want to be able to do
class ElementToSerialize : ISeriablizable {
virtual void WriteAndRead(Packer & p) {
p.Pack(32);
p.Pack("Hello World");
}
}
Packer<WRITE> wpacker;
Packer<READ> rpacker;
rpacker.buffer = wpacker.buffer;
WriteAndRead(wpacker); // Will write everything in wpacker.buffer
WriteAndRead(rpacker); // Will read wpacker.buffer
So i know this is not possible in C++, but what i'm looking for is an elegant way of dealing with this issue. I'm already aware of type-erasure, but i'm not a fan of the solution.
How about starting with something like this:
class IPacker
{
public:
virtual void Pack(int& value) = 0;
virtual void Pack(float& value) = 0;
}
class ISeriablizable
{
public:
virtual void Serialize(IPacker & p) = 0;
}
class WritePacker : public IPacker
{
char* buffer;
int index = 0;
public:
WritePacker(char* buffer) : buffer(buffer) {}
void Pack(int& value) override { /* write to buffer */ }
void Pack(float& value) override { /* write to buffer */ }
}
class ReadPacker : public IPacker
{
char* buffer;
int index = 0;
public:
ReadPacker(char* buffer) : buffer(buffer) {}
void Pack(int& value) override { /* read from buffer */ }
void Pack(float& value) override { /* read from buffer */ }
}
class ElementToSerialize : public ISeriablizable
{
int x = 32;
std::string y = "Hello world";
void Serialize(IPacker & p) override
{
p.Pack(x);
p.Pack(y);
}
}
If it is not a requirement, you could avoid using templates altogether, and just store whether the Packer is read or write so you know it at runtime (example):
class Packer {
public:
enum Type {
READ,
WRITE
};
char *buffer; // Packer will write here
uint32_t index;
Type type;
Packer(Type t) : type(t) {}
void Pack(int& value);
void Pack(float& value);
void Pack(std::string& value);
// etc...
};
I have a problem. I've written a GPS module that can detect the type of the message on the fly and configure them if needed. I've done it by composition of several classes. To make code a little more independent from the platform (stm32) I created a IStreamDevice interface that has baic i/o operations. It works. Everything appers to be great, but the classs are apparently coupled. That't why I have several question:
How can I avoid the passing IStreamDevice to all devices?
How can I make the whole design more platform-independent (and os-independent)? We have plans to move to another OS in the nearest future. It is POSIX compliant. I think I will be able to implement my IStreamDevice interface there (the buses I can aend up using are UART and SPI. In my current version I use only UART). Am I wrong?
class IStreamDevice
{
public:
virtual ~IStreamDevice() {}
virtual uint32_t read(uint8_t* data, uint32_t size) = 0;
virtual uint32_t write(const uint8_t* data, uint32_t size) = 0;
virtual uint32_t bytesToRead() const = 0;
virtual uint32_t bytesToWrite() const = 0;
};
class GPSModule {
public:
GPSModule(periph::IStreamDevice *source);
~GPSModule();
void turnDevice1Messages();
void turnDevice2Messages();
void configureDevice1(...);
void configureDevice2(...);
void Scan();
private:
Device1Configurator *_device1_configurator;
Device2Configurator *_device2_configurator;
StreamDeviceScanner*_scanner;
periph::IStreamDevice *_source;
};
GPSModule::GPSModule(periph::IStreamDevice *source): _source(source)
{
_scanner= new StreamDeviceScanner(_source);
_device1_configurator = new Device1Configurator(_source);
_device2_configurator = new Device2Configurator(_source);
}
GPSModule::~GPSModule()
{
delete _scanner;
}
void GPSModule::Scan()
{
_scanner->Scan();
}
void GPSModule::turnDevice1Messages() {
_device1_configurator->turnMessages();
}
class StreamDeviceScanner{
public:
StreamDeviceScanner(periph::IStreamDevice *source);
~StreamDeviceScanner();
void Scan();
private:
typedef enum
{
WAITING_SYNC,
WAITING_DEVICE1_MSG,
WAITING_DEVICE2_MSG
} states_t;
periph::IStreamDevice *_source;
ProtocolScanner *_protocol_scanner;
states_t _state;
private:
states_t _catchSync();
uint32_t _read(uint8_t* data, uint32_t length) { return _source->read(data,length); }
uint32_t _bytesToRead() const { return _source->bytesToRead(); }
};
StreamDeviceScanner::StreamDeviceScanner(periph::IStreamDevice *source):
_source(source),
_state(WAITING_SYNC)
{
_protocol_scanner = new ProtocolScanner(source);
}
StreamDeviceScanner::~StreamDeviceScanner()
{
delete _protocol_scanner;
}
void StreamDeviceScanner::Scan()
{
while (_source->bytesToRead()) {
switch (_state)
{
case WAITING_SYNC:
_state = _catchSync();
break;
case WAITING_DEVICE1_MSG:
_protocol_scanner->Device1Scan()
_state = WAITING_SYNC;
break;
case WAITING_DEVICE2_MSG:
_protocol_scanner->Device2Scan()
_state = WAITING_SYNC;
break;
}
}
}
class ProtocolScanner {
private:
Device1Scanner *_Device1Scanner;
Device2Scanner *_Device2Scanner;
public:
ProtocolScanner(periph::IStreamDevice *source)
{
_Device1Scanner = new Device1Scanner(source);
_Device2Scanner = new Device2Scanner(source);
}
~ProtocolScanner()
{
delete _Device1Scanner;
delete _Device1Scanner;
}
bool Device1Scan() const { return _Device1Scanner->Scan(); }
bool Device2Scan() const { return _Device2Scanner->Scan(); }
};
class Device1Scanner {
public:
Device1Scanner(periph::IStreamDevice *source);
~Device1Scanner();
bool Scan();
private:
enum { BUFFER_LENGTH = 8192 };
typedef enum {
Waiting_Header,
Waiting_Payload,
Waiting_Checksum
} state_t;
uint8_t _buffer[BUFFER_LENGTH];
periph::IStreamDevice *_source;
state_t _state;
Device1Parser *_destination;
Device1Scanner::NovatelMessage _message;
private:
uint32_t _read(uint8_t* data, uint32_t size) { return _source->read(data,size); }
const uint32_t _bytesToRead() const { return _source->bytesToRead(); }
bool _receiveHeader();
bool _receivePayload();
bool _receiveChecksum();
bool _validChecksum() const;
};
Device2Scanner looks exactly the same. I'd like to hear everything that anyone has to say about the design.
I don't see any inherent problem with your design. Your IStreamWriter interface seems like a proper abstraction of the underlying bus, without being dependent on specific bus details. That complies with the Dependency Inversion principle and with design-by-contract approach. I also don't see tight coupling in your classes. You're accessing the bus via its handler, according to the interface specification, without dependency on the implementation of the actual bus handling class.
There is nothing platform dependent in the shown code. If the bus handling differs per platform, there is not much you can do except providing a different implementations for IStreamWriter according to platform.
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;
}