Setting Policies at Namespace or Translation Unit Scope

| Home | Libraries | People | FAQ | More

#### Setting Policies at Namespace or Translation Unit Scope

Sometimes what you want to do is just change a set of policies within the current scope: the one thing you should not do in this situation is use the configuration macros, as this can lead to "One Definition Rule" violations. Instead this library provides a pair of macros especially for this purpose.

Let's consider the special functions first: we can declare a set of forwarding functions that all use a specific policy using the macro BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(Policy). This macro should be used either inside a unique namespace set aside for the purpose, or an unnamed namespace if you just want the functions visible in global scope for the current file only.

Suppose we want `C::foo()` to behave in a C-compatible way and set `::errno` on error rather than throwing any exceptions.

We'll begin by including the needed header:

    #include <boost/math/special_functions.hpp>

Open up the "C" namespace that we'll use for our functions, and define the policy type we want: in this case one that sets ::errno rather than throwing exceptions. Any policies we don't specify here will inherit the defaults:

    namespace C{

    using namespace boost::math::policies;

    typedef policy<
       domain_error<errno_on_error>,
       pole_error<errno_on_error>,
       overflow_error<errno_on_error>,
       evaluation_error<errno_on_error>
    > c_policy;

All we need do now is invoke the BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS macro passing our policy type as the single argument:

    BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(c_policy)

    } // close namespace C

We now have a set of forwarding functions defined in namespace C that all look something like this:

    template <class RealType>
    inline typename boost::math::tools::promote_args<RT>::type
       tgamma(RT z)
    {
       return boost::math::tgamma(z, c_policy());
    }

So that when we call `C::tgamma(z)` we really end up calling `boost::math::tgamma(z, C::c_policy())`:

    int main()
    {
       errno = 0;
       std::cout << "Result of tgamma(30000) is: "
          << C::tgamma(30000) << std::endl;
       std::cout << "errno = " << errno << std::endl;
       std::cout << "Result of tgamma(-10) is: "
          << C::tgamma(-10) << std::endl;
       std::cout << "errno = " << errno << std::endl;
    }

Which outputs:

    Result of C::tgamma(30000) is: 1.#INF
    errno = 34
    Result of C::tgamma(-10) is: 1.#QNAN
    errno = 33

This mechanism is particularly useful when we want to define a project-wide policy, and don't want to modify the Boost source or set - possibly fragile and easy to forget - project wide build macros.

The same mechanism works well at file scope as well, by using an unnamed namespace, we can ensure that these declarations don't conflict with any alternate policies present in other translation units:

    #include <boost/math/special_functions.hpp>

    namespace {

    using namespace boost::math::policies;

    typedef policy<
       domain_error<errno_on_error>,
       pole_error<errno_on_error>,
       overflow_error<errno_on_error>,
       evaluation_error<errno_on_error>
    > c_policy;

    BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(c_policy)

    } // close unnamed namespace

    int main()
    {
       errno = 0;
       std::cout << "Result of tgamma(30000) is: "
          << tgamma(30000) << std::endl;
       std::cout << "errno = " << errno << std::endl;
       std::cout << "Result of tgamma(-10) is: "
          << tgamma(-10) << std::endl;
       std::cout << "errno = " << errno << std::endl;
    }

Handling the statistical distributions is very similar except that now the macro BOOST_MATH_DECLARE_DISTRIBUTIONS accepts two parameters: the floating point type to use, and the policy type to apply. For example:

    BOOST_MATH_DECLARE_DISTRIBUTIONS(double, mypolicy)

Results a set of typedefs being defined like this:

    typedef boost::math::normal_distribution<double, mypolicy> normal;

The name of each typedef is the same as the name of the distribution class template, but without the "_distribution" suffix.

Suppose we want a set of distributions to behave as follows:

- Return infinity on overflow, rather than throwing an exception.
- Don't perform any promotion from double to long double internally.
- Return the closest integer result from the quantiles of discrete distributions.

We'll begin by including the needed header:

    #include <boost/math/distributions.hpp>

