Inheriting a packed C struct - c++

I am using a third party library (mavlink) that defines a number of structs that are all tagged with __attribute__((packed)) so they can be efficiently be transmitted across a serial connection (it is written in C and I am using it in a C++ application). When I receive and reconstruct them I would like to add a time stamp field to them. I think the simplest way is to create a new struct that inherits the existing struct. i.e. in the mavlink library this struct is defined:
MAVPACKED(
typedef struct __mavlink_heartbeat_t {
uint32_t custom_mode;
uint8_t type;
uint8_t autopilot;
uint8_t base_mode;
uint8_t system_status;
uint8_t mavlink_version;
}) mavlink_heartbeat_t;
where MAVPACKED is a macro that applies __attribute__((packed)). sizeof(mavlink_heartbeat_t) returns 9. If I define
struct new_heartbeat_t : mavlink_heartbeat_t
{
uint64_t timestamp;
};
sizeof(new_heartbeat_t) returns 24, so it looks like 7 padding bytes are added (I would assume to end of mavlink_heartbeat_t so that timestamp start at byte 16.)
Are there any gotchas or things to be aware of when doing this or is there a better way?

Inheritance is a is a kind of relationship.
Is the local representation of a heartbeat really a kind of wire message? I doubt it.
But it might reasonably contain one.
I would encapsulate it something like this:
#include <cstdint>
#include <cstddef>
typedef struct __attribute__((packed)) __mavlink_heartbeat_t {
uint32_t custom_mode;
uint8_t type;
uint8_t autopilot;
uint8_t base_mode;
uint8_t system_status;
uint8_t mavlink_version;
} mavlink_heartbeat_t;
extern std::uint64_t now();
void sync_fetch_data(mavlink_heartbeat_t&);
void foo(uint8_t);
struct local_heartbeat
{
mavlink_heartbeat_t const& get_io_buffer() const {
return io_buffer_;
}
mavlink_heartbeat_t& prepare_receive() {
request_timestamp_ = now();
return io_buffer_;
}
void complete_receive() {
response_timestamp_ = now();
}
std::uint64_t get_response_timestamp() const {
return response_timestamp_;
}
private:
// stuff in here might have suspect alignment
mavlink_heartbeat_t io_buffer_;
// but these will be aligned for optimal performance
std::uint64_t request_timestamp_;
std::uint64_t response_timestamp_;
};
int main()
{
// create my local representation
local_heartbeat hb;
// prepare it to receive data
auto& buffer = hb.prepare_receive();
// somehow populate the buffer
sync_fetch_data(buffer); // could be async, etc
// notify the object that reception is complete
hb.complete_receive();
// now use the local representation
foo(hb.get_io_buffer().system_status);
}

Related

How should I approach parsing the network packet using C++ template?

