Hello,
so this question is in some variants already present and answered partly but I can't find my special case covered and I'm struggling for a solution.
So we have an embedded system and I created a NorFlash-Driver-design which creates templated NorFlash-instances based on the read id of the connected flash. Based on this id we now set all the needed commands, timings, sizes etc. for exactly this flash. Those are part of a const or also constexpr struct. The declaration is in a separate header-file. the definitions for each flash in their respective header-file. The NorFlash is also a template-header-file. To have some buffer but also to reduce the use of dynamic memory the NorFlash has an member-array with the size of the Flash-defined blockSize. A wish would be to pass this struct with the parameters via a template parameter so the size of the array is known at compile-time.
I will write down what I have done and what problems occured. it's mainly a warning that is described here but in a slightly different way: Why do I get the "child has a base whose type uses the anonymous namespace" warning here
So let's assume a header with the struct that is used (reduced variables just to show for example). Flashparameters.hpp
namespace Flash
{
struct FlashCmds
{
const uint32_t WRITE_ENABLE;
const uint32_t WRITE_DISABLE;
}
struct FlashParameters
{
const uint32_t size;
const uint32_t blockSize;
const FlashCmds flashCmds;
}
}
Then the header with the definition of the parameters for one example-flash. ExampleFlashParameters.hpp
namespace Flash
{
constexpr FlashParameters exampleFlashParameters =
{
(32 * 1024 * 1024), // size
(128 * 1024), // block-size
{
0x06,
0x04
}
}
Then there is the NorFlash.hpp (not showing the interface as it is irrelevant):
namespace Flash
{
template <const FlashParameters& flashParameters> class NorFlash : public INorFlash
{
public:
explicit NorFlash(INorFlashDriver& flashDriver) : norFlashDriver_(flashDriver)
const FlashParameters& getFlashParameters() override
{
return flashParameters;
}
private:
/** Instance of used flash */
INorFlashDriver& norFlashDriver_;
/** array where block data is temporary stored */
uint8_t blockSave_[flashParameters.blockSize] = { 0 };
}
And the implementation for the concrete flash (so the user does not need to manually pass the data) ExampleFlash.hpp
namespace Flash
{
class ExampleFlash: public NorFlash<exampleFlashParameters >
{
public:
using NorFlash::NorFlash;
}
}
So the thing is, it did compile without a warning with a tiarmclang with C++14 (LTS2.1.3, clang 14.0.6), with mingw C++14 (GNU 9.3.0) with vanilla clang C++14 (clang 15.0.4) but not with vanilla GCC C++14 (GNU 9.4.0). Our build-pipeline uses different compilers and only with the gcc this popped up. I'm a bit suspicious about the correctnes to compile without a warning here, since the struct is passed by reference and a value of it is taken for an array. I would've expected that to be an VLA. But this does work at compile-time?
I have a full project example here: https://godbolt.org/z/eW7aPb4Ge you can change the compiler and see with clang this warning does not pop up
The warning I get is:
warning: Flash::ExampleFlash’ has a base ‘Flash::NorFlash<Flash::exampleFlashParameters >’ whose type uses the anonymous namespace [-Wsubobject-linkage] 26 | class ExampleFlash: public NorFlash
which is for every source-file that includes that header via a header-chain. extern const is no solution. Here the VLA-error for struct members pops up.
inline constexpr is first available in C++17.
Also passing all parameters per template parameter would be a real pain. Because this error pops up any time as soon as I pass a struct by reference as a template-parameter.
I know C++20 would've let me do this directly by value, but unfortunately we are bound to C++14.
Is this solveable without a bloated template-interface? Well I can hide it without problems behind the defined-Driver-constructor, but the Flash params have like 40 lines of code for their definition.
Thanks!
instead of reference, you might have type which has a getter to return that reference:
template <typename T>
class NorFlash : public INorFlash
{
public:
NorFlash() = default;
const FlashParameters& getFlashParameters() override { return T::flashParameters(); }
private:
/** array where block data is temporary stored */
uint8_t blockSave_[T::flashParameters().blockSize] = { 0 };
};
struct FlashParametersGetter
{
static constexpr const FlashParameters& flashParameters() { return exampleFlashParameters; }
};
class ExampleFlash: public NorFlash<FlashParametersGetter >
{
public:
using NorFlash::NorFlash;
void write()
{
printf("block-size is %d", FlashParametersGetter::flashParameters().blockSize);
}
};