Страницы

Поиск по вопросам

воскресенье, 1 марта 2020 г.

constexpt function with anonymous union in C++

#cpp #cpp17 #constexpr


Всем привет!

Сейчас у меня такой код:

// Source.hpp

/**
 * @brief Method that checks the endian type on the system.
 * @return DATA_LITTLE_ENDIAN(0x02) - if on the system little endian, otherwise -
DATA_BIG_ENDIAN(0x01).
 */
static inline DATA_ENDIAN_TYPE CheckSystemEndian(void) noexcept
{
    const union {
        const uint16_t value;
        const uint8_t data[sizeof(uint16_t)];
    } endian { 0x0102 };
    return static_cast(endian.data[0]);
}

Class Foo
{
    static const DATA_ENDIAN_TYPE system_endian;
}

// Source.cpp
inline const DATA_ENDIAN_TYPE BinaryDataEngine::system_endian = CheckSystemEndian();


Что требуется:
Мне хочется решать эту задачу в compile-time, следовательно необходимо переделать
функция CheckSystemEndian() на constexp. Однако в этой задаче мне очень мешает union.

Ошибка следующая:


  constexpr function never produces a constant expression.
  Read of member 'data' of union with active member 'value' is not allowed in a constant
expression.


Подсткажите, пожалуйста, способ, как можно добиться требуемой функциональности в
compile-time.
Спасибо. 
    


Ответы

Ответ 1



Насколько я знаю, определить порядок байт (endianness) на этапе компиляции невозможно в принципе (стандартными средствами). Тут нужен reinterpret_cast, а он не считается constexpr выражением. В С++20 для этого появится std::endian, и можно будет писать так: #include constexpr bool is_big_endian = std::endian::native == std::endian::big; constexpr bool is_little_endian = std::endian::native == std::endian::little; Дожидаясь С++20, можно использовать нестандартные фичи компиляторов. Например, GCC умеет так: constexpr bool is_big_endian = __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; constexpr bool is_little_endian = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__; Кроме того, у вас в программе неопределенное поведение, ведь С++, в отличие от С, не позволяет читать из неактивного поля union'а. Пруф. Вот пример, как можно исправить код: static inline DATA_ENDIAN_TYPE CheckSystemEndian(void) noexcept { const uint16_t value = 0x0102; return static_cast((uint8_t &)value); } К тому же, так он еще и места меньше занимает.

Ответ 2



Есть вариант а ля cmake или Lisp, мне больше нравится Лисп. Makefile : ... clean: ...TAB... @rm endian.hpp endian ... endian.hpp: endian.cpp ...TAB... @g++ --std=c++11 endian.cpp -o endian ...TAB... @./endian > endian.hpp ... endian.cpp : # include volatile uint16_t const v16 { 0x0102 } ; volatile uint8_t const * pv8 { reinterpret_cast < volatile uint8_t const * > ( & v16 ) } ; bool const little_endian { ( * pv8 ) == 0x02 } ; bool const big_endian { ( * pv8 ) == 0x01 } ; int main ( ) { if(little_endian) std::cout<<"# define MY_LITTLE_ENDIAN "<

Комментариев нет:

Отправить комментарий