Just recently started C++ programming for micro-controllers, and I've ran into situations* where it would be convenient to have a non-static const field on a struct that is always guaranteed to have a fixed value (same for every instance of the sturct, ever).
Given a struct
struct S {
const uint8_t c; // Should always be 42
char v;
uint32_t arr[4];
}
I'd like c to be a constant value, and the same constant value every time. I would love to be able to use the convenience of brace initializer lists, for setting v and the members of arr like
S some_var = {'v', { 0, 1, 2, 3 } };
Since I'd like c to be a constant, I'm under the impression that I have to use an initializer list for setting c, such as S() : c(42) {}, which works just fine, as long as I don't try to also initialize arr, in which case I'm lost on how the list should look like. Is this doable using C++11? (Also interested in an answer if this is not doable in C++11, but in some newer standard.)
Example code:
#include <stdio.h>
#include <stdint.h>
struct S {
const uint8_t c; // Should always be 42 on every instance
// of the struct due to hardware shenanigance
// (i.e. this struct is the representation of a register value)
char v;
uint32_t arr[4];
// This allows using "S s1;"
S() : c(42), v('a'), arr{} {}
// This allows using "S s2 = { 'v', 0, 1, 2, 3 };" works but it's clumsy:
S(uint32_t v, uint32_t arr0, uint32_t arr1, uint32_t arr2, uint32_t arr3) :
c(42), v(v), arr{ arr0, arr1, arr2, arr3 } {}
// I would like to do something along the lines of "S s2 = { 'v', { 0, 1, 2, 3 } };":
// S(uint32_t v, uint32_t arr[4] /*?*/) :
// c(42), v(v), arr{/*?*/} {}
};
// Main just for the sake of completeness
int main() {
// Works just fine
S s1;
printf("s1.c = %u\n", s1.c); // 42
printf("s1.v = '%c'\n", s1.v); // a
printf("s1.arr[3] = %u\n", s1.arr[3]); // 0
// Initialiation like this works with the line:12 signature:
S s2 = { 'v', 0, 1, 2, 3 };
// I'd like to initialize like this:
// S s2 = { 'v', { 0, 1, 2, 3 } };
printf("s2.c = %u\n", s2.c); // 42
printf("s2.v = '%c'\n", s2.v); // v
printf("s2.arr[3] = %u\n", s2.arr[3]); // 3
return 0;
}
*Context on why I'd want to do this: This might seem like a weird thing to want, since if the value is always the same, why bother storing it? Well imagine that the struct in question is a bitfield which corresponds to the register of an IC with which the micro-controller communicates. These registers sometimes have "reserved" fields, and the datasheet specifies what value you must write into these fields. From a programmer's point of view, it would be convenient if I never had to deal with setting said bits manually.
C++11 gives you std::array which is like a raw array, but comes with none of the "negatives" (array decay, can't copy). Using that you can get exactly what you want like
struct S {
const uint8_t c = 42;
char v = 'a';
std::array<uint32_t, 4> arr{};
// This allows using "S s1;"
S() {}
S(uint32_t v, std::array<uint32_t, 4> arr) : v(v), arr{arr} {}
};
// Main just for the sake of completeness
int main() {
// Works just fine
S s1;
printf("s1.c = %u\n", s1.c); // 42
printf("s1.v = '%c'\n", s1.v); // a
printf("s1.arr[3] = %u\n", s1.arr[3]); // 0
S s2 = { 'v', { 0, 1, 2, 3 } };
printf("s2.c = %u\n", s2.c); // 42
printf("s2.v = '%c'\n", s2.v); // v
printf("s2.arr[3] = %u\n", s2.arr[3]); // 3
return 0;
}
which outputs
s1.c = 42
s1.v = 'a'
s1.arr[3] = 0
s2.c = 42
s2.v = 'v'
s2.arr[3] = 3
If you absoluytley have to have a raw array in S then your other option is to use a std::initializer_list in the constructor. That would look like
struct S {
const uint8_t c = 42;
char v = 'a';
uint32_t arr[4]{};
// This allows using "S s1;"
S() {}
S(uint32_t v, std::initializer_list<uint32_t> data) : v(v)
{
int i = 0;
for (auto e : data)
arr[i++] = e;
}
};
// Main just for the sake of completeness
int main() {
// Works just fine
S s1;
printf("s1.c = %u\n", s1.c); // 42
printf("s1.v = '%c'\n", s1.v); // a
printf("s1.arr[3] = %u\n", s1.arr[3]); // 0
S s2 = { 'v', { 0, 1, 2, 3 } };
printf("s2.c = %u\n", s2.c); // 42
printf("s2.v = '%c'\n", s2.v); // v
printf("s2.arr[3] = %u\n", s2.arr[3]); // 3
return 0;
}
And you get the same results as the code using std::array.
Related
I have vector of structs:
typedef struct
{
uint64_t id = 0;
std::string name;
std::vector<uint64_t> data;
} entry;
That I want to write to file:
FILE *testFile = nullptr;
testFile = fopen("test.b", "wb");
However the normal method for read/write
fwrite(vector.data(), sizeof vector[0], vector.size(), testFile);
fread(vector.data(), sizeof(entry), numberOfEntries, testFile);
does not work as the size of entry can vary wildly depending on the contents of
std::string name;
std::vector<uint64_t> data;
so I would like methods and pointers about how to do read/writing of this data to/from files.
When dealing with non-fixed size data it's important to keep track of the size somehow. You can simply specify the amount of fixed size elements or byte size of whole structure and calculate needed values when reading the struct. I'm in favour of the first one though it can sometimes make debugging a bit harder.
Here is an example how to make a flexible serialization system.
struct my_data
{
int a;
char c;
std::vector<other_data> data;
}
template<class T>
void serialize(const T& v, std::vector<std::byte>& out)
{
static_assert(false, "Unsupported type");
}
template<class T>
requires std::is_trivially_copy_constructible_v<T>
void serialize(const T& v, std::vector<std::byte>& out)
{
out.resize(std::size(out) + sizeof(T));
std::memcpy(std::data(out) + std::size(out) - sizeof(T), std::bit_cast<std::byte*>(&v), sizeof(T));
}
template<class T>
void serialize<std::vector<T>>(const std::vector<T>& v, std::vector<std::byte>& out)
{
serialize<size_t>(std::size(v), out); // add size
for(const auto& e : v)
serialize<T>(v, out);
}
template<>
void serialize<my_data>(const my_data& v, std::vector<std::byte>& out)
{
serialize(v.a, out);
serialize(v.c, out);
serialize(v.data, out);
}
// And likewise you would do for deserialize
int main()
{
std::vector<std::byte> data;
my_data a;
serialize(a, data);
// write vector of bytes to file
}
This is a tedious job and there are already libraries that do it for you like Google's Flatbuffers, Google's Protobuf or a single header BinaryLove3. Some of them work out of the box with aggregate types (meaning all member variables are public). Here is an example of BinaryLove3 in action.
#include <iostream>
#include <vector>
#include <string>
#include <cstdint>
#include <string>
#include <list>
#include "BinaryLove3.hpp"
struct foo
{
uint32_t v0 = 3;
uint32_t v1 = 2;
float_t v2 = 2.5f;
char v3 = 'c';
struct
{
std::vector<int> vec_of_trivial = { 1, 2, 3 };
std::vector<std::string> vec_of_nontrivial = { "I am a Fox!", "In a big Box!" };
std::string str = "Foxes can fly!";
std::list<int> non_random_access_container = { 3, 4, 5 };
} non_trivial;
struct
{
uint32_t v0 = 1;
uint32_t v1 = 2;
} trivial;
};
auto main() -> int32_t
{
foo out = { 4, 5, 6.7f, 'd', {{5, 4, 3, 2}, {"cc", "dd"}, "Fly me to the moon..." , {7, 8, 9}}, {3, 4} };
auto data = BinaryLove3::serialize(bobux);
foo in;
BinaryLove3::deserialize(data, in);
return int32_t(0);
}
The following code is returning the compilation error below. I'm stuck understanding how there are too many initializers. This code works using vector<X>. Does anyone know why the error is being reported and how to resolve? Thanks
#include <iostream>
#include <array>
using namespace std;
struct X {
int x, y;
};
int main(int argc, char *argv[])
{
array<X,2> a0 = {{0,1}, {2,3}};
for (auto& p : a0) {
cout << p.x << endl;
cout << p.y << endl;
}
return 0;
}
Compilation:
g++ -pedantic -Wall test116.cc && ./a.out
test116.cc: In function ‘int main(int, char**)’:
test116.cc:11:34: error: too many initializers for ‘std::array<X, 2>’
array<X,2> a0 = {{0,1}, {2,3}};
Try
array<X,2> a0 = {{{0,1}, {2,3}}};
Note the extra set of braces.
It seems a bit odd but it's this way because the only member of array is the actual array:
template <class T, size_t N>
class array {
T val[N];
// ...
};
The constructors are all implicitly defined so that array ends up being a trivially constructable type.
You may use one of the following initializations because std::array is an aggregate that contains another aggregate as its data member.
array<X,2> a0 = { { { 0, 1 }, { 2, 3 } } };
array<X,2> a0 = { 0, 1, 2, 3 };
array<X,2> a0 = { { 0, 1, 2, 3 } };
array<X,2> a0 = { { 0, 1, { 2, 3 } } };
array<X,2> a0 = { { { 0, 1 }, 2, 3 } };
I found that my problem is related to the version of g++,9.4.0 is ok and 5.4.0 not.version of g++.By the way, the version of g++ is associated with the system version generally.
Note that the problem can be reproduced by running the snippet below (I use wandbox with gcc 9.1)
So I have a std::array (size 2 for simplicity) of std::variant of two custom types (Normal and Special) Normal is specified as first type so upon class construction, the array is default-constructed with Normal objects. I change some internal data members of the first element of the array and print it out. Looks fine.
Now I want to set the second element of the array to Special object. I tried doing this both by assigning to a new value and using emplace according to this tutorial (https://www.bfilipek.com/2018/06/variant.html#changing-the-values)
However, when I try to change the internal data members of the second object (now typed Special) it seems like I'm not operating on the object in the original arrays. Print out results show default value of construction (0 in this case) I am new to using std::variant so I don't have a clue why it would be the case. How can I get the actual reference to the recently type-change variant object in my array?
#include <iostream>
#include <memory>
#include <cstring>
#include <array>
#include <variant>
struct Normal {
struct Header {
std::array<uint8_t, 2> reserved;
};
Normal() : frame{0}, payload{reinterpret_cast<uint8_t*>(frame + sizeof(Header))} {}
constexpr static auto LENGTH = 10;
uint8_t frame[LENGTH];
uint8_t* payload;
};
struct Special {
struct Header {
std::array<uint8_t, 3> reserved;
};
Special() : frame{0}, payload{reinterpret_cast<uint8_t*>(frame + sizeof(Header))} {}
constexpr static auto LENGTH = 11;
uint8_t frame[LENGTH];
uint8_t* payload;
};
std::array<std::variant<Normal, Special>, 2> handlers;
Normal* normal_handler;
Special* special_handler;
int main() {
auto& nh = std::get<Normal>(handlers[0]);
memset(nh.payload, 3, 3);
normal_handler = &nh;
handlers[1].emplace<1>(Special{});
auto& sh = std::get<Special>(handlers[1]);
memset(sh.payload, 4 ,4);
// memset(std::get<Special>(handlers[1]).payload, 4, 4);
special_handler = &sh;
for (int i = 0; i < 10; i++) {
// Expect 3 bytes from 3rd bytes = 3
std::cout << (int) normal_handler->frame[i] << " ";
}
std::cout << std::endl;
for (int i = 0; i < 11; i++) {
// Expect 4 bytes from 4th bytes = 4
std::cout << (int) special_handler->frame[i] << " ";
// std::cout << (int) std::get<Special>(handlers[1]).frame[i] << " ";
}
}
Your issue isn't related to std::variant, the following code shows the same behaviour:
#include <iostream>
#include <memory>
#include <cstring>
struct Special {
struct Header {
std::array<uint8_t, 3> reserved;
};
Special() : frame{0}, payload{reinterpret_cast<uint8_t*>(frame + sizeof(Header))} {}
constexpr static auto LENGTH = 11;
uint8_t frame[LENGTH];
uint8_t* payload;
};
int main() {
Special s1;
s1 = Special{};
memset(s1.payload, 4 ,4);
for (int i = 0; i < 11; i++) {
// Expect 4 bytes from 4th bytes = 4
std::cout << (int) s1.frame[i] << " ";
}
}
This line:
s1 = Special{};
Creates a temporary Special object and then assigns it to s1. The default copy and move constructors will set s1.payload to the value of payload in the temporary. Therefore s1.payload is a dangling pointer to frame in the temporary object and the rest of your code therefore has undefined behaviour.
The simplest fix is to change the payload member into a function:
#include <iostream>
#include <memory>
#include <cstring>
struct Special {
struct Header {
std::array<uint8_t, 3> reserved;
};
Special() : frame{0} {}
constexpr static auto LENGTH = 11;
uint8_t frame[LENGTH];
uint8_t* payload() { return &frame[sizeof(Header)]; }
};
int main() {
Special s1;
s1 = Special{};
memset(s1.payload(), 4 ,4);
for (int i = 0; i < 11; i++) {
// Expect 4 bytes from 4th bytes = 4
std::cout << (int) s1.frame[i] << " ";
}
}
I got this question from the cracking the coding interview book. I was able to write this method in python and java. But when I tried to write it in c++, the compiler starts yelling at me. I think the problem is that in the main function, I had a array instantiated by a template but the function is taking in a primitive array. How should I instantiate a primitive array?
// Given a sorted array of positive integers with an empty spot (zero) at the
// end, insert an element in sorted order.
bool sortSortedArray(size_t arrInt[], size_t x)
{
size_t indexArr{0};
size_t insertNum{x};
while (x != 0) {
if (x < arrInt[indexArr]) {
size_t swapVal = arrInt[indexArr];
arrInt[indexArr];
insertNum = swapVal;
++indexArr;
}
}
return true;
}
// Test the sortSortedArray function.
int main()
{
array<size_t, 5> testArr{1, 4, 5, 8, 0};
if (sortSortedArray(testArr, 3)) {
return 0;
}
}
Either make testArr a primitive array:
int testArr[] = {1, 4, 5, 8, 0};
or call data() to get the underlying array:
if (sortSortedArray(testArr.data(), 3)) {
#include <cstddef>
#include <array>
#include <iostream>
// this is a function template because each std::array<> parameter set creates a
// a type and we need a function for each type (we know std::size_t is the element
// type so this is only parameterized on the size)
template<size_t ArrSize>
void sortSortedArray(
std::array<std::size_t, ArrSize>& arr,
const std::size_t insertNum)
{
// last position is known to be "empty"
arr[arr.size() - 1] = insertNum;
// swap value in last position leftwards until value to the left is less
auto pos = arr.size() - 1;
if (pos == 0)
return;
while (arr[pos - 1] > arr[pos])
{
const auto tmp = arr[pos - 1];
arr[pos - 1] = arr[pos];
arr[pos] = tmp;
--pos;
if (pos == 0)
return;
}
}
template<typename T, size_t N>
void printArray(const std::array<T, N>& r)
{
for (const auto i : r)
{
std::cout << i << " ";
}
std::cout << '\n';
}
int main()
{
std::array<std::size_t, 5> testArr{{1, 4, 5, 8, 0}};
printArray(testArr);
sortSortedArray(testArr, 3);
printArray(testArr);
}
How can I find the index of the maximum value in a VexCL vector? I can find the maximum value:
int h[] = {3, 2, 1, 5, 4};
vex::vector<int> d(ctx, 5);
vex::copy(h, d);
vex::Reductor<int, vex::MAX> max(ctx.queue());
int m = max(d);
Which gives m = 5 but is there a way to find the index of the maximum value, ind = 3?
You will need to
encode both vector value and vector position in a vexcl expression, and
create custom functor for vex::Reductor that would reduce the above expression based on its first component.
Here is the working code:
#include <iostream>
#include <vector>
#include <vexcl/vexcl.hpp>
// This function converts two integers to cl_int2
VEX_FUNCTION(cl_int2, make_int2, (int, x)(int, y),
int2 v = {x, y};
return v;
);
// This struct compares OpenCL vector types by the first component.
struct MAX0 {
template <class Tn>
struct impl {
typedef typename vex::cl_scalar_of<Tn>::type T;
// Initial value.
static Tn initial() {
Tn v;
if (std::is_unsigned<T>::value)
v.s[0] = static_cast<T>(0);
else
v.s[0] = -std::numeric_limits<T>::max();
return v;
}
// Device-side function call operator
struct device : vex::UserFunction<device, Tn(Tn, Tn)> {
static std::string name() { return "MAX_" + vex::type_name<Tn>(); }
static std::string body() { return "return prm1.x > prm2.x ? prm1 : prm2;"; }
};
// Host-side function call operator
Tn operator()(Tn a, Tn b) const {
return a.s[0] > b.s[0] ? a : b;
}
};
};
int main(int argc, char *argv[]) {
vex::Context ctx( vex::Filter::Env );
std::vector<int> h = {3, 2, 1, 5, 4};
vex::vector<int> d(ctx, h);
// Create reductor based on MAX0 operation,
// then reduce an expression that encodes both value and position of a
// vector element:
vex::Reductor<cl_int2, MAX0> max(ctx);
cl_int2 m = max(make_int2(d, vex::element_index()));
std::cout << "max value of " << m.s[0] << " at position " << m.s[1] << std::endl;
}
This outputs
max value of 5 at position 3