How to detect whether a struct's member is a bit-field at compile time in C++?
Asked Answered
I

1

5

Given a struct S:

struct S {
  bool a : 1;
  bool b : 1;
};

How to determine that S::a and S::b are bit-fields at compile time?

I was trying to come up a macro like IsBitField(S, a) but had hard time to apply SFINAE with offsetof() or addressof() (which are known to be invalid operations per bit-fields).

Ism answered 4/4, 2021 at 20:19 Comment(2)
I think those are UB and not SFINAE.Tenderfoot
My guess would be that since <type_traits> doesn't have any tools mentioning bit fields, and since cppreference doesn't mention any SFINAE errors caused by bit fields, this probably isn't possible using templates. There might be some compiler built-ins that could help, but those wouldn't be portable.Jubilate
B
7

First, for a SFINAE solution, the ellipsis overload trick (with expression SFINAE) still works, we just need to find a construct that is ill-formed when applied to a bitfield while well-formed when applied on a normal member.

For example, you cannot form a pointer-to-member if the member is a bitfield.

So we could write:

template <typename T>
struct is_bit_field_t {
    template <typename U>
    static constexpr bool helper(...) {
        return true;
    }
    template <typename U>
    static constexpr bool helper(decltype(&U::a) arg) {
        return false;
    }
    static constexpr bool value = helper<T>(nullptr); 
};

This will test whether the public member a of U is of bitfield type. Explanation:

  • If T::a is a bitfield, then the expression decltype(&U::a) would be ill-formed, which would then SFINAE out the second overload.

Live demo: https://godbolt.org/z/TTn8YPKW3


With C++20, this could be done with concepts, utilizing the fact that you cannot take the address of a bitfield.

The core implementation would just look like:

template <typename T>
concept A_IsBitField = !requires (T t) {
     { &t.a };
};

Live demo: https://godbolt.org/z/W3jbosY93

If you hate to define a standalone concept, you could just use the requires expression instead (with the SFINAE method you have no choice but to create that struct)


Of course, the above only works for the member named a. If you would like to use it with other members, then you need to rewrite the SFINAE trait or the concept, replacing a with whatever member you want. You could write a macro to help you generate those constructs if you need a lot of them.

Blocked answered 4/4, 2021 at 21:20 Comment(3)
Shouldn't that concept be named A_IsNotBitField?Citystate
@RayHamel Fixed, though I guess it's easier just to add a ! before the requires...Blocked
is it a common thing that people use macro factories to create many functions? Whenever I encounter this as the last solution, I try to resist using it as it's such an anti-generic solution. is it viable?Fendley

© 2022 - 2024 — McMap. All rights reserved.