boostorg / endian

Boost Endian library
46 stars 49 forks source link

Fix BOOST_STATIC_ASSERT when passing enums (regression) #44

Closed Chaosvex closed 4 years ago

Chaosvex commented 4 years ago

Boost 1.71.0 introduced a regression that breaks the ability to reverse enum types.

For example:

#include <boost/endian/conversion.hpp>

enum class Foo { BAR };

int main() {
    Foo bar { Foo::BAR };
    boost::endian::native_to_little_inplace(bar);
}

Under 1.70, this compiles but under 1.71:

In file included from /opt/wandbox/boost-1.71.0/gcc-head/include/boost/endian/detail/endian_reverse.hpp:13,
                 from /opt/wandbox/boost-1.71.0/gcc-head/include/boost/endian/conversion.hpp:11,
                 from prog.cc:1:
/opt/wandbox/boost-1.71.0/gcc-head/include/boost/endian/conversion.hpp: In instantiation of 'void boost::endian::native_to_little_inplace(EndianReversibleInplace&) [with EndianReversibleInplace = Foo]':
prog.cc:7:48:   required from here
/opt/wandbox/boost-1.71.0/gcc-head/include/boost/endian/conversion.hpp:330:89: error: static assertion failed: detail::is_endian_reversible_inplace<EndianReversibleInplace>::value
  330 |     BOOST_STATIC_ASSERT( detail::is_endian_reversible_inplace<EndianReversibleInplace>::value );
      |                                                                                         ^~~~~
/opt/wandbox/boost-1.71.0/gcc-head/include/boost/static_assert.hpp:70:55: note: in definition of macro 'BOOST_STATIC_ASSERT'
   70 | #     define BOOST_STATIC_ASSERT( ... ) static_assert(__VA_ARGS__, #__VA_ARGS__)
      |                                                       ^~~~~~~~~~~
pdimov commented 4 years ago

With this fix, the above would compile (on a little-endian platform), because it doesn't actually reverse anything, but big_to_native_inplace will still fail, in endian_reverse.

A case can be made that both should work. But it's a bit subtle because endian_reverse on a normal unscoped enum is often undefined behavior because the reversed value is outside of the valid range of the enum. Interestingly, this is not the case for scoped enums; their valid range is the entire range of the underlying type.

For endian_reverse_inplace though, the library can, in principle, support even unscoped enums; as long as nobody actually tries to read the reversed enum, there's no UB.

pdimov commented 4 years ago

The given example should now compile with the current develop.