В этом разделе мы предоставим «рецепт» для добавления новой специальной функции в эту библиотеку, чтобы облегчить жизнь будущим авторам, желающим внести свой вклад. Мы предположим, что функция возвращает один результат с плавающей точкой и принимает два аргумента с плавающей точкой. Для изложения мы дадим функции имя<my_special
>.
Обычно реализация такой функции разбивается на два слоя — общедоступный пользовательский слой и внутренний уровень реализации, выполняющий фактическую работу. Слой реализации объявляется внутри пространства имен<detail
>и имеет простую подпись:
namespace boost { namespace math { namespace detail {
template <class T, class Policy>
T my_special_imp(const T& a, const T&b, const Policy& pol)
{
}
}}}
Мы вернемся к тому, что может войти в реализацию позже, но сначала давайте посмотрим на пользовательский уровень. Это состоит из двух перегрузок функции, с аргументомПолитикаи без него:
namespace boost{ namespace math{
template <class T, class U>
typename tools::promote_args<T, U>::type my_special(const T& a, const U& b);
template <class T, class U, class Policy>
typename tools::promote_args<T, U>::type my_special(const T& a, const U& b, const Policy& pol);
}}
Обратите внимание, что каждый аргумент имеет различный тип шаблона - это позволяет использовать аргументы смешанного типа - тип возврата вычисляется из класса признаков и является «общим типом» всех аргументов после того, как любые целочисленные аргументы были продвинуты на тип<double
>.
Осуществление неполитической перегрузки тривиально:
namespace boost{ namespace math{
template <class T, class U>
inline typename tools::promote_args<T, U>::type my_special(const T& a, const U& b)
{
return my_special(a, b, policies::policy<>();
}
}}
Реализация другой перегрузки несколько сложнее, так как есть некоторое метапрограммирование, но с точки зрения времени выполнения все еще является функцией пересылки одной строки. Вот комментарии, объясняющие, что делает каждая строка:
namespace boost{ namespace math{
template <class T, class U, class Policy>
inline typename tools::promote_args<T, U>::type my_special(const T& a, const U& b, const Policy& pol)
{
BOOST_FPU_EXCEPTION_GUARD
typedef typename tools::promote_args<T, U>::type result_type;
typedef typename policies::evaluation<result_type, Policy>::type value_type;
typedef typename policies::normalise<
Policy,
policies::promote_float<false>,
policies::promote_double<false>,
policies::discrete_quantile<>,
policies::assert_undefined<> >::type forwarding_policy;
return policies::checked_narrowing_cast<result_type, forwarding_policy>(
detail::my_special_imp(
static_cast<value_type>(a),
static_cast<value_type>(x),
forwarding_policy()),
"boost::math::my_special<%1%>(%1%, %1%)");
}
}}
Сейчас мы почти на месте, нам просто нужно прояснить детали уровня реализации:
namespace boost { namespace math { namespace detail {
template <class T, class Policy>
T my_special_imp(const T& a, const T&b, const Policy& pol)
{
}
}}}
Следующие руководящие принципы указывают, что (кроме базовой арифметики) может пойти в реализации:
- Условия ошибки (например, плохие аргументы) должны обрабатываться путем вызова одного изобработчиков ошибок на основе политики..
- Звонки в стандартные библиотечные функции должны быть сделаны неквалифицированными (это позволяет зависимому от аргумента поиску найти стандартные библиотечные функции для определяемых пользователем типов плавающих точек, таких как те изBoost.Multiprecision). Кроме того, макрос<
BOOST_MATH_STD_USING
>должен появиться в начале функции (заметьте, что после этого нет полуколона!), так что все математические функции в<namespace
std
>видны в текущем объеме.
- Звонки на другие специальные функции должны выполняться в качестве полностью квалифицированных вызовов и включать в качестве последнего аргумента политический параметр, например<
boost::math::tgamma(a,pol)
>.
- Там, где это возможно, для оценки последовательностей, непрерывных фракций, полиномов или нахождения корней следует использовать одну из функцийкотельной пластины. В любом случае, после любого итеративного метода следует проверить, что количество итераций не превысило максимум, указанный вПолитикетипа, и если оно действительно закончилось в результате превышения максимума, то следует вызвать соответствующего обработчика ошибок (см. существующий код для примеров).
- Численные константы, такие как π и т.д., должны быть получены посредством вызовасоответствующей функции, например:<
constants::pi<T>()
>.
- Там, где используются таблицы коэффициентов (например, для рациональных приближений), следует позаботиться о том, чтобы они были инициализированы при запуске программы для обеспечения безопасности потока при использовании определенных пользователем типов чисел. См., например, использование<
erf_initializer
>вerf.hpp.
Вот некоторые другие полезные внутренние функции: