Мета-рекурсия
От: Шахтер Интернет  
Дата: 03.08.15 12:20
Оценка: 2 (1) +1 -1 :))
Теперь некоторые вещи можно упростить.


template <class UInt,UInt Val=UInt(-1)>
const unsigned BitsOf = (Val>1)? 1+BitsOf<UInt,(Val>>1)> : Val ;


Считаем число бит в беззнаковом типе.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: Мета-рекурсия
От: Vamp Россия  
Дата: 03.08.15 12:40
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Теперь...

Почему "теперь"? С тех пор, как шаблоны придумали, можно.
Да здравствует мыло душистое и веревка пушистая.
Re: Мета-рекурсия
От: watchmaker  
Дата: 03.08.15 12:51
Оценка:
Здравствуйте, Шахтер, Вы писали:


Ш>Считаем число бит в беззнаковом типе.


Не считает:
error: recursive template instantiation exceeded maximum depth
А всё из-за того, что из Bits<T, 0> идёт бесконечно рекурсивный вызов к Bits<T, 0>. Если бы это были обычные функции, то такой рекурсивный вызов не происходил бы из-за условия в тернарном операторе, но для шаблонов соответствующую ветку компилятор всё равно обязан рассмотреть.

Ш>Теперь некоторые вещи можно упростить.

Вот сначала приведи в соответствие со стандартом предыдущий код, а потом сравни простоту и понятность с тем же
std::numeric_limits<UInt>::digits

Ну или с вариантом, завёрнутым в свой шаблон с аналогичным твоему именем:
template <class UInt> constexpr auto BitsOf = std::numeric_limits<UInt>::digits;
Отредактировано 03.08.2015 19:23 watchmaker . Предыдущая версия .
Re[2]: Мета-рекурсия
От: Шахтер Интернет  
Дата: 03.08.15 15:17
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Здравствуйте, Шахтер, Вы писали:



Ш>>Считаем число бит в беззнаковом типе.


W>Не считает:
error: recursive template instantiation exceeded maximum depth
А всё из-за того, что из Bits<T, 0> идёт бесконечно рекурсивный вызов к Bits<T, 0>. Если бы это были обычные функции, то такой рекурсивный вызов не происходил бы из-за условия в тернарном операторе, но для шаблонов соответствующую ветку компилятор всё равно обязан рассмотреть.


У меня считает.

Ш>>Теперь некоторые вещи можно упростить.

W>Вот сначала приведи в соответствие со стандартом предыдущий код, а потом сравни простоту и понятность с тем же
template <class UInt> constexpr auto BitsOf = std::numeric_limits<UInt>::digits;
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[2]: Мета-рекурсия
От: LaptevVV Россия  
Дата: 03.08.15 16:22
Оценка:
W>Не считает:
error: recursive template instantiation exceeded maximum depth
А всё из-за того, что из Bits<T, 0> идёт бесконечно рекурсивный вызов к Bits<T, 0>. Если бы это были обычные функции, то такой рекурсивный вызов не происходил бы из-за условия в тернарном операторе, но для шаблонов соответствующую ветку компилятор всё равно обязан рассмотреть.

Ну так, может быть, просто разные компилеры используются?
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[3]: Мета-рекурсия
От: watchmaker  
Дата: 03.08.15 16:32
Оценка: 2 (1)
Здравствуйте, LaptevVV, Вы писали:

LVV>Ну так, может быть, просто разные компилеры используются?


Конечно разные. Но смысл не в том, что они разные, а в том, что результат шаблона из первого сообщения неопределён. И поэтому такой код не нужно писать или использовать. То что разные компиляторы выдают разные ответы — лишь следствие проблемы в исходном тексте.


Ну и если уж разговор зашёл об компиляторах, то, например, gcc-5.2 компилирует исходный пример, а clang-3.7 выдаёт ошибку. С другой стороны, если пример чуть-чуть переписать пользуясь средствами только C++03, то ситуация может изменится на противоположную: gcc-5.2 теперь находит бесконечную рекурсию, а clang-3.7 выдаёт ответ. Так что тут даже нельзя утверждать что gcc или clang хуже другого справляются с таким кодом — оба иногда сообщают об ошибке (что хорошо), а иногда выдают численный ответ (что на самом деле хуже, так как может замаскировать ошибки в более сложных случаях).

Ну и если уж выбирать способ считать биты, то куда лучше вообще вместо шаблонов взять constexpr-функции — в них правила рекурсии другие, более интуитивно понятные и с ними всё работает во всех компиляторах, удовлетворяющих стандарту:
template <typename UInt>
constexpr unsigned BitsOf(UInt A = -1) {
    return A ? 1 + BitsOf(A >> 1) : 0;
}
Отредактировано 03.08.2015 16:59 watchmaker . Предыдущая версия .
Re: Мета-рекурсия
От: SuhanovSergey  
Дата: 03.08.15 19:20
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Теперь некоторые вещи можно упростить.


Ш>

Ш>template <class UInt,UInt Val=UInt(-1)>
Ш>const unsigned BitsOf = (Val>1)? 1+BitsOf<UInt,(Val>>1)> : Val ;