Open up an appropriate namespace for our distributions, and define the policy type we want. Any policies we don't specify here will inherit the defaults:

    namespace my_distributions{

    using namespace boost::math::policies;

    typedef policy<
       // return infinity and set errno rather than throw:
       overflow_error<errno_on_error>,
       // Don't promote double -> long double internally:
       promote_double<false>,
       // Return the closest integer result for discrete quantiles:
       discrete_quantile<integer_round_nearest>
    > my_policy;

All we need do now is invoke the BOOST_MATH_DECLARE_DISTRIBUTIONS macro passing the floating point and policy types as arguments:

    BOOST_MATH_DECLARE_DISTRIBUTIONS(double, my_policy)

    } // close namespace my_namespace

We now have a set of typedefs defined in namespace my_namespace that all look something like this:

    typedef boost::math::normal_distribution<double, my_policy> normal;
    typedef boost::math::cauchy_distribution<double, my_policy> cauchy;
    typedef boost::math::gamma_distribution<double, my_policy> gamma;
    // etc

So that when we use my_namespace::normal we really end up using `boost::math::normal_distribution<double, my_policy>`:

    int main()
    {
       //
       // Start with something we know will overflow:
       //
       my_distributions::normal norm(10, 2);
       errno = 0;
       std::cout << "Result of quantile(norm, 0) is: "
          << quantile(norm, 0) << std::endl;
       std::cout << "errno = " << errno << std::endl;
       errno = 0;
       std::cout << "Result of quantile(norm, 1) is: "
          << quantile(norm, 1) << std::endl;
       std::cout << "errno = " << errno << std::endl;
       //
       // Now try a discrete distribution:
       //
       my_distributions::binomial binom(20, 0.25);
       std::cout << "Result of quantile(binom, 0.05) is: "
          << quantile(binom, 0.05) << std::endl;
       std::cout << "Result of quantile(complement(binom, 0.05)) is: "
          << quantile(complement(binom, 0.05)) << std::endl;
    }

Which outputs:

    Result of quantile(norm, 0) is: -1.#INF
    errno = 34
    Result of quantile(norm, 1) is: 1.#INF
    errno = 34
    Result of quantile(binom, 0.05) is: 1
    Result of quantile(complement(binom, 0.05)) is: 8

This mechanism is particularly useful when we want to define a project-wide policy, and don't want to modify the Boost source or set - possibly fragile and easy to forget - project wide build macros.

| Note
There is an important limitation to note: you can not use the macros BOOST_MATH_DECLARE_DISTRIBUTIONS and BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS in the same namespace, as doing so creates ambiguities between functions and distributions of the same name.

As before, the same mechanism works well at file scope as well: by using an unnamed namespace, we can ensure that these declarations don't conflict with any alternate policies present in other translation units:

    #include <boost/math/distributions.hpp>

    namespace {

    using namespace boost::math::policies;

    typedef policy<
       // return infinity and set errno rather than throw:
       overflow_error<errno_on_error>,
       // Don't promote double -> long double internally:
       promote_double<false>,
       // Return the closest integer result for discrete quantiles:
       discrete_quantile<integer_round_nearest>
    > my_policy;

    BOOST_MATH_DECLARE_DISTRIBUTIONS(double, my_policy)

    } // close namespace my_namespace

    int main()
    {
       //
       // Start with something we know will overflow:
       //
       normal norm(10, 2);
       errno = 0;
       std::cout << "Result of quantile(norm, 0) is: "
          << quantile(norm, 0) << std::endl;
       std::cout << "errno = " << errno << std::endl;
       errno = 0;
       std::cout << "Result of quantile(norm, 1) is: "
          << quantile(norm, 1) << std::endl;
       std::cout << "errno = " << errno << std::endl;
       //
       // Now try a discrete distribution:
       //
       binomial binom(20, 0.25);
       std::cout << "Result of quantile(binom, 0.05) is: "
          << quantile(binom, 0.05) << std::endl;
       std::cout << "Result of quantile(complement(binom, 0.05)) is: "
          << quantile(complement(binom, 0.05)) << std::endl;
    }