Let's say I have an application that keeps receiving the byte stream from the socket. I have the documentation that describes what the packet looks like. For example, the total header size, and total payload size, with the data type corresponding to different byte offsets. I want to parse it as a struct. The approach I can think of is that I will declare a struct and disable the padding by using some compiler macro, probably something like:
struct Payload
{
char field1;
uint32 field2;
uint32 field3;
char field5;
} __attribute__((packed));
and then I can declare a buffer and memcpy the bytes to the buffer and reinterpret_cast it to my structure. Another way I can think of is that process the bytes one by one and fill the data into the struct. I think either one should work but it is kind of old school and probably not safe.
The reinterpret_cast approach mentioned, should be something like:
void receive(const char*data, std::size_t data_size)
{
if(data_size == sizeof(payload)
{
const Payload* payload = reinterpret_cast<const Payload*>(data);
// ... further processing ...
}
}
I'm wondering are there any better approaches (more modern C++ style? more elegant?) for this kind of use case? I feel like using metaprogramming should help but I don't have an idea how to use it.
Can anyone share some thoughts? Or Point me to some related references or resources or even relevant open source code so that I can have a look and learn more about how to solve this kind of problem in a more elegant way.
There are many different ways of approaching this. Here's one:
Keeping in mind that reading a struct from a network stream is semantically the same thing as reading a single value, the operation should look the same in either case.
Note that from what you posted, I am inferring that you will not be dealing with types with non-trivial default constructors. If that were the case, I would approach things a bit differently.
In this approach, we:
Define a read_into(src&, dst&) function that takes in a source of raw bytes, as well as an object to populate.
Provide a general implementation for all arithmetic types is provided, switching from network byte order when appropriate.
Overload the function for our struct, calling read_into() on each field in the order expected on the wire.
#include <cstdint>
#include <bit>
#include <concepts>
#include <array>
#include <algorithm>
// Use std::byteswap when available. In the meantime, just lift the implementation from
// https://en.cppreference.com/w/cpp/numeric/byteswap
template<std::integral T>
constexpr T byteswap(T value) noexcept
{
static_assert(std::has_unique_object_representations_v<T>, "T may not have padding bits");
auto value_representation = std::bit_cast<std::array<std::byte, sizeof(T)>>(value);
std::ranges::reverse(value_representation);
return std::bit_cast<T>(value_representation);
}
template<typename T>
concept DataSource = requires(T& x, char* dst, std::size_t size ) {
{x.read(dst, size)};
};
// General read implementation for all arithmetic types
template<std::endian network_order = std::endian::big>
void read_into(DataSource auto& src, std::integral auto& dst) {
src.read(reinterpret_cast<char*>(&dst), sizeof(dst));
if constexpr (sizeof(dst) > 1 && std::endian::native != network_order) {
dst = byteswap(dst);
}
}
struct Payload
{
char field1;
std::uint32_t field2;
std::uint32_t field3;
char field5;
};
// Read implementation specific to Payload
void read_into(DataSource auto& src, Payload& dst) {
read_into(src, dst.field1);
read_into<std::endian::little>(src, dst.field2);
read_into(src, dst.field3);
read_into(src, dst.field5);
}
// mind you, nothing stops you from just reading directly into the struct, but beware of endianness issues:
// struct Payload
// {
// char field1;
// std::uint32_t field2;
// std::uint32_t field3;
// char field5;
// } __attribute__((packed));
// void read_into(DataSource auto& src, Payload& dst) {
// src.read(reinterpret_cast<char*>(&dst), sizeof(Payload));
// }
// Example
struct some_data_source {
std::size_t read(char*, std::size_t size);
};
void foo() {
some_data_source data;
Payload p;
read_into(data, p);
}
An alternative API could have been dst.field2 = read<std::uint32_t>(src), which has the drawback of requiring to be explicit about the type, but is more appropriate if you have to deal with non-trivial constructors.
see it in action on godbolt: https://gcc.godbolt.org/z/77rvYE1qn

C++ Variable Array length within struct

hey guys i trying to write a handler for a generalised master/slave communication.
The implementation should be able to handle different communication types (USB/modbus/SPI etc.)
For every communication type a new instance of this class is created.
Each communication way has another max datalength limit. Every received packed (byte array) is mapped on a struct
class My_Communication
{
struct My_Struct
{
//some Header Information
byte data[max_data_length]; ///< this array has a different datalength.
}
}
Can i set the (const) datalength within a class constructor?
If you don't know the array size at compile time you should use std::vector:
class My_Communication
{
struct My_Struct
{
//some Header Information
std::vector< byte > data;
};
};
If you know the size at compile time then use std::array in a templated class:
class My_Communication
{
template < size_t DataSize >
struct My_Struct
{
//some Header Information
std::array< byte, DataSize > data;
};
};

Member variables seem to change by their own.?

Usually given answers on Stack Overflow are enough to deal with problems which I come across, but now I'm running into sth. special...
I'm trying to build a custom and modular solution for driving a stepper motor using a shift register. For this I tried to implement a "RegisterWrite" function (just like Arduinos "digitalWrite") to change the states of the registers output pins.
Now it seems that I cannot use values of the member variables of my "register" object in its member functions. I don't know where I screwed up...
I'm trying to compress my codes as much as possible:
main.cpp
#include<Register.h>
Register register1(8, 16, 2, 0);
void motor(){ //this function is called via a html request handle
uint8_t value[8] = {1,0,1,0,1,0,1,0}; //setting values for register outputs
register1.setOutputValues(value); //writing values to member variable
register1.setOutput(); //actually setting the output of the register
}
void setup(){
something something
}
void main(){
something something
}
Register.h
#ifndef REGISTER_H
#define REGISTER_H
#include<Arduino.h>
class Register{
private:
int _NumberOutputs;
public:
uint8_t _OutputValues[];
uint8_t _DataPin;
uint8_t _ClockPin;
uint8_t _ShiftPin;
Register(int NOutputs, uint8_t Data, uint8_t Clock, uint8_t Shift);
Register();
void setOutputValues(uint8_t OValues[]);
void setOutput();
};
#endif
Register.cpp
#include<Register.h>
#include<Arduino.h>
Register::Register(int NOutputs, uint8_t Data, uint8_t Clock, uint8_t Shift){
_NumberOutputs = NOutputs;
_DataPin = Data;
_ClockPin = Clock;
_ShiftPin = Shift;
}
Register::Register(){
}
void Register::setOutputValues(uint8_t OValues[]){
for (int i = 0; i < _NumberOutputs; i++){
_OutputValues[i] = OValues[i];
}
}
void Register::setOutput(){
digitalWrite(_DataPin, 0);
....
....
}
This is, where the value of _DataPin ends up being "1" instead of "16" what screws up the code.
Now in addition, if I go back to main.cpp... Before I call register1.setOutput() the actual value of _DataPin is "16", and then again after calling register1.setOutput(). It seems that only inside of register1.setOutput() _DataPin equals 1. I Have no clue.

join structs in c++? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I'm trying to create an ethernet packet plus TCP/IP payload, for that I've create three structs as follows: ethernet struct, tcp struct and ip struct all these structs are filled with valid information but I don't know what I need to do now, I need join the spared structs into a plain struct (such as an array) since I want to inject the constructed packet and all bits need to be consecutive.
Define a new class, make these three structs its instance variables, code setters and getters for the class. You may want to pay attention to const correctness when writing getters - mark getters with const keyword.
struct EthStruct1
{
// ...
};
struct EthStruct2
{
// ...
};
struct EthStruct3
{
// ...
}
class newClass
{
public :
newClass(){}
~newClass(){}
// Add functions to get and set values in A. Mark getters with const for immutability of receiver
// Add functions to get and set values in B. Mark getters with const for immutability of receiver
// Add functions to get and set values in C. Mark getters with const for immutability of receiver
private:
EthStruct1 A;
EthStruct2 B;
EthStruct3 C;
};
Declare a byte array whose size is the sum of the sizes of the three structs. Then copy the raw bytes of each struct into the array where needed. Then use the array as needed.
struct ethernet
{
...
};
struct tcp
{
...
};
struct ip
{
...
};
ethernet e;
tcp t;
ip i;
unsigned char arr[sizeof(e)+sizeof(t)+sizeof(i)];
memcpy(&arr[0], &e, sizeof(e)];
memcpy(&arr[sizeof(e)], &t, sizeof(t)];
memcpy(&arr[sizeof(e)+sizeof(t)], &i, sizeof(i)];
Alternatively:
struct ethernet
{
...
};
struct tcp
{
...
};
struct ip
{
...
};
struct pkt
{
ethernet e;
tcp t;
ip i;
};
ethernet e;
tcp t;
ip i;
unsigned char arr[sizeof(pkt)];
pkt *p = (pkt) &arr[0];
p->e = e;
p->t = t;
p->i = i;
I like to use union when converting structures to byte arrays. Be sure your compiler is using single-byte structure alignment... for the Windows compiler I know you can use #pragma pack(push,1) to start that and #pragma pack(pop) to terminate. I will use this way in my example.
#pragma pack(push,1) // start single-byte struct alignment
struct EthernetInfo
{
// data here
};
struct TCPInfo
{
// data here
};
struct IPInfo
{
// data here
};
union Packet
{
struct {
struct EthernetInfo ethernetInfo;
struct TCPInfo tcpInfo;
struct IPInfo ipInfo;
} AsPacket;
unsigned char AsBytes[sizeof(EthernetInfo) + sizeof(TCPInfo) + sizeof(IPInfo)];
};
#pragma pack(pop) // revert to whatever struct alignment was in use before

Receiving flexible array over socket

I got a codebase which used to transfer several kinds of message on socket to another process and used a union for that
#define maxlen 60
typedef struct
{
TYRES t[MAXLEN];
} TYRE_T;
typdef struct
{
TUBES t[MAXLEN];
}TUBES_T;
typedef struct
{
TYPE t[MAXLEN];
} TYPE_T;
typedef union
{
TYRE_T tyres;
TUBES_T tubes;
TYPE_T type;
} messageunion;
typedef struct
{
int code;
messageunion msg;
} socketmessage_t;
enum msgtype
{
INVALID = -1;
TYRES,
TUBES,
TYPES
};
//On clientside
processMessage(socketmessage_t* msg)
{
switch(msg->code)
{
....
}
}
Now MAXLEN is not a macro but it will be run time variable evaluated by program. so I changed all array members to flexible array like this
typedef struct
{
int size;
TYRES t[];
}
My question is almost all socket functions needs size of buffer to receive message so how to calculate that size here?
Thanks
First you should probably start using fixed-size integers, like uint32_t (from <cstdint>) for the sizes and message types.
Then the receiving application know how much to read to get the message type, and how much to read to get the size. Then simply read the specified number of bytes into the correct structure.
The important thing is that all structures in the union must start with a size member.