Reading a struct from a read only memory
The most practical approach would be to lose the const
. By a strict reading of the standard, gCalibrationData
shouldn't be allowed to be const, since writing to a const
object -- regardless of who does it -- leads to undefined behavior.
Failing that, though, just define it as extern const
(and, if necessary to placate the linker, put the non-extern
definition in its own translation unit. That will get you your const-correctness checking, allow the compiler to, e.g., do hoisting optimizations based on the initial values of the calibration data, while still preventing it from making any specific assumptions about those values at compile time.
One solution could be to declare a buffer in a separate source file, to inform the linker of size of data_block
and then define gCalibrationData
to be a symbol whose value is the begining of this buffer:
data_block.cpp:
//no initialization performed here, just used to
//transmit to the linker the information of the size
//and alignment of data_block
extern "C"{//simpler name mangling
[[gnu::section(".caldata")]] volatile
aligned_storage<sizeof(data_block),alignof(data_block)> datablock_buffer;
}
//then we specify that gCalibrationData refers to this buffer
extern const volatile data_block
gCalibrationData [[gnu::alias("datablock_buffer")]];
Alternatively the definition of gCalibrationData
symbol can be done via a linker script:
SECTIONS{
.caldata : {
gCalibrationData = . ;
data_block.o(.caldata)
}
}
gCalibrationData
is an alias to an data_block_buffer
. This will not cause undefined behavior because such aliasing is permitted by the language: data_block_buffer
provides storage for gCalibrationData
.
Semanticaly, the extern
specifier is used to say that this declaration is not a definition of the value of gCalibrationData
. Nevertheless the alias
attribute is a definition of the symbol for the linker.
data_block.hpp
extern const volatile data_block gCalibrationData;
//and copy must be enabled for volatile:
struct data_block{
/*...*/
data_block(const data_block&) =default;
data_block& operator=(const data_block&) =default;
data_block(const volatile data_block& other){
//the const cast means: you are responsible not to
//perform this operation while performing a rom update.
memcpy(this,const_cast<const data_block*>(&other);
}
data_block& operator=(const volatile data_block& other){
memmove(this,const_cast<const data_block*>(&other);
//or memcpy if you are sure a self assignment will never happen.
return *this;
}
};