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.
Related
I'm writing a packet processor and have the following classes:
class Parser {
private:
Packet* curr_pkt;
public:
void parsePacket(unsigned char byte) {
if(byte == SyncPacket::headerVal) {
SyncPacket pkt;
curr_pkt = &pkt;
}
if(byte == SyncPacket::headerVal) {
TypeAPacket pkt;
curr_pkt = &pkt;
}
}
};
class Packet {
public:
void parseByte(unsigned char byte) {}
};
class SyncPacket : public Packet {
public:
static const unsigned char headerVal = 0x0A;
void parseByte(unsigned char byte) {
<do sync>
}
};
class TypeAPacket : public Packet {
public:
static const unsigned char headerVal = 0x0B;
void parseByte(unsigned char byte) {
<do type A decode>
}
};
int main() {
std::vector<unsigned char> chars;
<stream in characters>
Parser pktParser;
for(unsigned char byte: bytes) {
pktParser.parseByte(byte);
}
}
This seems to work fine and is in fact what I think polymorphism is for. But my question is: should I be worried about the raw pointer use here? Would there be a more recommended way to do this via smart pointers?
For example, in this line:
if(byte == SyncPacket::headerVal) {
SyncPacket pkt;
curr_pkt = &pkt;
}
Technically, pkt is out of scope after the if statement, so normally it's lifetime would be over. But because curr_pkt is pointing to it, it lives on. This is a potential issue, isn't it?
Technically, pkt is out of scope after the if statement, so normally it's lifetime would be over.
This is spot on correct, but there's nothing technically about it.
But because curr_pkt is pointing to it, it lives on.
This is not correct though, the object does not exist anymore at that point, the pointer is deemed dangling, and any attempt to dereference it would be Undefined Behavior.
If you managed to make this work, then you simply got lucky.
Would there be a more recommended way to do this via smart pointers?
Not so much a "more recommended" way, but a way that actually works, yes.
You can indeed use smart pointers to accomplish what your code is trying to express. (You don't need smart pointers to get there, but it does make it a lot safer).
class Parser {
private:
std::unique_ptr<Packet> curr_pkt;
public:
void parsePacket(unsigned char byte) {
if(byte == SyncPacket::headerVal) {
curr_pkt = std::make_unique<SyncPacket>();
}
if(byte == SyncPacket::headerVal) {
curr_pkt = std::make_unique<TypeAPacket>();
}
}
};
Yes this returning the address of a local variable is not going to work.
And there is more to polymorphism, as explained in this example
#include <cstdint>
#include <memory>
#include <vector>
//-------------------------------------------------------------------------------------------------
// Packet is going to be an abstract base class
// (closest thing C++ has to an interface)
class Packet_itf
{
public:
// Dynamic polymorphism means virtual functions
// for interfaces this will be pure virtual functions
virtual void parseByte(unsigned char byte) = 0;
// destructor needs to be virtual
virtual ~Packet_itf() = default;
protected:
// protected constructor Packet_itf is not supposed to be instantiated
Packet_itf() = default;
// todo delete copy/move/assignment rule of 5
};
//-------------------------------------------------------------------------------------------------
// define a null strategy for the interface
// an implementation that does nothing
class NullPacket :
public Packet_itf
{
void parseByte(unsigned char byte) override
{
}
};
//-------------------------------------------------------------------------------------------------
// here are your packets
class SyncPacket :
public Packet_itf
{
public:
static const std::uint8_t headerVal = 0x0A;
void parseByte(unsigned char byte) override
{
//<do sync>
}
};
//-------------------------------------------------------------------------------------------------
class TypeAPacket :
public Packet_itf
{
public:
static const std::uint8_t headerVal = 0x0B;
void parseByte(unsigned char byte) override
{
//<do type A decode>
}
};
//-------------------------------------------------------------------------------------------------
// Dynamic polymorphism renamed parser to factory
// factory pattern
class PacketFactory
{
public:
static std::unique_ptr<Packet_itf> createPacket(const std::uint8_t byte)
{
switch (byte)
{
case SyncPacket::headerVal:
{
return std::make_unique<SyncPacket>();
}
break;
case TypeAPacket::headerVal:
{
return std::make_unique<TypeAPacket>();
}
break;
default:
// error handling here
break;
}
// return a default packet that does nothing
// Null strategy pattern
return std::make_unique<NullPacket>();
}
};
//-------------------------------------------------------------------------------------------------
int main()
{
//<stream in characters>
// note 0x02 isn't know yet so ut will result in a NullPacket
// no ifs in the loop need to handle that case.
std::vector<std::uint8_t> bytes{ SyncPacket::headerVal, TypeAPacket::headerVal, 0x02 };
for (const auto byte : bytes)
{
// now packet is a unique ptr that will live for the lifetime
// of this for loop then it will be deleted.
auto packet = PacketFactory::createPacket(SyncPacket::headerVal);
std::vector<std::uint8_t> data{ 0x00,0x00,0x01,0x03 };
for (const auto value : data)
{
packet->parseByte(value);
}
}
return 0;
}
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;
}
(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...
};
For protocol buffers in C++, I am wondering if it is better to contain a protobuf message in my class, or to have it be constructed from and populate an external protobuf message.
I could not find examples describing best practices for this case. I'm particular worried about performance differences between the two designs.
In my processing, I will have some cases where I am going to read only a few fields from my message and then route the message to another process (possibly manipulating the message before sendind it back out), and other cases where my objects will have a long lifetime and be used many times before being serialized again. In the first case, I could likely operate directly on the protobuf message and not even need my class, execpt to fit into an existing interface.
Here is an example message:
package example;
message Example {
optional string name = 1;
optional uint32 source = 2;
optional uint32 destination = 3;
optional uint32 value_1 = 4;
optional uint32 value_2 = 5;
optional uint32 value_3 = 6;
}
I could see one of the following designs for my class. I know these classes aren't doing anything else but accessing data, but that's not what I'm trying to focus on for this question.
Composition
class Widget
{
public:
Widget() : message_() {}
Widget(const example::Example& other_message)
: message_(other_message) {}
const example::Example& getMessage() const
{ return message_; }
void populateMessage(example::Example& message) const
{ message = message_; }
// Some example inspectors filled out...
std::string getName() const
{ return message_.name(); }
uint32_t getSource() const;
{ return message_.source(); }
uint32_t getDestination() const;
uint32_t getValue1() const;
uint32_t getValue2() const;
uint32_t getValue3() const;
// Some example mutators filled out...
void setName(const std::string& new_name)
{ message_.set_name(new_name); }
void setSource(uint32_t new_source);
{ message_.set_source(new_source); }
void setDestination(uint32_t new_destination);
void setValue1(uint32_t new_value);
void setValue2(uint32_t new_value);
void setValue3(uint32_t new_value);
private:
example::Example message_;
};
Standard data members
class Widget
{
public:
Widget();
Widget(const example::Example& other_message)
: name_(other_message.name()),
source_(other_message.source()),
destination_(other_message.destination()),
value_1_(other_messsage.value_1()),
value_2_(other_messsage.value_2()),
value_3_(other_messsage.value_3())
{}
example::Example getMessage() const
{
example::Example message;
populateMessage(message);
return message;
}
void populateMessage(example::Example& message) const
{
message.set_name(name_);
message.set_source(source_);
message.set_value_1(value_1_);
message.set_value_2(value_2_);
message.set_value_3(value_3_);
}
// Some example inspectors filled out...
std::string getName() const
{ return name_; }
uint32_t getSource() const;
{ return source_; }
uint32_t getDestination() const;
uint32_t getValue1() const;
uint32_t getValue2() const;
uint32_t getValue3() const;
// Some example mutators filled out...
void setName(const std::string& new_name)
{ name_ = new_name; }
void setSource(uint32_t new_source);
{ source_ = new_source; }
void setDestination(uint32_t new_destination);
void setValue1(uint32_t new_value);
void setValue2(uint32_t new_value);
void setValue3(uint32_t new_value);
private:
std::string name_;
uint32_t source_;
uint32_t destination_;
uint32_t value_1_;
uint32_t value_2_;
uint32_t value_3_;
};
There is no recognized "best practice" here. I have seen plenty of examples of both, and even written programs that worked both ways. Some people have very strong opinions about this, but in my opinion it depends on the use case. For example, as you say, if you plan to forward most of the data to another server, then it makes a lot of sense to keep the protobuf object around. But other times you have a more convenient internal representation -- for example, before protobufs added native support for maps, if you had a protobuf that represented a map as a repeated list of key/value pairs, you might want to convert it to an std::map upfront.
I am interested in learning how others design their software. I have used different solutions on different projects, but I have felt that I could have done it better. My implementations involved use of delegates and observers, but today I couldn't resist asking you how you would write it.
Let's assume that we have the following :
class Sensor
{
...
public:
void sensorTriggered();
};
Class Device
{
...
public:
void notifyChangesFromHardware(unsigned int inNotificationInfo);
protected:
Sensor *fireAlarm_;
};
int main()
{
Device someDevice;
return 0;
}
How would you design it if you wanted to call "Device::notifyChangesFromHardware"
from the Sensor object (fireAlarm_)?
Thank you
I would use function pointers or function object:
struct Notifier_Base
{
virtual void notify(void) = 0;
};
class Sensor
{
std::vector<Notifier_Base *> notifiers;
void publish(void)
{
std::vector<Notifier_Base *>::iterator iter;
for (iter = notifiers.begin();
iter != notifiers.end();
++iter)
{
(*iter)->notify();
}
};
See design patterns: Publisher / Consumer, Publisher / subscriber.
I would take a look at Boost Signals also like Piotr S. suggests. Also, a simple pattern I've used would look like this in your case:
template<class NotifyDelegate>
class Sensor
{
...
public:
// assumes you only have one notify delegate
Sensor( NotifyDelegate &nd ) : nd_(nd)
{
}
void sensorTriggered()
{
unsigned int notifyInfo = 99;
nd_.notifyChangesFromHardware( notifyInfo );
}
private:
NotifyDelegate &nd_;
};
Class Device
{
...
public:
void notifyChangesFromHardware(unsigned int inNotificationInfo);
};
int main()
{
Device someDevice;
Sensor<Device> someSensor(someDevice);
someSensor.sensorTriggered();
return 0;
}
Take a look at Observer Pattern as well.