Check if two types can be aliased

I have a public API function (in a library header) that accepts a struct foo &, and that function's internal implementation calls a third-party dependency's function that requires a struct bar *.

I do not want to pull in the third party dependency into the public header as the library will be distributed in binary form and this dependency is an implementation detail.

However, I do not intend to attach any additional information to foo and instead want to be able to do reinterpret_cast<bar*>(&foo) in order to reduce the amount of copies I have to perform (it's a very frequently called function).

Does the standard library have a type_trait or some other mechanism to check if the structure of foo and bar are identical? I understand that this will require more maintanence upkeep on my end to make sure the header matches the internal dependency's type definition as well, but that's fine as it's unlikely to change any time soon.

Ultimately I need something like

struct foo {
    int a;
    const char *b;
};

struct bar {
    int c;
    const char *d;
};

static_assert(
    std::is_aliasable_to<foo, bar>::value, //        <-- Is this possible?
    "foo must be structurally equivalent to bar"
);

void public_api_function(foo &f) {
    private_third_party_function(
        reinterpret_cast<bar*>(&f)
    );
}

Upvotes: 4

Views: 133

Answers (1)

Artyer
Artyer

Reputation: 40801

reinterpret_cast<bar*>(&f) is always UB because of strict-aliasing.

Without strict-aliasing, you could use c++20's std::is_layout_compatible, but there isn't any compiler support for that.

You could use a hack like this:

template<typename T, typename U>
constexpr bool is_same_member_type(T foo::*, U bar::*) {
    return std::is_same_v<T, U>;
}

#define HAS_SAME_MEMBER(foo_name, bar_name) (offsetof(foo, foo_name) == offsetof(bar, bar_name) && is_same_member_type(&foo:: foo_name, &bar:: bar_name))

// add more HAS_SAME_MEMBER calls if new members are added
static_assert(
    sizeof(foo) == sizeof(bar) &&
    std::is_standard_layout_v<foo> && std::is_standard_layout_v<bar> &&
    HAS_SAME_MEMBER(a, c) &&
    HAS_SAME_MEMBER(b, d),
    "foo must be structurally equivalent to bar"
);

#undef HAS_SAME_MEMBER

Upvotes: 6

Related Questions