Consider the following C99 structure, ending with a flexible array member:
struct hdr
{
size_t len;
size_t free;
char buf[];
};
len
, for example, gets accessed with an inline function (to be put into a header file) like this, having buf
as its argument:
static inline size_t slen(const char *s)
{
struct hdr *h = (struct hdr*)(s - (int)offsetof(struct hdr, buf));
return h->len;
}
This is part of a library, to be compiled with a C compiler. However, I would like to access this library from C++; this essentially means that the corresponding header file (with proper extern "C" {...}
guard) must be valid C++ code. A possible solution is to define the slen
function in the source code body, avoiding inline code completely, but this is not optimal.
My idea is to define a dummy C++ structure that is valid, and that I can map somehow to hdr
, e.g.
struct cpp_hdr
{
size_t len;
size_t free;
char buf[1];
}
Note that I only want to get correct (negative) offset values for len
and free
; no access to buf
is intended.
Now my question: Is there any guarantee that
static inline size_t slen(const char *s)
{
struct cpp_hdr *h = (struct cpp_hdr*)(s - (int)offsetof(struct cpp_hdr, buf));
return h->len;
}
works, giving the same result?
In the formal nothing is guaranteed, because C++ doesn't support flexible arrays: there is no such thing, no such syntax.
In practice compilers don't do things for no direct reason. So there won't be any willy-nilly padding introduced. However, just to make that abundantly clear I would use an array size of e.g. 666 instead of 1, which works better in the more general case (e.g. a small array of 1 char
might be moved into some otherwise padding area in some other struct). As a benefit, clever code for allocation won't look simple any more. So that that has to be done properly.
All this said, it sure sounds like a 16-bit Windows BSTR
, except a BSTR
doesn't have that gap between the length and the string data. Consider whether this library is just someone's for no good reason reinvention of the wheel. If so, I suggest using an original wheel instead.
Assuming library was compiled by a different compiler, it is not guaranteed that offset would be same. Practically, it should work most of the time.
If you are working on a specific compiler, you can use debugger and tune your offset calculation for that compiler and make an assertion in beginning to ensure your assumption (tuning) still holds true if later you decide to move to a different compiler or newer version.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With