четверг, 22 апреля 2010 г.

Более-менее...

Все таки мы неправильно как-то живем. Нашими интерфейсами руководят низменные железные ограничения. Вот например - часто в интерфейсах нас просят ввести 0, если ограничений не предполагается. Но, по моему 0 - это ноль, это совершенно не похоже на бесконечность. И если с точки зрения архитектуры удобно делить значения на нулевые и ненулевые, то с точки зрения интерфейса - это нелогично.

И размышляя таким образом о физической природе чисел я дошел до стандартной библиотеки...

C++ конечно, язык со строгой типизацией - никуда не денешься.

Сравнение знакового и беззнакового числа нелогично только с точки зрения ограниченности платформы. Мы знаем, что компилятор пытается привести числа к одному типу с чем испытывает многочисленные проблемы.

Хотя с точки зрения математики int(-10) определенно меньше чем uint(10). А int(-2000000000) определенно меньше uint(3000000000). Почему же нам запрещают их сравнивать?

То же самое касается min/max, которые вообще требуют строго одинаковых типов - негибко.
Гибкость с типами позволяет, кроме того, гораздо шире трактовать понятие целое число.

И это даже можно реализовать...
Только почему-то наткнулся на странную фигню - компилятор говорит что нельзя реализовать шаблонный operator < вне класса... Ну да ладно, продемонстрирую идею на примере min Для начала напишем тестик:
BOOST_AUTO_TEST_CASE(minimal)
{
BOOST_REQUIRE_EQUAL(min(-10, 10), -10);
BOOST_REQUIRE_EQUAL(min(-2000000000, 3000000000), -2000000000);
BOOST_REQUIRE_EQUAL(min(3000000000, -2000000000), -2000000000);
}
Первая строка - вызывает стандартный min, Но последующие две строки под него никак не подходят.
И тут вступает наша реализация:
template<typename A, typename B>
struct min_type_selector {
 typedef typename mpl::if_c<is_signed<A>::value,
  typename mpl::if_c<is_signed<B>::value,
   typename mpl::if_c<(sizeof(A) > sizeof(B)), A, B>::type, A>::type,
  typename mpl::if_c<is_signed<B>::value,
   B, typename mpl::if_c<(sizeof(A) > sizeof(B)), A, B>::type>::type
  >::type type;

 // Переполнение результата может вызвать только беззнаковое значение,
 // По размеру не меньше резулььтата
 enum { b_can_overload = ((sizeof(B) >= sizeof(type) && is_unsigned<B>::value)) };
 enum { a_can_overload = ((sizeof(A) >= sizeof(type) && is_unsigned<A>::value)) };

 static type highest() { return numeric::bounds<type>::highest(); }
};

template<typename A, typename B>
typename min_type_selector<A, B>::type min(const A &a, const B &b) {
 typedef min_type_selector<A, B> TS;
 if (TS::b_can_overload && b > numeric_cast<B>(TS::highest()))
  return numeric_cast<typename TS::type>(a);
 if (TS::a_can_overload && a > numeric_cast<A>(TS::highest()))
  return numeric_cast<typename TS::type>(b);
 return min(numeric_cast<typename TS::type>(a), numeric_cast<typename TS::type>(b));
}
Вот...

Кому нужно сравнивать такие большие числа - не очень то понятно, но может быть это пригодиться для того, чтобы постоянно не приводить два сравниваемых числа к одному типу?

А может и проще можно написать?