Paavo Helde <
ees...@osa.pri.ee> wrote:
> If by that you mean that dynamic allocations are slow, then you can
> create also larger opaque types in C without any dynamic allocations:
Of course it depends on what exactly the struct is for, and how it's used.
For example, if it's a large struct which basically contains nothing the
programmer may be directly interested in, and which is ostensibly
instantiated only relatively rarely and infrequently, then it's not
wrong to use this idiom per se. (A lot of C libraries do this, such
as libpng, libz, etc, and that's ok, because those structs are
usually no instantiated in the millions nor accessed in tight
inner loops requiring maximum speed.)
However, when it comes to small structs that are instantiated in
the millions and which should be as efficient as possible, this
idiom would completely kill the performance. Not only is instantiating
them slow, but also handling them is very slow as well (in comparison
to the structs being "public" and directly accessed.)
This is especially so in number-crunching applications (which things
like image manipulation etc. tend to be in practice). Not only would
this idiom consume significantly more RAM than necessary, and not
only would instantiating the objects be slower, but accessing them
would be a lot slower as well. (Modern compilers are relatively good
at autovectorizing linear accesses to values in an array. However,
if these accesses are done via non-inline functions, ie. resulting
in actual function calls, that pretty much kills all these
autovectorization optimizations.)
> struct item_tag {
> // use a type guaranteeing proper alignment,
> // choose big enough N to cover the real struct size.
> uint64_t opaque[N];
> };
> typedef struct item_tag item_t;
>
> void InitItem(item_t* it) {
> // cast it to real struct type and do things.
> // ...
> }
>
> // Client C code example:
>
> item_t x;
> InitItem(&x);
> // ...
> DestroyItem(&x);
>
>
> This way one could also support e.g. SSO strings, avoiding both
> arbitrary string length limits and excessive dynamic allocation
> overheads. Been there, done that.
If such a struct is intended to be as efficient as possible, then that
idiom might be acceptable, assuming that all the accessor functions are
inline.