Ш>


в самом деле, как же мы раньше мучались без мета-рекурсии

template <class T> const unsigned NrOfBits = sizeof(T)*CHAR_BIT;
Re: Мета-рекурсия
От: Abyx Россия  
Дата: 03.08.15 19:49
Оценка: +1
Здравствуйте, Шахтер, Вы писали:

Ш>

Ш>template <class UInt,UInt Val=UInt(-1)>
Ш>const unsigned BitsOf = (Val>1)? 1+BitsOf<UInt,(Val>>1)> : Val ;

Ш>


есть же constexpr функции, зачем писать такую нечитаемую штуку
In Zen We Trust
Re[2]: Мета-рекурсия
От: watchyourinfo Аргентина  
Дата: 03.08.15 20:02
Оценка:
SS>в самом деле, как же мы раньше мучались без мета-рекурсии

SS>
SS>template <class T> const unsigned NrOfBits = sizeof(T)*CHAR_BIT;
SS>


ну а теперь представь себе 80-битовое число (например, с плавающей точкой), которое архитектура требует выравнивать по 16-байтовым границам.
sizeof такого числа будет 16, а не 10.
Re[4]: Мета-рекурсия
От: Шахтер Интернет  
Дата: 03.08.15 21:06
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Здравствуйте, LaptevVV, Вы писали:


LVV>>Ну так, может быть, просто разные компилеры используются?


W>Конечно разные. Но смысл не в том, что они разные, а в том, что результат шаблона из первого сообщения неопределён.


Это почему? Я не видел обоснования.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[2]: Мета-рекурсия
От: Шахтер Интернет  
Дата: 03.08.15 21:20
Оценка:
Здравствуйте, Abyx, Вы писали:

A>Здравствуйте, Шахтер, Вы писали:


Ш>>

Ш>>template <class UInt,UInt Val=UInt(-1)>
Ш>>const unsigned BitsOf = (Val>1)? 1+BitsOf<UInt,(Val>>1)> : Val ;

Ш>>


A>есть же constexpr функции, зачем писать такую нечитаемую штуку


Ну не знаю, я вполне себе читаю.
А constexpr-функции использовать тоже можно, но тогда придётся делать две декларации -- сначала функцию, а потом константу.


template <class UInt>
constexpr unsigned BitsOfFunc(UInt val)
 {
  return (val>1)? 1+BitsOfFunc<UInt>(val>>1) : val ;
 }

template <class UInt,UInt Val=UInt(-1)>
сonst unsigned BitsOf = BitsOfFunc<UInt>(Val) ;
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: Мета-рекурсия
От: enji  
Дата: 04.08.15 07:41
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>

Ш>template <class UInt,UInt Val=UInt(-1)>
Ш>const unsigned BitsOf = (Val>1)? 1+BitsOf<UInt,(Val>>1)> : Val ;

Ш>


Круто. Шаблонные константы как-то прошли мимо меня
Re: Мета-рекурсия
От: ArtDenis Россия  
Дата: 05.08.15 05:16
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Считаем число бит в беззнаковом типе.


В дополнении к другим придиркам тоже добавлю что Val=UInt(-1) лучше заменить Val=UInt(~0). Так "более" правильно, т.к. представление отрицательных чисел может быть разным (в случае представления под названием "код со сдвигом" твой код будет выдавать некорректный результат).
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Отредактировано 05.08.2015 5:17 ArtDenis . Предыдущая версия .
Re[2]: Мета-рекурсия
От: _NN_ www.nemerleweb.com
Дата: 05.08.15 05:31
Оценка: +1
Здравствуйте, ArtDenis, Вы писали:

AD>В дополнении к другим придиркам тоже добавлю что Val=UInt(-1) лучше заменить Val=UInt(~0). Так "более" правильно, т.к. представление отрицательных чисел может быть разным (в случае представления под названием "код со сдвигом" твой код будет выдавать некорректный результат).


В любом случае это не будет работать для знаковых типов потому как "Val>>1" немного не определен
Если хотим поддерживать и знаковые числа нужно вызывать std::make_unsigned .
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: Мета-рекурсия
От: Шахтер Интернет  
Дата: 05.08.15 23:53
Оценка:
Здравствуйте, ArtDenis, Вы писали:

AD>Здравствуйте, Шахтер, Вы писали:


Ш>>Считаем число бит в беззнаковом типе.


AD>В дополнении к другим придиркам тоже добавлю что Val=UInt(-1) лучше заменить Val=UInt(~0). Так "более" правильно, т.к. представление отрицательных чисел может быть разным (в случае представления под названием "код со сдвигом" твой код будет выдавать некорректный результат).


Эта конструкция только для беззнаковых чисел. Да и речь не о том. Подсчет числа битов взят только как пример. Речь о рекурсии.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: Мета-рекурсия
От: Кодт Россия  
Дата: 06.08.15 12:09
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Теперь некоторые вещи можно упростить.


Грабельки. Не надо такое тащить в продакшен.

http://ideone.com/a83SVo
#include <iostream>
using namespace std;

