-Wclass-memaccess warning with boost::endian and gcc - c++

I'm getting a -Wclass-memaccess with gcc >= 8 and I would like to know if I can safely ignore the warning.
Test case:
#include <array>
#include <boost/endian/buffers.hpp>
int main()
{
static_assert(std::is_trivial<boost::endian::big_uint16_buf_t>::value);
static_assert(std::is_trivially_copyable<boost::endian::big_uint16_buf_t>::value);
// No warning when buffer is char*
{
std::array<char, 2> buffer{0x0, 0x1};
boost::endian::big_uint16_buf_t v;
std::memcpy(&v, buffer.data(), sizeof(v));
}
// -Wclass-memaccess when buffer is std::byte*
{
std::array<std::byte, 2> buffer{std::byte{0x0}, std::byte{0x1}};
boost::endian::big_uint16_buf_t v;
std::memcpy(&v, buffer.data(), sizeof(v));
}
}
Try it on wandbox
Background: This type of code is used to decode binary network traffic and memcpy is used to be safe with strict aliasing rule.
Before using that pattern more extensively on a bigger codebase, I would like to be sure it's not flawed.
I have the impression that it's safe to copy into a boost::endian buffer. Strangely there is no warning when the buffer is a pointer to char, but only when the buffer is a pointer to std::byte
Full warning message is:
prog.cc: In function 'int main()':
prog.cc:23:49: warning: 'void* memcpy(void*, const void*, size_t)' copying an object of type 'boost::endian::big_uint16_buf_t' {aka 'class boost::endian::endian_buffer<boost::endian::order::big, short unsigned int, 16>'} with 'protected' member 'boost::endian::endian_buffer<boost::endian::order::big, short unsigned int, 16>::m_value' from an array of 'std::array<std::byte, 6>::value_type' {aka 'enum class std::byte'}; use assignment or copy-initialization instead [-Wclass-memaccess]
23 | std::memcpy(&v, buffer.data(), sizeof(v));
| ^
In file included from prog.cc:2:
/opt/wandbox/boost-1.70.0/gcc-9.1.0/include/boost/endian/buffers.hpp:375:11: note: 'boost::endian::big_uint16_buf_t' {aka 'class boost::endian::endian_buffer<boost::endian::order::big, short unsigned int, 16>'} declared here
375 | class endian_buffer< order::big, T, n_bits, align::no >
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Related

callback in MCS6502 functions will not compile in arduino on esp32 but will compile in codeblocks c file

I know this is a noob question bit I am not familiar with c++ .
My understanding is that arduino code compiles in c++ and this is what causes the problem in compiling.
The code below will compile fine in codeblocks and will run correctly , however if I use the same code without any modifications then it will produce an error at compile time.
heres is the .ino file
#include "MCS6502.h"
int8_t ram [65535];
//////CALL BACK FUNCTION ///////////////////////////////////////////////
uint8_t readBytesFunction(uint16_t add) {
uint8_t tc = 5;
tc = ram[add];
return tc;
}
//////CALL BACK FUNCTION ///////////////////////////////////////////////
void writeBytesFunction(uint16_t add,uint8_t bb) {
}
void setup()
{
Serial.begin(115200);
Serial.println();
///CODE BELOW WILL COMPILE AND RUN IN CODEBLOCKS BUT WILL NOT COMPILE IN ARDUINO
/////////////////////////////////////////////////////////////////////
MCS6502ExecutionContext context;
MCS6502Init(&context, readBytesFunction, writeBytesFunction, NULL); // Final param is optional conte>
MCS6502Reset(&context);
MCS6502Tick(&context); //use timings
MCS6502ExecNext(&context); //as fast as possible
}
these are the errors after compiling in arduino for esp32
MCS6502.ino: In function 'void setup()':
wahid_MCS6502:22:27: error: invalid conversion from 'uint8_t (*)(uint16_t)' {aka 'unsigned char (*)(short unsigned int)'} to 'MCS6502DataReadByteFunction' {aka 'unsigned char (*)(short unsigned int, void*)'} [-fpermissive]
MCS6502Init(&context, readBytesFunction, writeBytesFunction, NULL); // Final param is optional conte>
^~~~~~~~~~~~~~~~~
MCS6502.h:91:33: note: initializing argument 2 of 'void MCS6502Init(MCS6502ExecutionContext*, MCS6502DataReadByteFunction, MCS6502DataWriteByteFunction, void*)'
MCS6502DataReadByteFunction readByteFn,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
MCS6502:22:46: error: invalid conversion from 'void (*)(uint16_t, uint8_t)' {aka 'void (*)(short unsigned int, unsigned char)'} to 'MCS6502DataWriteByteFunction' {aka 'void (*)(short unsigned int, unsigned char, void*)'} [-fpermissive]
MCS6502Init(&context, readBytesFunction, writeBytesFunction, NULL); // Final param is optional conte>
^~~~~~~~~~~~~~~~~~
MCS6502.h:92:34: note: initializing argument 3 of 'void MCS6502Init(MCS6502ExecutionContext*, MCS6502DataReadByteFunction, MCS6502DataWriteByteFunction, void*)'
MCS6502DataWriteByteFunction writeByteFn,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
exit status 1
invalid conversion from 'uint8_t (*)(uint16_t)' {aka 'unsigned char (*)(short unsigned int)'} to 'MCS6502DataReadByteFunction' {aka 'unsigned char (*)(short unsigned int, void*)'} [-fpermissive]
what do i need to do it compiles in arduino ??
apologies if some details are missing from this question but its my 1st ever time asking a question here.
many thanks
I'm guessing that you are using this library?
Looking at the signatures of the callback functions both require a final void* parameter which your callbacks are missing.
You need:
uint8_t readBytesFunction(uint16_t add, void*) {
uint8_t tc = 5;
tc = ram[add];
return tc;
}
void writeBytesFunction(uint16_t add,uint8_t bb, void*) {
}

inconsistent behavior in regard to passing a pointer to a C++17 Standard Variant to function expecting const reference [duplicate]

