Компилирование с -Wsuggest-attribute=pure
заставляет GCC предлагать потенциальные функции, которые могут быть помечены __attribute__ ((pure))
в целях оптимизации.
Вот значение pure
по документации GCC:
Многие функции не имеют эффектов, кроме возвращаемого значения, и их возвращаемое значение зависит только по параметрам и / или глобальным переменным. Такая функция может быть подвержена общему исключению подвыражения и оптимизации цикла так же, как и арифметический оператор. Эти функции должны быть объявлены с атрибутом pure.
Я создаю небольшой игровой движок, где у меня есть input_context
класс, который содержит input_state
член. input_context
класс обновляет input_state
Участник каждого кадра, получая глобальное состояние ввода из операционной системы.
Он также содержит несколько «получателей» для запроса состояния ввода.
Упрощенный пример:
class input_context
{
private:
input_state _input_state;
public:
void update()
{
os::fill_input_state(_input_state);
}
auto mouse_x() const noexcept
{
return _input_state._mouse_x;
}
auto mouse_y() const noexcept
{
return _input_state._mouse_y;
}
auto is_key_down(keycode k) const noexcept
{
// `_keys` is an array of `bool` values.
return _input_state._keys[k];
}
};
GCC говорит мне, что все эти «методы получения», как mouse_x()
, mouse_y()
а также is_key_down()
являются кандидатами на __attribute__ ((pure))
,
Должен ли я отметить эти методы как pure
?
Я так не думаю, но предложение GCC заставляет меня задуматься об этом.
Я не уверен, как интерпретировать определение GCC pure
— это говорит о том, что функции, которые полагаются только по параметрам и / или глобальным переменным должны быть помечены как таковые.
В некотором смысле, глобальное состояние ввода ОС может быть интерпретировано как глобальная переменная.
С другой стороны, «методы получения» всегда возвращают разные значения, в зависимости от _input_state
переменная-член.
Я считаю, что все в порядке, чтобы пометить его как чистый.
Рассмотрим ваш пример в упрощенной форме с некоторыми добавленными функциями ввода-вывода:
#include <stdio.h>
class X {
int x_=0;
public:
int x() const noexcept __attribute__ ((pure)) /*__attribute__((noinline))*/;
void inc() noxcept { x_++; }
};
int X::x() const noexcept { puts("getting x"); return x_;}
int main(){
X x;
printf("%d\n", x.x() + x.x() + x.x());
x.inc();
printf("%d\n", x.x() + x.x() + x.x());
}
чистый позволяет получить:
getting x
0
getting x
3
вместо
getting x
getting x
getting x
0
getting x
getting x
getting x
3
на уровне оптимизации не менее -O1 (на более высоких уровнях вам может понадобиться добавить __attribute__((noinline))
для предотвращения встраивания).
Это нормально, если состояние изменяется между двумя последовательными вызовами к этим получателям, пока компилятор может обнаружить, что состояние изменилось.
Если вам нужно запустить non-const
способ изменить состояние, тогда это не нарушение чистоты. Однако, если состояние меняется on its own
(система изменяет это / другой поток изменяет это / обработчик сигнала изменяет это) таким образом, что компилятор не может знать об этом изменении, тогда pure
атрибут больше не является законным.
Я думаю, что вы можете пометить эти методы как чистые, потому что они не имеют побочных эффектов.
Согласно приведенной вами документации, чистая функция может зависеть от глобальной переменной / внешнего состояния. Я думаю, это также связано с концепцией закрытия. Вы можете определить функцию f
(используя Haskell, например) на основе внешней переменной:
x=1
f y = x + y
сама функция по-прежнему не имеет побочных эффектов (хотя в Haskell вы не можете изменить значение x
в то время как вы можете изменить x
вне функции / метода в C / C ++).
Разница между pure
а также const
резюмируется в этом вопросе: __attribute __ ((const)) против __attribute __ ((pure)) в GNU C
квотирование ответ там:
атрибут((const)) так же, как атрибут((чисто)), но без доступа к глобальным переменным.