template<int X> int const num = X==1 ? 0 : 1 + num<X%2 ? X*3+1 : X/2>;
//template<int X> int const ccc = X==0 ? 0 : 1 + ccc<0>; // ошибка: константа ccc<0> явно определена через саму себя
template<int X> int const rec = X==0 ? 0 : 1 + rec<(X ? 0 : 0)>; // поэтому прибегнем к трюку, сделаем вид, что параметр вычисляется
template<int X> int const err = X==0 ? 0 : 1 + err<(X ? X : X)>;

template<int X> int constexpr eee = X==0 ? 0 : 1 + eee<(X ? X : X)>;
template<int X> int const inf = X==0 ? 0 : 1+inf<X-1>;

int n[num<27>];
int r[rec<10>];
//int e[err<10>]; // это не константа времени компиляции, а статическая переменная
//int e[eee<10>]; // зацикливание в constexpr

int main() {
  cout << num<27> << endl; // 111 - честно вычислил рекурсию
  cout << rec<10> << endl; // 1 - честно вычислил рекурсию
  cout << err<10> << endl; // 1 - особенности инициализации статической переменной
//cout << eee<10> << endl; // ошибка: зацикливание в constexpr
//cout << inf<10> << endl; // ошибка: исчерпание глубины рекурсии, inf<-899>
}
Перекуём баги на фичи!
Re[2]: Мета-рекурсия
От: B0FEE664  
Дата: 07.08.15 09:48
Оценка: -1
Здравствуйте, ArtDenis, Вы писали:

Ш>>Считаем число бит в беззнаковом типе.


AD>В дополнении к другим придиркам тоже добавлю что Val=UInt(-1) лучше заменить Val=UInt(~0). Так "более" правильно, т.к. представление отрицательных чисел может быть разным (в случае представления под названием "код со сдвигом" твой код будет выдавать некорректный результат).


А чем Val=UInt(~0) лучше Val=UInt(-1)?
UInt(-1) гарантированно даст наибольшее целое влезающие в беззнаковый UInt. А разве можно сказать то же про UInt(~0)?
И каждый день — без права на ошибку...
Re[3]: Мета-рекурсия
От: ArtDenis Россия  
Дата: 07.08.15 13:25
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>UInt(-1) гарантированно даст наибольшее целое влезающие в беззнаковый UInt.

С какой это стати?

BFE>А разве можно сказать то же про UInt(~0)?

Сложно сказать. Надо посмотреть что прописано в стандарте насчёт каста (int)~0 к типу unsigned long long. Возможно что и ~0 тоже не катит для данного случая.
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[4]: Мета-рекурсия
От: B0FEE664  
Дата: 07.08.15 13:37
Оценка:
Здравствуйте, ArtDenis, Вы писали:

BFE>>UInt(-1) гарантированно даст наибольшее целое влезающие в беззнаковый UInt.

AD>С какой это стати?

Следствие из 4.7/2 стандарта.

BFE>>А разве можно сказать то же про UInt(~0)?

AD>Сложно сказать.
Вот именно.

AD>Надо посмотреть что прописано в стандарте насчёт каста (int)~0 к типу unsigned long long. Возможно что и ~0 тоже не катит для данного случая.

Прежде чем к касту переходить, надо выяснить что даст ~0.
И каждый день — без права на ошибку...
Re[2]: Мета-рекурсия
От: Шахтер Интернет  
Дата: 07.08.15 15:29
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Шахтер, Вы писали:


Ш>>Теперь некоторые вещи можно упростить.


К>Грабельки. Не надо такое тащить в продакшен.


К>http://ideone.com/a83SVo

К>
К>#include <iostream>
К>using namespace std;

К>template<int X> int const num = X==1 ? 0 : 1 + num<X%2 ? X*3+1 : X/2>;
К>//template<int X> int const ccc = X==0 ? 0 : 1 + ccc<0>; // ошибка: константа ccc<0> явно определена через саму себя
К>template<int X> int const rec = X==0 ? 0 : 1 + rec<(X ? 0 : 0)>; // поэтому прибегнем к трюку, сделаем вид, что параметр вычисляется
К>template<int X> int const err = X==0 ? 0 : 1 + err<(X ? X : X)>;

К>template<int X> int constexpr eee = X==0 ? 0 : 1 + eee<(X ? X : X)>;
К>template<int X> int const inf = X==0 ? 0 : 1+inf<X-1>;

К>int n[num<27>];
К>int r[rec<10>];
К>//int e[err<10>]; // это не константа времени компиляции, а статическая переменная
К>//int e[eee<10>]; // зацикливание в constexpr

К>int main() {
К>  cout << num<27> << endl; // 111 - честно вычислил рекурсию
К>  cout << rec<10> << endl; // 1 - честно вычислил рекурсию
К>  cout << err<10> << endl; // 1 - особенности инициализации статической переменной
К>//cout << eee<10> << endl; // ошибка: зацикливание в constexpr
К>//cout << inf<10> << endl; // ошибка: исчерпание глубины рекурсии, inf<-899>
К>}
К>


Скобки забыл поставить в тернарном операторе. Это так и было задумано?
В целом я так и не понял, чего ты добивался.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.