This question already has answers here:
What is the best way to disable implicit conversion from pointer types to bool when constructing an std::variant?
(2 answers)
Why is there an implicit type conversion from pointers to bool in C++?
(3 answers)
Closed 1 year ago.
I'm seeing some odd behavior in regard to passing a pointer into a function that expects a constant reference. Obviously C++ expects me to de reference the pointer before I pass it into the function expecting the reference. I'm looking for an error or warning but I'm not getting one UNLESS a certain condition is met that doesn't make any sense to me.
The type of the pointer and reference is a C++ 17 Standard Variant (below). If my C++17 variant includes the boolean in the template (as below) then GCC8.3 compiles the code just fine. But I have unexpected runtime results.
If I remove the boolean from the standard variant template, then the code does not compile as I expect. Why the difference?
#include <iostream>
#include <variant>
#include <stdint.h>
typedef std::variant <
std::monostate,
int8_t,
int16_t,
int32_t,
int64_t,
uint8_t,
uint16_t,
uint32_t,
uint64_t,
float,
double
,bool //If bool is in the variant, the compiler compiles the code without error and understandably has UB. Why isn't the lack of de-reference bug in DoThing not caught when that is the case?
> StdVARIANT;
//if 'bool' is commented out of the variant the error (expected) is:
//invalid initialization of reference of type ‘const StdVARIANT&’
static size_t GetIndexOfVariant(const StdVARIANT& RefToVariant);
static size_t DoThing(StdVARIANT* PtrToVariant);
static size_t DoThing(StdVARIANT* PtrToVariant){
return GetIndexOfVariant(PtrToVariant); //this is a bug-PtrToVariant should be de referenced!
}
static size_t GetIndexOfVariant(const StdVARIANT& RefToVariant){
size_t Index = RefToVariant.index();
return Index;
}
int main()
{
StdVARIANT* myvar = new StdVARIANT();
*myvar = (int32_t)1;
std::cout<<"long: "<<std::get<int32_t>(*myvar)<<" "<<sizeof(std::get<int32_t>(*myvar))<<std::endl;
*myvar = (double)2;
std::cout<<"double: "<<std::get<double>(*myvar)<<" "<<sizeof(std::get<double>(*myvar))<<std::endl;
std::cout<< myvar->index() << " " << DoThing(myvar)<<std::endl; //when 'bool' is in the std::variant, these two calls return different results (UB)
delete myvar;
return 0;
}
Compile and run as the code block above:
~/gcc-test$ g++ -std=c++17 -Wall -Wextra -pedantic main.cpp
~/gcc-test$ ./a.out
long: 1 4
double: 2 8
10 11
Comment 'bool' out of StdVARIANT, and:
~/gcc-test$ g++ -std=c++17 -Wall -Wextra -pedantic main.cpp
main.cpp: In function ‘size_t DoThing(StdVARIANT*)’:
main.cpp:27:30: error: invalid initialization of reference of type ‘const StdVARIANT&’ {aka ‘const std::variant<std::monostate, signed char, short int, int, long int, unsigned char, short unsigned int, unsigned int, long unsigned int, float, double>&’} from expression of type ‘StdVARIANT*’ {aka ‘std::variant<std::monostate, signed char, short int, int, long int, unsigned char, short unsigned int, unsigned int, long unsigned int, float, double>*’}
return GetIndexOfVariant(PtrToVariant); //this is a bug-PtrToVariant should be de referenced!
^~~~~~~~~~~~
main.cpp:23:15: note: in passing argument 1 of ‘size_t GetIndexOfVariant(const StdVARIANT&)’
static size_t GetIndexOfVariant(const StdVARIANT& RefToVariant);
^~~~~~~~~~~~~~~~~
Why don't I always get the error instead of just when bool is commented out?
To reproduce the problem, you can simplify your code as follows
#include <iostream>
#include <variant>
#include <stdint.h>
typedef std::variant <
std::monostate,
int8_t,
int16_t,
int32_t,
int64_t,
uint8_t,
uint16_t,
uint32_t,
uint64_t,
float,
double
,bool //If bool is in the variant, the compiler compiles the code without error and understandably has UB. Why isn't the lack of de-reference bug in DoThing not caught when that is the case?
> StdVARIANT;
int main()
{
StdVARIANT * pVar = new StdVARIANT();
StdVARIANT var = pVar;
}
The point is that a StdVARIANT * can't be converted to any of the StdVARIANT alternative except for bool.
So if StdVARIANT contains bool,
StdVARIANT var = pVar;
var is initialized as containing a bool value (a true, given that pVar isn't nullptr).
If StdVARIANT doesn't contains bool, the var initialization fails.
The same inside your DoThing() function: when you call
GetIndexOfVariant(PtrToVariant);
a temporary StdVARIANT is created (or not, if bool isn't available) and the constant reference is passed to GetIndexOfVariant().

How to read bytes from file using std::ifstream to std::array?

The below program tries to open a rom file and loads it to std::array.
#include <array>
#include <fstream>
#include <iostream>
const std::string ROM_FILE = "cpu_instrs.gb";
int main()
{
std::array<uint8_t, 0x8000> m_Cartridge;
std::ifstream istream(ROM_FILE, std::ios::in | std::ios::binary);
istream.seekg(0, std::ios::end);
size_t length = istream.tellg();
istream.seekg(0, std::ios::beg);
if (length > m_Cartridge.size())
{
length = m_Cartridge.size();
}
istream.read(m_Cartridge.data(), length);
for (const uint8_t& b : m_Cartridge)
{
std::cout << b << std::endl;
}
return 0;
}
When I ran the program above using g++, I got the following error
test.cpp: In function 'int main()':
test.cpp:21:34: error: invalid conversion from 'std::array<unsigned char, 32768>::pointer' {aka 'unsigned char*'} to 'std::basic_istream<char>::char_type*' {aka 'char*'} [-fpermissive]
21 | istream.read(m_Cartridge.data(), length);
| ~~~~~~~~~~~~~~~~^~
| |
| std::array<unsigned char, 32768>::pointer {aka unsigned char*}
In file included from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/9.2.0/include/c++/fstream:38,
from test.cpp:2:
C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/9.2.0/include/c++/istream:486:23: note: initializing argument 1 of 'std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::read(std::basic_istream<_CharT, _Traits>::char_type*, std::streamsize) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_istream<_CharT, _Traits>::char_type = char; std::streamsize = long long int]'
486 | read(char_type* __s, streamsize __n);
| ~~~~~~~~~~~^~~
It seems to me that std::istream only works with char not unsigned char. When I change type from uint8_t to char. The program compiles and ran without problems. Is there any way to make std::istream work with uint8_t?
std::istream is written in terms of char_type being simply char, and similarly, std::istream::read is the same.
The conventional approach would be to reinterpret_cast<char*> the pointer that you are reading to:
istream.read(reinterpret_cast<char*>(m_Cartridge.data()), length);
Although using reinterpret_cast is often frowned upon in many cases, this is one such case where it's both necessary and also safe to perform, since pointers to char are capable of aliasing any object with the same reachability as the parent object (in this case, the same reachability as the entire array object).
In fact, if you check the std::istream::read page on cppreference, even it uses a similar example to read the raw binary:
std::uint32_t n;
if(raw.read(reinterpret_cast<char*>(&n), sizeof n)) { ... }
That said, there is technically an alternative way you can do this -- though it's not recommended. The various stream classes are templated on the character type (basic_*stream<...>), of which std::ifstream is an alias of. Technically you could try to instantiate std::basic_ifstream<unsigned char> and read from this without a cast. However, in doing so, you may need to implement a custom std::char_traits -- as this is not required by the C++ standard to work with signed or unsigned character types (e.g. std::char_traits<unsigned char> is not guaranteed to be valid)

Arduino, understanding invalid conversion warning, unsigned char to char*

Edit:
Resolved, can't accept my answer for two days
I'm using PlatformIO to program a Arduino Nano to write a message to an OLED display.
I'm calling a function printWeight:
void printWeight(
float weight,
uint8_t scale=3,
uint8_t numbd=3,
uint8_t numad=2,
bool kg=false,
const char* description_text_line="Weight:",
uint8_t width_per_character=5+1,
uint8_t height_per_line=7+3
){
...
The function is centering the text by calculating the leading space leading_space and calls itself with a smaller scale (scale-1) if the message doesn't fit on the screen:
if(leading_space < 0){
printWeight(weight, scale-1, numbd, numad, description_text_line,
width_per_character, height_per_line);
return;
}
The code compiles and runs fine. However I'm getting the following warning that I don't know how to resolve:
src/main.cpp: In function 'void printWeight(float, uint8_t, uint8_t, uint8_t, bool, const char*, uint8_t, uint8_t)':
src/main.cpp:138:53: warning: invalid conversion from 'uint8_t {aka unsigned char}' to 'const char*' [-fpermissive]
width_per_character, height_per_line);
^
src/main.cpp:93:6: note: initializing argument 6 of 'void printWeight(float, uint8_t, uint8_t, uint8_t, bool, const char*, uint8_t, uint8_t)'
void printWeight(
^
How do I get rid of this warning?
Missing parameter...
Thanks to the ones commenting.

c++: error: cannot convert ‘ns3::TracedValue<ns3::SequenceNumber<unsigned int, int> >’ to ‘uint32_t {aka unsigned int}

I am trying to cast TracedValue<uint32_t> m_bytesInFlight to uint32_t but I get the following error
error: cannot convert ‘ns3::TracedValue<ns3::SequenceNumber<unsigned int, int> >’ to ‘uint32_t {aka unsigned int}
Function prototype and variable declarations are
uint32_t UnAckDCount (void) const;
TracedValue<uint32_t> m_bytesInFlight {0}; //!< Bytes in flight
Here i am calling the function
uint32_t
TcpSocketBase::UnAckDCount () const
{
return m_tcb->m_highTxMark - (uint32_t) m_tcb->m_bytesInFlight;
}
Please suggest some method so that I can execute the return statement to get the result. Thanks in advance
Changing m_tcb->m_highTxMark to m_tcb->m_highTxMark.Get().GetValue() should work.
Seeing the compiler error, it's easy to figure out that variable m_highTxMark is of type ns3::TracedValue<ns3::SequenceNumber<unsigned int, int> >. I checked the documentation of ns3::TracedValue and ns3::SequenceNumber, they both have getter functions Get() and GetValue() respectively.
See:
https://www.nsnam.org/doxygen/classns3_1_1_traced_value.html
https://www.nsnam.org/doxygen/classns3_1_1_sequence_number.html