Сообщение Re[12]: 64 бита для целого без вариантов - добро или зло? от 04.08.2023 9:04
Изменено 04.08.2023 11:31 netch80
Re[12]: 64 бита для целого без вариантов - добро или зло?
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, netch80, Вы писали:
N>>На самом деле тут вообще ещё вопрос, при чём тут тьюринг-полнота языка на фазе трансляции, когда проверки всё равно надо впихивать в рантайм. Но если мы не сойдёмся по сказанному выше, то этот вопрос поднимать смысла нет.
S>Тут интереснее возможность не впихивать в рантайм избыточные проверки.
S>Например, при присванивании из RangeInteger<10, 20> в RangeInteger<5, 30> никакие проверки делать не надо.
S>В простых случаях вида RangeInteger<10, 50> a = 42; компилятору легко — после разворачивания шаблона сравнения if(42<10) будут выброшены.
S>А вот как отследить инварианты через границу двух разных типов?
Это как раз уже делается на существующих на данный момент средствах.
Clang имеет __builtin_assume(), который как раз говорит "считай данное условие гарантированным со стороны кодера". GCC (и Clang для совместимости) имеет __builtin_unreachable(), через который assume делается так:
И оптимизатор тоже умеет отбрасывать лишние проверки на таком основании.
Для такого RangeInteger мы можем написать
Главное не забыть делать реальные проверки при присвоении (в конструкторе, operator=, где ещё надо — ты в курсе).
В твоём примере типа
компилятор получит гарантии 10 <= i1.mValue <= 20, и выкинет лишние проверки при присвоении i2.
Переносилось ли это в какие-то другие компиляторы (хоть в MSVC) — я не узнавал.
S>Здравствуйте, netch80, Вы писали:
N>>На самом деле тут вообще ещё вопрос, при чём тут тьюринг-полнота языка на фазе трансляции, когда проверки всё равно надо впихивать в рантайм. Но если мы не сойдёмся по сказанному выше, то этот вопрос поднимать смысла нет.
S>Тут интереснее возможность не впихивать в рантайм избыточные проверки.
S>Например, при присванивании из RangeInteger<10, 20> в RangeInteger<5, 30> никакие проверки делать не надо.
S>В простых случаях вида RangeInteger<10, 50> a = 42; компилятору легко — после разворачивания шаблона сравнения if(42<10) будут выброшены.
S>А вот как отследить инварианты через границу двух разных типов?
Это как раз уже делается на существующих на данный момент средствах.
Clang имеет __builtin_assume(), который как раз говорит "считай данное условие гарантированным со стороны кодера". GCC (и Clang для совместимости) имеет __builtin_unreachable(), через который assume делается так:
#define assume(x) {if(!x) __builtin_unreachable(); }
И оптимизатор тоже умеет отбрасывать лишние проверки на таком основании.
Для такого RangeInteger мы можем написать
template <int Min, int Max> class RangeInteger {
...
operator underlying_int_t() const {
__builtin_unreachable(mValue < Min);
__builtin_unreachable(mValue > Max);
return mValue;
}
};
Главное не забыть делать реальные проверки при присвоении (в конструкторе, operator=, где ещё надо — ты в курсе).
В твоём примере типа
RangeInteger<10, 20> i1;
...
RangeInteger<5, 30> i2 = i1;
компилятор получит гарантии 10 <= i1.mValue <= 20, и выкинет лишние проверки при присвоении i2.
Переносилось ли это в какие-то другие компиляторы (хоть в MSVC) — я не узнавал.
Re[12]: 64 бита для целого без вариантов - добро или зло?
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, netch80, Вы писали:
N>>На самом деле тут вообще ещё вопрос, при чём тут тьюринг-полнота языка на фазе трансляции, когда проверки всё равно надо впихивать в рантайм. Но если мы не сойдёмся по сказанному выше, то этот вопрос поднимать смысла нет.
S>Тут интереснее возможность не впихивать в рантайм избыточные проверки.
S>Например, при присванивании из RangeInteger<10, 20> в RangeInteger<5, 30> никакие проверки делать не надо.
S>В простых случаях вида RangeInteger<10, 50> a = 42; компилятору легко — после разворачивания шаблона сравнения if(42<10) будут выброшены.
S>А вот как отследить инварианты через границу двух разных типов?
Это как раз уже делается на существующих на данный момент средствах.
Clang имеет __builtin_assume(), который как раз говорит "считай данное условие гарантированным со стороны кодера". GCC (и Clang для совместимости) имеет __builtin_unreachable(), через который assume делается так:
И оптимизатор тоже умеет отбрасывать лишние проверки на таком основании.
Для такого RangeInteger мы можем написать
Главное не забыть делать реальные проверки при присвоении (в конструкторе, operator=, где ещё надо — ты в курсе).
В твоём примере типа
компилятор получит гарантии 10 <= i1.mValue <= 20, и выкинет лишние проверки при присвоении i2.
Переносилось ли это в какие-то другие компиляторы (хоть в MSVC) — я не узнавал.
S>Здравствуйте, netch80, Вы писали:
N>>На самом деле тут вообще ещё вопрос, при чём тут тьюринг-полнота языка на фазе трансляции, когда проверки всё равно надо впихивать в рантайм. Но если мы не сойдёмся по сказанному выше, то этот вопрос поднимать смысла нет.
S>Тут интереснее возможность не впихивать в рантайм избыточные проверки.
S>Например, при присванивании из RangeInteger<10, 20> в RangeInteger<5, 30> никакие проверки делать не надо.
S>В простых случаях вида RangeInteger<10, 50> a = 42; компилятору легко — после разворачивания шаблона сравнения if(42<10) будут выброшены.
S>А вот как отследить инварианты через границу двух разных типов?
Это как раз уже делается на существующих на данный момент средствах.
Clang имеет __builtin_assume(), который как раз говорит "считай данное условие гарантированным со стороны кодера". GCC (и Clang для совместимости) имеет __builtin_unreachable(), через который assume делается так:
#define assume(x) {if(!x) __builtin_unreachable(); }
И оптимизатор тоже умеет отбрасывать лишние проверки на таком основании.
Для такого RangeInteger мы можем написать
template <int Min, int Max> class RangeInteger {
...
operator underlying_int_t() const {
if (mValue < Min) {__builtin_unreachable();} // или __builtin_assume(mValue >= Min);
if (mValue > Max) {__builtin_unreachable();} // или __builtin_assume(mValue >= Max);
return mValue;
}
};
Главное не забыть делать реальные проверки при присвоении (в конструкторе, operator=, где ещё надо — ты в курсе).
В твоём примере типа
RangeInteger<10, 20> i1;
...
RangeInteger<5, 30> i2 = i1;
компилятор получит гарантии 10 <= i1.mValue <= 20, и выкинет лишние проверки при присвоении i2.
Переносилось ли это в какие-то другие компиляторы (хоть в MSVC) — я не узнавал.