Hi all,
As part of a little project I'm working on, I'm writing some modern
C++-style wrappers around traditional UNIX/C-style APIs. Of particular
relevance to my question, I have a standard-layout filedes class with a
single int member and a destructor that closes the file (along with
member-free subclasses like kqueue or named_file that provide
constructors specific to their type and type-specific member functions
like kevent), and a struct kevent wrapper that uses scoped enums and
bitmask types built on top of them to represent fields like filter,
flags, etc. (for those unfamiliar with kqueue, see the section on struct
kevent at
http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2).
I could of course write member functions that construct the proper
C data structures when I need to call an underlying C API, but I
purposefully defined them with the intent that their underlying memory
layout would be *equivalent* to the relevant C types, and that I could
just cast back and forth at the interface boundaries. What I have [1] is
working and seems to me like a reasonable interpretation of the *spirit*
of concepts like standard layout and layout compatible, but there are a
few assumptions I'm making that I can't seem to find justification for
in the standard (I am going based off of n3690).
In particular, I assume that:
* A standard-layout class with a single non-static member (and no base
classes with any non-static members) is layout-compatible with the
type of that member. For example, I assume that my filedes class is
layout-compatible with int.
* An enumeration type is layout-compatible with its underlying type (see
my SO question at [2]) I was able to find language that ensures two
enumerations with the same underlying type are layout-compatible with
each other, but nothing about the relationship between an enumeration
type and its underlying type.
* If T1 and T2 are standard-layout and layout-compatible with each
other, then you can safely reinterpret_cast back and forth between
them in all contexts. For example, my event class is (assuming my
previous assumptions are correct) layout-compatible with struct
kevent, so I can reinterpret_cast an array of events to an array of
struct kevent when calling the kevent function and reinterpret_cast
the results back when I'm done. If I only have a single member and I
only have a single element I want to point to (i.e. I'm dealing with a
pointer, not an array), this is covered by the "a pointer to a
standard-layout type is a pointer to its first element" rule, but once
we get to arrays or structs with more than one member that doesn't
suffice. I was able to find that T1 and T2 here have the same value
representation and the same alignment requirements, but nothing that
allows me to cast types with the same value representation and same
alignment requirements to each other.
I hope you'll agree that these assumptions are reasonable and intended
by the spirit of the standard, but I would like to verify a) that they
indeed are reasonable and b) whether or not they can actually be
justified based on the text of the standard alone. It would also be nice
to know if the approach is a good idea at all, but this project is
mostly just for fun so that's a secondary concern here :).
Cheers,
Shea Levy
P.S. This is mostly unrelated, but it would be nice to have an
is_layout_compatible type trait so I could have some static_asserts on
some of my assumptions here.
[1]:
https://bitbucket.org/shlevy/linkpaper/src/b5d1f3fe21f5/src , see
in particular filedes.hh and event.hh. Sorry if the coding style is
horrid, part of the goal of this project is to just try some things
and see how they work
[2]:
http://stackoverflow.com/questions/21956017/are-enumeration-types-layout-compatible-with-their-underlying-type