I have a struct representing a byte having a fix offset 0b1111. I have defined a conversion operator to cast the SByte to unit8.
How can I have the opposite conversion (from uint8 to SByte)?
Is the only solution to replace the SByte with a uint8_t or maybe a union in the first place?
struct SByte
{
uint8_t offset : 4;
uint8_t bit4 : 1;
uint8_t bit5 : 1;
uint8_t bit6 : 1;
uint8_t bit7 : 1;
SByte():offset(15), bit4(0), bit5(0), bit6(0), bit7(0){}
explicit operator int8_t() const
{
return static_cast<uint8_t>((bit7 << 7) | (bit6 << 6) | (bit5 << 5) | (bit4 << 4));
}
};
int main()
{
auto lbyte = SByte();
auto result = static_cast<int8_t>(lbyte);
assert(result == 15); // 0b00001111
}
How can I have the opposite conversion (from uint8 to SByte)?
By defining a constructor:
explicit SByte(uint8_t byte) {
// extract individual bits from `byte`
}
Related
I have a structure like this
struct foo {
string str1;
uint16_t int1
string str2;
uint32_t int2;
string str3;
};
strings str1, str2 , str3 are of fixed length of 12 bytes, 3 bytes,etc. left padded with spaces.
I have a function
void func(const byte* data, const size_t len) which is supposed to convert the byte * data to structure foo. len is length of data.What are the ways in which I can do this?
Again the data is const pointer of byte type and will not have null characters in between to distinguish different members.
Should I use character array instead of string for str1, str2, str3?
Easiest (but most errorprone) way is to just reinterpret_cast / std::memcpy if the strings have fixed length:
// no padding
#pragma pack(push, 1)
struct foo {
char str1[12];
uint16_t int1;
char str2[3];
uint32_t int2;
char str3[4];
};
#pragma pack(pop)
void func(const byte* data, const size_t len) {
assert(len == sizeof(foo));
// non owning
const foo* reinterpreted = reinterpret_cast<const foo*>(data);
// owning
foo reinterpreted_val = *reinterpret_cast<const foo*>(data);
foo copied;
memcpy(&copied, data, len);
}
Notes:
Make sure that you're allowed to use reinterpret_cast
https://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing
if you'd try to use strlen or another string operation on any of the strings you most likely will get UB, since the strings are not null terminated.
Slightly better approach:
struct foo {
char str1[13];
uint16_t int1;
char str2[4];
uint32_t int2;
char str3[5];
};
void func(const char* data, const size_t len) {
foo f;
memcpy(f.str1, data, 12);
f.str1[12] = '\0';
data+=12;
memcpy(&f.int1, data, sizeof(uint16_t));
data+=sizeof(uint16_t);
memcpy(f.str2, data, 3);
f.str2[3] = '\0';
data+=3;
memcpy(&f.int2, data, sizeof(uint32_t));
data+=sizeof(uint32_t);
memcpy(f.str3, data, 4);
f.str3[4] = '\0';
data+=4;
}
Notes:
You could combine both approaches to get rid of the pointer arithmetic. That would also account for any padding in your struct you might have.
I think the easiest way to do this is to change the string inside the structure
to the type of char. Then you can easily copy the objects of this
structure according to its size.
you will have to somehow deal with the byte order on machines with different byte
order
struct foo {
char str1[12];
uint16_t int1;
char str2[3];
uint32_t int2;
char str3[5];
};
byte* Encode(foo* p, int Size) {
int FullSize = Size * sizeof(foo);
byte* Returner = new byte[FullSize];
memcpy_s(Returner, FullSize, p, FullSize);
return Returner;
}
foo * func(const byte* data, const size_t len) {
int ArrSize = len/sizeof(foo);
if (!ArrSize || (ArrSize* sizeof(foo)!= len))
return nullptr;
foo* Returner = new foo[ArrSize];
memcpy_s(Returner, len, data, len);
return Returner;
}
int main()
{
const size_t ArrSize = 3;
foo Test[ArrSize] = { {"Test1",1000,"TT",2000,"cccc"},{"Test2",1001,"YY",2001,"vvvv"},{"Test1",1002,"UU",2002,"bbbb"}};
foo* Test1 = nullptr;
byte* Data = Encode(Test, ArrSize);
Test1 = func(Data, ArrSize * sizeof(foo));
if (Test1 == nullptr) {
std::cout << "Error extracting data!" << std::endl;
delete [] Data;
return -1;
}
std::cout << Test1[0].str1 << " " << Test1[1].str1 << " " << Test1[2].str3 << std::endl;
delete [] Data;
delete[] Test1;
return 0;
}
output
Test1 Test2 bbbb
I'm trying to update some legacy C code that uses an array as a data container with named access via macros to a more elegant C++17 solution (will be updated to C++20 when available, possible C++20 solutions are welcome). Sorry if there is a lot of code, but this is my first StackOverflow question suggestions regarding layout are welcome.
The current legacy C design:
#define WORD_ARR_SIZE 100
int16_t word_arr[WORD_ARR_SIZE]; //two byte variables
#define var0 word_arr[0] //macros are used to hide the array and use its members like variables
#define myValue word_arr[1]
#define myParameter word_arr[2]
#define free ((uint16_t)word_arr[3]) //'hidden' explicit cast needed when using the array as all data must be of the same type
#define var1 word_arr[4]
#define var2 word_arr[5]
#define var3 word_arr[6]
#define var4 word_arr[7]
#define var5 word_arr[7] //very easy to write the wrong index when adding new 'variables'
extern int send(int16_t* arr, size_t size); //The array is necessary as it needs to be fed to a library (code cannot be modified)
int main()
{
(uint16_t)var1 = UINT16_MAX; //'visible' explicit cast needed when using the array as all data is of the same type
myValues = 50;
for(int a = 20; a < 30; a++)
{
word_arr[a] = 10; //array is also used like it should be
}
return send(word_arr, WORD_ARR_SIZE);
}
My first attempt at solving the problem was using a struct instead of an array, this removed the explicit casts and the need for macros, but had the disadvantage of missing the simple access via index that the array implementation had, replacing it with an ugly reinterpret_cast.
//no need for pragma pack, the code doesn't care about padding
struct word_arr_t
{
int16_t var0; //no more macros
int16_t myValue;
int16_t myParameter;
uint16_t free; //no need for cast, value is alredy declared using the correct type
int16_t var1;
int16_t var2; //no way to get the index as if it was an array by simply using the value.
int16_t var3;
int16_t var4;
int16_t var5;
}word_arr;
constexpr size_t WORD_ARR_SIZE = sizeof(word_arr_t) / sizeof(uint16_t);
auto word_arr_p = reinterpret_cast<int16_t*>(&word_arr); //needed for indexed access
extern int send(int16_t* arr, size_t size);
int main()
{
word_arr.var1 = UINT16_MAX;
word_arr.myValues = 50;
for(int a = 20; a < 30; a++)
{
word_arr_p[a] = 10; //'hidden' pointer arithmetic to access the struct like an array
}
return send(word_arr_p, sizeof(word_arr_t));
}
The current solution:
I created a custom templated class called SmartStruct, I pass the struct type and values type in the template; I created an overload to operator[] allowing access via index hiding the ugly reinterpret_cast;
/**
* \brief A wrapper for structs made of object of the same type, allows indexed access
* \tparam StructT struct type
* \tparam DataT struct data type
*/
template <typename StructT, typename DataT>
class SmartStruct
{
DataT* m_dataPointer;
public:
/**
* \brief let the struct be accessible from the outside as well
*/
StructT Data;
const size_t Count;
/**
* \brief Default constructor
*/
SmartStruct();
/**
* \brief Construct by struct copy
* \param data struct to copy
*/
explicit SmartStruct(const StructT& data);
/**
* \brief operator to access struct in array style
* \param index element to access
* \return element, if index >= size then first element
*/
DataT& operator[](size_t index);
};
template <typename StructT, typename DataT>
SmartStruct<StructT, DataT>::SmartStruct() : Data{}, Count{ sizeof Data / sizeof(DataT) }
{
m_dataPointer = reinterpret_cast<DataT*>(&Data);
}
template <typename StructT, typename DataT>
SmartStruct<StructT, DataT>::SmartStruct(const StructT& data) : Count{ sizeof data / sizeof(DataT) }
{
//copy the struct
Data = data;
m_dataPointer = reinterpret_cast<DataT*>(&Data);
}
template <typename StructT, typename DataT>
DataT& SmartStruct<StructT, DataT>::operator[](size_t index)
{
if (index >= Count)
{
return *m_dataPointer;
}
return m_dataPointer[index];
}
Usage example:
struct word_arr_t
{
int16_t var0;
int16_t myValue;
int16_t myParameter;
uint16_t free;
int16_t var1;
int16_t var2;
int16_t var3; //Still no way to get array index from variable name
int16_t var4;
int16_t var5;
};
SmartStruct<word_arr_t, word> smart_word_arr{}; //Would love it if I could use std containers interface without having to implement it all by hand...
extern int send(int16_t* arr, size_t size);
int main()
{
word_arr_t& word_arr = smart_word_arr.Data;
word_arr.var1 = UINT16_MAX;
word_arr.myValues = 50;
for(int a = 20; a < 30; a++)
{
smart_word_arr[a] = 10;
}
return send(&smart_word_arr[0], smart_word_arr.Count);
}
Now that I got the context out of the way I can finally go to the real question:
Would it be possible to use a std::array as a a data container for the struct?
meaning initializing it via the struct; this would make it possible to access the data via variable using the struct itself and via index using std::array with the added bonus of a std interface without having to re-implement it.
My current attempt at getting this solution to work:
struct word_arr_t
{
int16_t var0;
int16_t myValue;
int16_t myParameter;
uint16_t free;
int16_t var1;
int16_t var2;
int16_t var3; //Still no way to get array index from variable name
int16_t var4;
int16_t var5;
}word_struct;
std:.array<int16_t, sizeof(word_arr_t) / sizeof(word)> word_array{};
//std:.array<int16_t, sizeof(word_arr_t) / sizeof(word)> word_array{&word_struct}; would be lovely if I could do this.
//word_array.Data = reinterpret_cast<int16_t*>(&word_struct); this would also be good.
extern int send(int16_t* arr, size_t size);
int main()
{
word_struct.var1 = UINT16_MAX;
word_struct.myValues = 50;
//copy struct into array, very very bad as it's not usable unless you know when
//code writes to the struct and when code writes to the array,
//this could be solved by wrapping the array into a read only object but still not ideal
//and extremely slow especially if the struct is very large
memcpy(word_array.Data, &word_struct, sizeof(word_struct));
for(auto& a : word_array)
{
a = 10;
}
return send(word_array.Data, word_array.Size);
}
If compiling this part of the code as C is an option, then the most elegant solution by far is to use a union. Since alignment isn't an issue here and there will be no padding. This can be done in C only, which allows type punning through unions.
typedef union
{
struct // C11 anonymous struct
{
int16_t var0;
int16_t myValue;
int16_t myParameter,
uint16_t free;
int16_t var1;
int16_t var2;
int16_t var3;
int16_t var4;
// var5 not declared on purpose
};
int16_t arr [WORD_ARR_SIZE];
} word_t;
This is how the original C code should have been written. Here all type information and special cases are handled at compile-time, and type punning from int16_t to uint16_t is well-defined and their effective types alias.
You can make an enum for the special case var5 indexing:
typedef enum
{
var0_index = 0,
myValue_index = 1,
myParameter_index = 2,
free_index = 3,
var_1index = 4,
var_2index = 5,
var_3index = 6,
var_4index = 7,
var_5index = 7,
} var_t;
word_t word =
{
// different sorts of designated initializers can be mixed:
.var0 = x,
.myValue = y,
[var5_index] = z,
};
("word" is a horrible type/variable name though, since in computer science the term word refers to a complete integer type. So please come up with something better.)
You cannot treat separate variable as array.
Not ideal but you can have accessors, something like:
struct word_arr_t
{
std::array<std::int16_t, 100> data{}; // your array
int16_t& var0() { return data[0]; }
int16_t& myValue() { return data[1]; }
int16_t& myParameter { return data[2]; }
uint16_t free() const { return static_cast<uint16_t>(data[3]); }
void set_free(uint16_t value) { data[3] = static_cast<int16_t>(value); }
int16_t& var1() { return data[4]; }
int16_t& var2() { return data[5]; }
int16_t& var3() { return data[6]; }
int16_t& var4() { return data[7]; }
int16_t& var5() { return data[8]; }
};
int main()
{
word_arr_t word_arr;
word_arr.var1() = INT16_MAX;
word_arr.myValues() = 50;
send(word_arr.data.data(), word_arr.data.size());
// ...
}
or enum indexes:
enum Index
{
var0,
myValue,
myParameter,
free,
var1,
var2,
var3,
var4,
var5,
};
struct word_arr_t
{
std::array<std::int16_t, 100> data{}; // your array
};
int main()
{
word_arr_t word_arr;
word_arr.data[Index::var1] = INT16_MAX;
word_arr.data[Index::myValues] = 50;
send(word_arr.data.data(), word_arr.data.size());
// ...
}
Would it be possible to use a std::array as a a data container for the struct?
A POD struct in C++ is a class that is trivial and has standard layout.
Starting with std::array as the underlying storage for a "struct" means you're not actually defining any struct with named members. You would have to write a class that contains the array and members that access indices of the array, and that can easily become not trivial and not standard layout.
By overloading operator[] you can access the "array" members of a struct with array-like syntax, while keeping the struct definition and POD status.
If it looks like a struct and []'s like a std::array, then it's probably good enough as far as the end-user is concerned.
Here is an example program demonstrating that:
#include <iostream>
#include <stdexcept>
#include <type_traits>
struct ArrPODStruct
{
int someHeaderValue1;
int someHeaderValue2;
int someHeaderValue3;
int a0;
int a1;
int a2;
int a3;
int a4;
int operator[] (int i)
{
switch (i)
{
case 0: return a0;
case 1: return a1;
case 2: return a2;
case 3: return a3;
case 4: return a4;
default: throw std::out_of_range("...");
}
}
};
int main()
{
ArrPODStruct arr;
// Put some useful data in arr...
std::cout << "Is POD: " << (std::is_pod<ArrPODStruct>() ? "Yes" : "No") << "\n";
std::cout << "Element 0: " << arr.a0 << " or " << arr[0] << "\n";
std::cout << "Element 1: " << arr.a1 << " or " << arr[1] << "\n";
std::cout << "Element 2: " << arr.a2 << " or " << arr[2] << "\n";
std::cout << "Element 3: " << arr.a3 << " or " << arr[3] << "\n";
std::cout << "Element 4: " << arr.a4 << " or " << arr[4] << "\n";
}
Output:
Is POD: Yes
Element 0: 0 or 0
Element 1: 0 or 0
Element 2: 0 or 0
Element 3: 0 or 0
Element 4: 0 or 0
I have a program that reads some bytes from a file. If the second byte is exactly 0, it should cast the current type to the correct one.
This is what I have:
struct Event {
virtual ~Event() {}
};
struct MidiEvent : public Event {
unsigned m_channel;
uint8_t byte1;
uint8_t byte2;
MidiEvent(uint8_t channel) : m_channel{ channel } {}
};
struct EventNoteOff : public MidiEvent {
uint8_t note() { return byte1; }
uint8_t velocity() { return byte2; }
EventNoteOff(std::fstream& t_midi_file, uint8_t channel);
};
struct EventNoteOn : public MidiEvent {
uint8_t note() { return byte1; }
uint8_t velocity() { return byte2; }
EventNoteOn(std::fstream& t_midi_file, uint8_t channel);
};
And this is the part where the program decides which class assign to each bytes:
// track_type and track_chanel is a "uint8_t", m_event is a "Event *" and t_midi_file is a std::fstream opened with 'in' and 'binary' flags.
switch (track_type) {
case 0x80:
m_event = new EventNoteOff{ t_midi_file, track_channel };
break;
case 0x90:
t_midi_file.seekg(1, t_midi_file.cur);
char c;
t_midi_file.read(&c, 1);
t_midi_file.seekg(-2, t_midi_file.cur);
if (c == 0)
m_event = new EventNoteOff{ t_midi_file, track_channel };
else
m_event = new EventNoteOn{ t_midi_file, track_channel };
break;
...
}
Whenever track_type is 0x90 and byte2 has the value of 0, i want m_event to behave like a EventNoteOff* with same values as before, not as an EventNoteOn*. My attempt works but I get a feel that this approach is dirty because it involves moving around the file read pointer and creating a temporal variable, when I think it could be done with a reinterpret_cast.
I tried doing
case 0x90:
m_event = new EventNoteOn{ t_midi_file, track_channel };
if(dynamic_cast<EventNoteOn*>(m_event)->velocity() == 0)
m_event = reinterpret_cast<EventNoteOff*>(m_event);
break;
with no luck.
What is a better way to do this?
What I am trying to accomplish is either return an array of bytes in a function so that I can do:
byte array[8] = function();
I am using my self made library to get values with the i2c bus. This is the library:
#ifndef MPULIBRARY_HPP
#define MPULIBRARY_HPP
#include "hwlib.hpp"
namespace mpulibrary{
class mpumanager
{
private:
hwlib::i2c_bus_bit_banged_scl_sda bus;
byte startregister[2] = {0x6B, 0};
byte adress = 0x68;
byte xregister[8] = {0x3B};
byte yregister[8] = {0x3D};
byte zregister[8] = {0x3F};
public:
mpumanager(hwlib::i2c_bus_bit_banged_scl_sda bus):
bus(bus)
{}
void startup(){
bus.write(adress, startregister, 2);
}
byte getXaxis(){
bus.write(adress, xregister, 1);
bus.read(adress, xregister, 2);
return xregister[8];
}
byte getYaxis(){
bus.write(adress, yregister, 1);
bus.read(adress, yregister, 2);
return yregister[8];
}
byte getZaxis(){
bus.write(adress, zregister, 1);
bus.read(adress, zregister, 2);
return zregister[8];
}
};
};
#endif // MPULIBRARY_HPP
My other code:
auto manager = mpulibrary::mpumanager(i2c_bus);
for(;;){
manager.startup();
byte ydata[8] = {};
manager.getYaxis(ydata[8]);
byte zdata[8] = {0x3F};
i2c_bus.write(adress, zdata, 1);
i2c_bus.read(adress, zdata, 2);
int16_t yaxis = 0;
int16_t zaxis = 0;
yaxis = (ydata[0] << 8) + ydata[1];
zaxis = (zdata[0] << 8) + zdata[1];
hwlib::cout << "Y: " << yaxis << " Z:" << zaxis << "\n";
hwlib::wait_ms(100);
}
I hope someone can provide me an answer. Thanks in advance!
byte array[8] = function();
You cannot directly with C-array.
You have to wrap it in a class (as std::array<byte, 8>).
byte xregister[8] = {0x3B};
byte getXaxis(){
bus.write(adress, xregister, 1);
bus.read(adress, xregister, 2);
return xregister[8];
}
You may return reference to array:
byte xregister[8] = {0x3B};
using byte_array = byte[8]; // typedef to avoid strange syntax in return type
byte_array& getXaxis(){
bus.write(adress, xregister, 1);
bus.read(adress, xregister, 2);
return xregister;
}
and use it:
auto& xAxis = manager.getXaxis();
You could pass an array by reference if you could transform your function as:
template<int N>
void function(byte (&array)[N]) {
}
Or if you want to restrict the size as (e.g., 8):
void function(byte (&array)[8]) {
}
You can't return a raw array because C-style raw arrays are not copy-able.
union LowLevelNumber
{
unsigned int n;
struct
{
unsigned int lowByte : 8;
unsigned int highByte : 8;
unsigned int upperLowByte : 8;
unsigned int upperHighByte : 8;
} bytes;
struct
{
unsigned int lowWord : 16;
unsigned int highWord : 16;
} words;
};
This union allows me to access the unsigned integer byte or word-wise.
However, the code looks rather ugly:
var.words.lowWord = 0x66;
Is there a way which would allow me to write code like this:
var.lowWord = 0x66;
Update:
This is really about writing short / beautiful code as in the example above. The union solution itself does work, I just don't want to write .words or .bytes everytime I access lowWord or lowByte.
union LowLevelNumber {
unsigned int n;
struct {
unsigned int lowByte : 8;
unsigned int highByte : 8;
unsigned int upperLowByte : 8;
unsigned int upperHighByte : 8;
};
struct {
unsigned int lowWord : 16;
unsigned int highWord : 16;
};
};
Note the removed bytes and words names.
C++
Would http://www.cplusplus.com/reference/stl/bitset/ serve for your needs?
Plain C version would look something like this:
int32 foo;
//...
//Set to 0x66 at the low byte
foo &= 0xffffff00;
foo |= 0x66;
This is probably going to be more maintainable down the road than writing a custom class/union, because it follows the typical C idiom.
You can make
short& loword() { return (short&)(*(void*)&m_source); }
and use it if you don't care parenthesis.
Or you can go fancy
public class lowordaccess
{
unsigned int m_source;
public:
void assign(unsigned int& source) { m_source = source; }
short& operator=(short& value) { ... set m_source }
operator short() { return m_source & 0xFF; }
}
and then
struct LowLevelNumber
{
LowLevelNumber() { loword.assign(number); }
unsigned int number;
lowordaccess loword;
}
var.loword = 1;
short n = var.loword;
The latter technique is a known property emulation in C++.
You could easily wrap that in a class and use get/set accessors.
Using a union for this is bad, because it is not portable w.r.t. endianness.
Use accessor functions and implement them with bit masks and shifts.