Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

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 (for example, a C namespace for a C-style policy), 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 for our function:

#include <boost/math/special_functions.hpp>
//using boost::math::tgamma; // Not needed because using C::tgamma.

Open up the "C" namespace that we'll use for our functions, and define the policy type we want: in this case a C-style one that sets ::errno and returns a standard value, rather than throwing exceptions.

Any policies we don't specify here will inherit the defaults.

namespace C
{ // To hold our C-style policy.
  //using namespace boost::math::policies; or explicitly:
  using boost::math::policies::policy;

  using boost::math::policies::domain_error;
  using boost::math::policies::pole_error;
  using boost::math::policies::overflow_error;
  using boost::math::policies::evaluation_error;
  using boost::math::policies::errno_on_error;

  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 c_policy 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;
   cout << "Result of tgamma(30000) is: "
      << C::tgamma(30000) << endl; // Note using C::tgamma
   cout << "errno = " << errno << endl; // errno = 34
   cout << "Result of tgamma(-10) is: "
      << C::tgamma(-10) << endl;
   cout << "errno = " << errno << endl; // errno = 33, overwriting previous value of 34.
}

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 to set project wide build macros (possibly fragile and easy to forget).

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>
// using boost::math::tgamma; // Would create an ambiguity between
// 'double boost::math::tgamma<int>(T)' and
// 'double 'anonymous-namespace'::tgamma<int>(RT)'.

namespace mymath
{ // unnamed

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)

So that when we call mymath::tgamma(z), we really end up calling boost::math::tgamma(z, anonymous-namespace::c_policy()).

} // close unnamed namespace

int main()
{
   errno = 0;
   cout << "Result of tgamma(30000) is: "
      << mymath::tgamma(30000) << endl;
      // tgamma in unnamed namespace in this translation unit (file) only.
   cout << "errno = " << errno << endl;
   cout << "Result of tgamma(-10) is: "
      << mymath::tgamma(-10) << endl;
   cout << "errno = " << errno << endl;
   // Default tgamma policy would throw an exception, and abort.
}

Handling policies for 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, my_policy)

Results a set of typedefs being defined like this:

typedef boost::math::normal_distribution<double, my_policy> 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:

We'll begin by including the needed header for all the distributions:

#include <boost/math/distributions.hpp>

Open up an appropriate namespace, calling it my_distributions, 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;
  // using boost::math::policies::errno_on_error; // etc.

  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 type double and policy types my_policy as arguments:

BOOST_MATH_DECLARE_DISTRIBUTIONS(double, my_policy)

} // close namespace my_namespace

We now have a set of typedefs defined in namespace my_distributions 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_distributions::normal we really end up using boost::math::normal_distribution<double, my_policy>:

int main()
{
   // Construct distribution with something we know will overflow
  // (using double rather than if promoted to long double):
   my_distributions::normal norm(10, 2);

   errno = 0;
   cout << "Result of quantile(norm, 0) is: "
      << quantile(norm, 0) << endl; // -infinity.
   cout << "errno = " << errno << endl;
   errno = 0;
   cout << "Result of quantile(norm, 1) is: "
      << quantile(norm, 1) << endl; // +infinity.
   cout << "errno = " << errno << endl;

   // Now try a discrete distribution.
   my_distributions::binomial binom(20, 0.25);
   cout << "Result of quantile(binom, 0.05) is: "
      << quantile(binom, 0.05) << endl; // To check we get integer results.
   cout << "Result of quantile(complement(binom, 0.05)) is: "
      << quantile(complement(binom, 0.05)) << 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 project wide build macros (possibly fragile and easy to forget).

[Note] 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> // All distributions.
// using boost::math::normal; // Would create an ambiguity between
// boost::math::normal_distribution<RealType> boost::math::normal and
// 'anonymous-namespace'::normal'.

namespace
{ // anonymous or unnamed (rather than named as in policy_eg_6.cpp).

  using namespace boost::math::policies;
   // using boost::math::policies::errno_on_error; // etc.
  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()
{
   // Construct distribution with something we know will overflow.
   normal norm(10, 2); // using 'anonymous-namespace'::normal
   errno = 0;
   cout << "Result of quantile(norm, 0) is: "
      << quantile(norm, 0) << endl;
   cout << "errno = " << errno << endl;
   errno = 0;
   cout << "Result of quantile(norm, 1) is: "
      << quantile(norm, 1) << endl;
   cout << "errno = " << errno << endl;
   //
   // Now try a discrete distribution:
   binomial binom(20, 0.25);
   cout << "Result of quantile(binom, 0.05) is: "
      << quantile(binom, 0.05) << endl;
   cout << "Result of quantile(complement(binom, 0.05)) is: "
      << quantile(complement(binom, 0.05)) << endl;
}

PrevUpHomeNext