Страницы

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

Показаны сообщения с ярлыком constexpr. Показать все сообщения
Показаны сообщения с ярлыком constexpr. Показать все сообщения

понедельник, 2 марта 2020 г.

Разное поведение constexpr на разных платформах

#cpp #cpp14 #constexpr


Доброго времени суток. Есть класс, который включает в себя другой класс со статическими
членами массивами. Я очень хочу сделать что бы у меня была возможность задать все статик
значения этих членов в хедере а не в cpp части. Для этого я применил constexpr и эта
схема нормально заработала на компиляторе от Microsoft. Однако на g++ под ARM на Debian
этот код не компилируется:


  undefined reference to `Foo::Target::IP'


В обоих случаях используется С++14. Вопрос вот в чём. Почему один компилятор это
принимает, а другой нет, т.е. в чём отличия работы с constexpr внутри у этих компиляторов?
Это никак не стандартизовано? Что мне сделать, чтобы такая схема заработала?

class Foo {
public:
    static void GetIp(uint8_t* data) {
        memcpy(data, &Target::IP[0], 4);
    };
private:
    class Target {
    public:
        static constexpr uint8_t IP[4] = { 192,168,0,110 };
        static constexpr uint16_t Port = 3000;
    };
};
int main()
{
    uint8_t A[4];
    Foo::GetIp(A);
    printf("%u.%u.%u.%u", A[0], A[1], A[2], A[3]);
    system("PAUSE");
    return 0;
}

    


Ответы

Ответ 1



Да чтоб заработало, все просто - добавить constexpr uint8_t Foo::Target::IP[4]; Только и всего. См. https://ideone.com/ZMqPMn Но вот что именно строго по стандарту, а что нет - тут я не скажу, не уверен. Понятно, что целочисленное поле и массив - разные вещи, но что именно стандарт разрешает, что нет, и что отдает на откуп реализации - не скажу...

воскресенье, 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 "<

пятница, 13 декабря 2019 г.

Время выполнения функций

#cpp #cpp11 #runtime #constexpr


Вот, я создал constexpr функцию. Как точно определить, выполнится она во время компиляции
или в runtime?
    


Ответы

Ответ 1



Присвойте ее значение переменной, объявленной как constexpr. Если все пройдет успешно - значит, выполнилось во время компиляции :)

Ответ 2



Можно попробовать использовать её в контексте, предполагающем исключительно compile-time выполнение. Например, static_assert: constexpr void f(int a) { } /*const*/ int i = 42; static_assert( (f(i),true), ""); Без const у i будет ошибка: error: non-constant condition for static assertion

суббота, 7 декабря 2019 г.

Как заставить работать constexpr с указателем на -1?

#cpp #cpp11 #constexpr


Нужен указатель на адрес 0xFFFF FFFF FFFF FFFF, или на -1. Пытаюсь определить как
constexpr auto hInvd = reinterpret_cast(-1ul);, но вываливается ошибка, что
это не константное выражение. Очевидно из-за reinterpret_cast. К сожалению, другие
*_cast тоже не дают ничего полезного. Простой const работает без проблем const auto
hInvd=reinterpret_cast(-1ul);
Мне нужен указатель на область памяти, которая будет свободна. nullptr для этого
не годится, так как нулевой адрес может быть использован, а вот вероятность использования
адреса 0xFFFFFFFFFFFFFFFF практически равна нулю.
    


Ответы

Ответ 1



Конвертировать любую целочисленную константу в указатель на constexpr невозможно, т.к. для этого используется reinterpet_cast, который прямо запрещён в описании константных выражений ([expr.const]). Почему так? Потому что constexpr всегда является чем-то определённым и не может дать на выходе неопределённое поведение. Если бы можно было случайный адрес привести к указателю на constexpr, тогда получилось бы, что его использование (разыменовывание) может приводить к UB.

Ответ 2



Как выяснилось, этот ответ - баг компилятора :) Ну, gcc сожрал финт с вычитанием единицы из нулевого указателя, правда, только через промежуточную переменную: https://ideone.com/Hp6DNt #include using namespace std; int main() { constexpr void *p = nullptr; constexpr void *res = p - 1; cout << res << endl; return 0; }

пятница, 17 мая 2019 г.

Разное поведение constexpr на разных платформах

Доброго времени суток. Есть класс, который включает в себя другой класс со статическими членами массивами. Я очень хочу сделать что бы у меня была возможность задать все статик значения этих членов в хедере а не в cpp части. Для этого я применил constexpr и эта схема нормально заработала на компиляторе от Microsoft. Однако на g++ под ARM на Debian этот код не компилируется:
undefined reference to `Foo::Target::IP'
В обоих случаях используется С++14. Вопрос вот в чём. Почему один компилятор это принимает, а другой нет, т.е. в чём отличия работы с constexpr внутри у этих компиляторов? Это никак не стандартизовано? Что мне сделать, чтобы такая схема заработала?
class Foo { public: static void GetIp(uint8_t* data) { memcpy(data, &Target::IP[0], 4); }; private: class Target { public: static constexpr uint8_t IP[4] = { 192,168,0,110 }; static constexpr uint16_t Port = 3000; }; }; int main() { uint8_t A[4]; Foo::GetIp(A); printf("%u.%u.%u.%u", A[0], A[1], A[2], A[3]); system("PAUSE"); return 0; }


Ответ

Да чтоб заработало, все просто - добавить
constexpr uint8_t Foo::Target::IP[4];
Только и всего. См. https://ideone.com/ZMqPMn
Но вот что именно строго по стандарту, а что нет - тут я не скажу, не уверен. Понятно, что целочисленное поле и массив - разные вещи, но что именно стандарт разрешает, что нет, и что отдает на откуп реализации - не скажу...

четверг, 16 мая 2019 г.

constexpt function with anonymous union in C++

Всем привет!
Сейчас у меня такой код:
// 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. Спасибо.


Ответ

Насколько я знаю, определить порядок байт (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); }
К тому же, так он еще и места меньше занимает.

четверг, 18 октября 2018 г.

Время выполнения функций

Вот, я создал constexpr функцию. Как точно определить, выполнится она во время компиляции или в runtime?


Ответ

Присвойте ее значение переменной, объявленной как constexpr. Если все пройдет успешно - значит, выполнилось во время компиляции :)

среда, 10 октября 2018 г.

Как заставить работать constexpr с указателем на -1?

Нужен указатель на адрес 0xFFFF FFFF FFFF FFFF, или на -1. Пытаюсь определить как constexpr auto hInvd = reinterpret_cast(-1ul);, но вываливается ошибка, что это не константное выражение. Очевидно из-за reinterpret_cast. К сожалению, другие *_cast тоже не дают ничего полезного. Простой const работает без проблем const auto hInvd=reinterpret_cast(-1ul); Мне нужен указатель на область памяти, которая будет свободна. nullptr для этого не годится, так как нулевой адрес может быть использован, а вот вероятность использования адреса 0xFFFFFFFFFFFFFFFF практически равна нулю.


Ответ

Конвертировать любую целочисленную константу в указатель на constexpr невозможно, т.к. для этого используется reinterpet_cast, который прямо запрещён в описании константных выражений ([expr.const]).
Почему так? Потому что constexpr всегда является чем-то определённым и не может дать на выходе неопределённое поведение. Если бы можно было случайный адрес привести к указателю на constexpr, тогда получилось бы, что его использование (разыменовывание) может приводить к UB.