Reputation: 1748
Basic problem
I'm in a tricky situation that requires taking a pointer to a struct mainset
and turning this into a pointer to a struct subset
, whose fields are a contiguous subset of the fields of mainset
, starting from the first. Is such a thing possible, with well-defined behavior? I realize that this is a pretty terrible thing to do, but I have good and frustrating reasons to do it [explained at the bottom for patient readers].
My attempt an an implementation seems to work, on OS X with the clang compiler:
#include <iostream>
struct mainset {
size_t size;
uint32_t reflex_size;
};
struct subset {
size_t size;
};
using namespace std;
int main(int argc, char *argv[]) {
mainset test = {1, 1};
subset* stest = reinterpret_cast<subset*>(&test);
std::cout << stest->size << std::endl;
}
The output is indeed 1, as I expect. However, I wonder: am I just getting lucky with a particular compiler and a simple case (in reality my structs are more complicated), or will this work in general?
Also, a follow-up question: for other annoying reasons, I worry that I might need to make my larger struct
struct mainset {
uint32_t reflex_size;
size_t size;
};
instead, with the extra field coming at the front. Could my implementation be extended to work in this case? I tried replacing &test
with &test+sizeof(test.reflex_size)
but this didn't work; the output of the cout
statement was 0.
Explanation of why I have to do this
My project uses the GSL library for linear algebra. This library makes use of structs of the form
struct gsl_block {
size_t size;
double* data;
}
and similar structs like gsl_vector
and gsl_matrix
. So, I've used these structs as members of my C++ classes; no problem. A recently demanded feature for my project, however, is to enable reflection to my classes with the Reflex tool, part of the ROOT ecosystem. To enable reflection for a struct like this in Reflex, I must add an annotation like
struct gsl_block {
size_t size;
double* data; //[size]
}
This annotation tells Reflex that that the length of the array is provided by the field size
of the same struct. Normally that would be that, but Reflex and ROOT have a very unfortunate limitation: the length field must be 32 bit. Having been informed that this limitation won't be fixed anytime soon, and not having the time/resources to fix it myself, I'm looking for workarounds. My idea is to somehow embed a struct bit-compatible with gsl_block
within a larger struct:
struct extended_gsl_block {
size_t size;
double* data; //[reflex_size]
uint32_t reflex_size;
}
and similar things for gsl_vector
and gsl_matrix
; I can ensure that reflex_size
and size
are always equal (neither is ever bigger than ~50) and Reflex will be able to parse this header correctly (I hope; if reflex_size is required to precede data as a field something more difficult would be required). Since GSL routines work with pointers to these structs, my idea is this: given a pointer extended_gsl_block*
, somehow get a pointer to just the fields size
and data
and reinterpret_cast
this into a gsl_block*
.
Upvotes: 2
Views: 713
Reputation: 69864
You are in luck.
The classes you show as an example conform to the requirements of standard layout types.
You can read more here:
http://en.cppreference.com/w/cpp/language/data_members#Standard_layout
You can test this premise in the compiler with:
static_assert(std::is_standard_layout<gsl_block>::value, "not a standard layout");
Upvotes: 2