constexpt function with anonymous union in C++

228
24 июня 2018, 19:20

Всем привет!

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

// 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<DATA_ENDIAN_TYPE>(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. Спасибо.

Answer 1

Насколько я знаю, определить порядок байт (endianness) на этапе компиляции невозможно в принципе (стандартными средствами). Тут нужен reinterpret_cast, а он не считается constexpr выражением.

В С++20 для этого появится std::endian, и можно будет писать так:

#include <type_traits>
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<DATA_ENDIAN_TYPE>((uint8_t &)value);
}

К тому же, так он еще и места меньше занимает.

Answer 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 <iostream>
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 "<<std::endl;
  else std::cout<<"# define MY_BIG_ENDIAN "<<std::endl;
  std::cout<<"bool constexpr little_endian { "<<(little_endian ? "true" : "false")<<" } ;"<<std::endl;
  std::cout<<"bool constexpr big_endian { "<<(big_endian ? "true" : "false")<<" } ;"<<std::endl; }
READ ALSO
почему компилятор ругается на fscanf? Выдает ошибку clang: error: linker command failed with exit code 1 (use -v to see invocation)

почему компилятор ругается на fscanf? Выдает ошибку clang: error: linker command failed with exit code 1 (use -v to see invocation)

//Из текстового файла вводится символьная строка, состоящая из чисел, //разделенных пробеломСоставить программу, которая вводит строку, организует...

210
Сравнение структур yaml файлов с++

Сравнение структур yaml файлов с++

Нужно с использованием библиотеки yamlh написать код для сравнения yaml файлов

187
Цикл для школьной задачи

Цикл для школьной задачи

Допустим, что задача звучит следующим образом:

213