Here is pseudo code towards an algorithm that may fit your needs to ensure the use with the proper target OS and platform.
If using the C
language you will not be able to use classes
, templates
and a few other things, but you can use preprocessor directives
to create the version of your struct(s)
you need based on the OS
, the architect CPU-GPU-Hardware Controller Manufacturer {Intel, AMD, IBM, Apple, etc.}
, platform x86 - x64 bit
, and finally the endian
of the byte layout. Otherwise the focus here would be towards C++ and the use of templates.
Take your struct(s)
for example:
struct Sensor1Telemetry {
int16_t temperature;
uint32_t timestamp;
uint16_t voltageMv;
// etc...
} __attribute__((__packed__));
struct TelemetryPacket {
Sensor1Telemetry tele1;
Sensor2Telemetry tele2;
// etc...
} __attribute__((__packed__));
You could template these structs as such:
enum OS_Type {
// Flag Bits - Windows First 4bits
WINDOWS = 0x01 // 1
WINDOWS_7 = 0x02 // 2
WINDOWS_8 = 0x04, // 4
WINDOWS_10 = 0x08, // 8
// Flag Bits - Linux Second 4bits
LINUX = 0x10, // 16
LINUX_vA = 0x20, // 32
LINUX_vB = 0x40, // 64
LINUX_vC = 0x80, // 128
// Flag Bits - Linux Third Byte
OS = 0x100, // 256
OS_vA = 0x200, // 512
OS_vB = 0x400, // 1024
OS_vC = 0x800 // 2048
//....
};
enum ArchitectureType {
ANDROID = 0x01
AMD = 0x02,
ASUS = 0x04,
NVIDIA = 0x08,
IBM = 0x10,
INTEL = 0x20,
MOTOROALA = 0x40,
//...
};
enum PlatformType {
X86 = 0x01,
X64 = 0x02,
// Legacy - Deprecated Models
X32 = 0x04,
X16 = 0x08,
// ... etc.
};
enum EndianType {
LITTLE = 0x01,
BIG = 0x02,
MIXED = 0x04,
// ....
};
// Struct to hold the target machines properties & attributes: add this to your existing struct.
struct TargetMachine {
unsigned int os_;
unsigned int architecture_;
unsigned char platform_;
unsigned char endian_;
TargetMachine() :
os_(0), architecture_(0),
platform_(0), endian_(0) {
}
TargetMachine( unsigned int os, unsigned int architecture_,
unsigned char platform_, unsigned char endian_ ) :
os_(os), architecture_(architecture),
platform_(platform), endian_(endian) {
}
};
template<unsigned int OS, unsigned int Architecture, unsigned char Platform, unsigned char Endian>
struct Sensor1Telemetry {
int16_t temperature;
uint32_t timestamp;
uint16_t voltageMv;
// etc...
} __attribute__((__packed__));
template<unsigned int OS, unsigned int Architecture, unsigned char Platform, unsigned char Endian>
struct TelemetryPacket {
TargetMachine targetMachine { OS, Architecture, Platform, Endian };
Sensor1Telemetry tele1;
Sensor2Telemetry tele2;
// etc...
} __attribute__((__packed__));
With these enum
identifiers you could then use class template specialization
to set the up this class
to its needs depending on the above combinations. Here I would take all the common cases that would seem to work fine with default
class declaration & definition
and set that as the main class's functionality. Then for those special cases, such as different Endian
with byte order, or specific OS versions doing something in a different way, or GCC versus MS
compilers with the use of __attribute__((__packed__))
versus #pragma pack()
can then be the few specializations that need to be accounted for. You shouldn't need to specify a specialization for every possible combination; that would be too daunting and time consuming, should only need to do the few rare case scenarios that can occur to make sure you always have proper code instructions for the target audience. What also makes the enums
very handy too is that if you pass these as a function argument, you can set multiple ones at a time as they are designed as bit flags. So if you want to create a function that takes this template struct as its first argument, then supported OS's as its second you could then pass in all available OS support as bit flags.
This may help to ensure that this set of packed structures
is being "packed" and or aligned correctly according to the appropriate target and that it will always perform the same functionality to maintain portability across different platforms.
Now you may have to do this specialization twice between the preprocessor directives for different supporting compilers. Such that if the current compiler is GCC as it defines the struct in one way with its specializations, then Clang in another, or MSVC, Code Blocks etc. So there is a little overhead to get this initially set up, but it should, could highly ensure that it is being properly used in the specified scenario or combination of attributes of the target machine.