I would like to create objects having a variable-length array of elements, and have them be compatible in a base/derived-class sense. In C, one could put an indeterminate array at the end of a struct
and then just malloc the object to contain the full array:
struct foo {
int n;
double x[];
} ;
struct foo *foo1 = (foo *)malloc( sizeof( foo ) sizeof( double[4] ) );
struct foo *foo2 = (foo *)malloc( sizeof( foo ) sizeof( double[100] ) );
In c , it seems like you could do:
template <unsigned I>
class foo {
public:
int n;
double x[I];
} ;
but:
auto foo1 = new foo<4>( );
auto foo2 = new foo<100>( );
if (foo1 == foo2) cerr << "incompatible pointers";
You can do it with a common base class, but is that necessary? I just want to use foo1
and foo2
, where each object knows the length of its array.
My application is for an ESP32 microcontroller running FreeRTOS. It has limited, non-virtual RAM and a slightly complicated allocation system because of differing capabilities of various chunks of memory (some is slower, some can't contain executable code, some can't be accessed by DMA, etc.) So allocating multiple chunks for pieces of an object (for example, by using std::vector
for the array of double
at the end) becomes complicated.
I know the length of the double
array at object construction time, but I would like the header and the array to be in a single allocated block of memory (so it can have the characteristics I need for it to have later).
The C-style way of doing it would be fine, but it would be nice to have C features like iteration over the array (for various objects, which will each have different numbers of double
s). Plus, a native C solution would allow me to have objects in the x[]
array, instead of fooling around with placement new
in raw allocated memory. So, for example:
auto a[] = { new foo<5>( ), new foo<10>( ), new foo<15>( ) };
for (auto i : a)
for (auto j : i.x)
cout << log10( j ); // prints 40 logs of doubles
(I expect that's got C syntax errors, but hopefully it communicates the idea. I can figure out the syntax for that, if I could get all the foo
s into a common container.)
CodePudding user response:
As a low-level C developer, I understand exactly what you need, and sadly, there is no replacement for flexible array members in standard C , with or without templates. You have to keep using flexible array members via compiler extensions.
They are not included in the language since in their current form, they are essentially a hack. They don't do well with inheritance or composition.
The problem with templates is that, the flexible array version has a common type of arrays of all sizes. That means you can place them in arrays, have non-template functions take them as parameters etc:
foo* make_foo(int n);
foo* foos[] = { make_foo(1); make_foo(2); make_foo(3); }; // ok
void take_foo(foo*);
With the template version, types foo<1>
and foo<2>
are completely unrelated, so you cannot put them in arrays or have non-template functions that take them:
template <int N>
foo<N>* make_foo();
auto foos[] = { make_foo<1>(), make_foo<2>(), make_foo<3>() }; // ill-formed
template <int N>
void take_foo(foo<N>*);
std::array
won't help in this discussion, and inheriting from one will still have the problem of having unrelated types.
However, since this is still C (albeit non-standard), you can at least have some additional niceties:
tempate <class T>
struct flex_array {
int n;
T data[];
T* begin() { return &data[0]; }
T* end() { return begin() n; }
};
void iterate(flex_array<double>& f) {
for (double j : f) {
cout << log10(j); // print however many doubles are in f
}
}