Как проверить правильность вычисления constexpr

114
14

Я использовал constexpr для вычисления хеш-кодов во время компиляции. Код компилируется правильно, выполняется правильно. Но я не знаю, если хэш-значения - это время компиляции или время выполнения. Если я трачу код во время выполнения, я не выполняю функции constexpr. Но они не отслеживаются даже для значений времени выполнения (вычислять хеш для генерируемой строки времени исполнения - одни и те же методы).
Я попытался разглядеть разборку, но я ее совсем не понимаю.


Для целей отладки мой хэш-код является только длиной строки, используя это:


constexpr inline size_t StringLengthCExpr(const char * const str) noexcept
{
return (*str == 0) ? 0 : StringLengthCExpr(str + 1) + 1;
};

У меня есть класс ID, созданный таким образом


class StringID
{
public:
constexpr StringID(const char * key);
private:
const unsigned int hashID;
}

constexpr inline StringID::StringID(const char * key)
: hashID(StringLengthCExpr(key))
{

}


Если я делаю это в программе main method


StringID id("hello world"); 

Я получил этот разобранный код (его часть - в основном есть много других методов и других вещей)


;;;     StringID id("hello world"); 

lea eax, DWORD PTR [-76+ebp]
lea edx, DWORD PTR [id.14876.0]
mov edi, eax
mov esi, edx
mov ecx, 4
mov eax, ecx
shr ecx, 2
rep movsd
mov ecx, eax
and ecx, 3
rep movsb

// another code


Как я могу сказать из этого, что "хэш-значение" - это время компиляции. Я не вижу постоянной, как 11, чтобы зарегистрироваться. Я не очень хорошо разбираюсь в ASM, поэтому, возможно, это правильно, но я не уверен, что проверить или как быть уверенным, что значения "хэш-кода" являются временем компиляции и не вычисляются во время выполнения из этого кода.


(Я использую Visual Studio 2013 + Intel С++ 15 Compiler - VS Compiler не поддерживает constexpr)


Edit:


Если я изменю свой код и сделаю это


    const int ix = StringLengthCExpr("hello world");

mov DWORD PTR [-24+ebp], 11 ;55.15


У меня есть правильный результат


Даже при этом


изменить личный hashID на общедоступный


 StringID id("hello world"); 
// mov DWORD PTR [-24+ebp], 11 ;55.15

printf("%i", id.hashID);
// some other ASM code


Но если я использую private hashID и добавляю Getter


  inline uint32 GetHashID() const { return this->hashID; };

для класса ID, тогда я получил


  StringID id("hello world"); 
//see original "wrong" ASM code

printf("%i", id.GetHashID());
// some other ASM code

спросил(а) 2015-03-11T16:52:00+03:00 5 лет, 11 месяцев назад
1
Решение
181

Самый удобный способ - использовать constexpr в инструкции static_assert. Код не будет компилироваться, когда он не будет оценен во время компиляции, и выражение static_assert не даст вам никаких накладных расходов во время выполнения (и без ненужного сгенерированного кода, как в случае с решением шаблона).


Пример:

static_assert(_StringLength("meow") == 4, "The length should be 4!");

Это также проверяет, правильно ли вычисляется ваша функция или нет.

ответил(а) 2015-03-11T17:11:00+03:00 5 лет, 11 месяцев назад
95

Если вы хотите убедиться, что функция constexpr оценивается во время компиляции, используйте ее результат для чего-то, что требует оценки времени компиляции:


template <size_t N>
struct ForceCompileTimeEvaluation { static constexpr size_t value = N; };

constexpr inline StringID::StringID(const char * key)
: hashID(ForceCompileTimeEvaluation<StringLength(key)>::value)
{}

Обратите внимание, что я переименовал функцию только StringLength. Имя, начинающееся с символа подчеркивания, за которым следует заглавная буква или содержащее два последовательных символа подчеркивания, не является законным в коде пользователя. Они зарезервированы для реализации (компилятор и стандартная библиотека).

ответил(а) 2015-03-11T16:56:00+03:00 5 лет, 11 месяцев назад
44

Следующий трюк может помочь проверить, была ли оценена функция constexpr только во время компиляции:


С помощью gcc вы можете скомпилировать исходный файл с листингом сборки + c источниками; что и constexpr, и его вызовы находятся в исходном файле try.cpp


 gcc -std=c++11 -O2 -Wa,-a,-ad  try.cpp  | c++filt >try.lst  

Если функция constexpr была оценена во время выполнения, тогда вы увидите скомпилированную функцию и инструкцию вызова (вызов имя_функции на x86) в списке сборки try.lst(обратите внимание, что команда С++ filter не укомплектована компоновщиком имена)


Интересно, что я всегда вижу вызов, если он скомпилирован без оптимизации (без опции -O2 или -O3).

ответил(а) 2017-05-16T19:56:00+03:00 3 года, 9 месяцев назад
43

Существует несколько способов принудительной оценки времени компиляции. Но это не так гибко и легко настроить, как вы ожидали при использовании constexpr. И они не помогут вам найти, действительно ли использовались константы времени компиляции.


Что бы вы хотели для constexpr, это работать там, где вы ожидаете его полезности. Поэтому вы пытаетесь удовлетворить его требования. Но тогда вам нужно проверить, был ли генерирован код, который вы ожидаете получить во время компиляции, и если пользователи фактически потребляют сгенерированный результат или запускают функцию во время выполнения.


Я нашел два способа определить, использует ли функция класса или (члена) путь оценки времени компиляции или времени выполнения.


    Используя свойство constexpr функций, возвращающих true из оператора noexcept (bool noexcept( expression )), если он вычисляется во время компиляции. Поскольку сгенерированный результат будет константой времени компиляции. Этот метод вполне доступен и применим с помощью Unit-testing.

    (Имейте в виду, что маркировка этих функций явно noexcept приведет к поломке теста.)

Источник: cppreference.com(2017/3/3)
Поскольку оператор noexcept всегда возвращает true для константного выражения, его можно использовать для проверки того, принимает ли конкретный вызов функции constexpr постоянную ветвь выражения (...)



(менее удобно) Использование отладчика: Помещая точку останова внутри функции, помеченной constexpr. Всякий раз, когда точка прерывания не запускается, использовался результат оценки компилятора. Не самая простая, но возможная случайная проверка.

Суре: документация Microsoft (2017/3/3)
Примечание. В отладчике Visual Studio вы можете определить, будет ли функция constexpr оцениваться во время компиляции, помещая в нее контрольную точку. Если точка останова ударяется, функция вызывается во время выполнения. Если нет, то функция вызывается во время компиляции.



Я нашел оба этих метода полезными, экспериментируя с constexpr. Хотя я не тестировал среду вне VS2017. И не удалось найти явное утверждение, поддерживающее это поведение в текущем черновике стандарта.

ответил(а) 2017-03-03T19:42:00+03:00 4 года назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема