Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

在名字空間或編譯單元作用域設置策略(Setting Policies at Namespace or Translation Unit Scope)

有時你想做的事情是在當前作用域內改變一些策略:使用配置宏這件事是不能做的,因為這將導致違反「只定義一次規則(One Definition Rule)」。取而代之的是這個庫提供了一些特定的宏來滿足這個需要。

讓我們首先考慮特殊函數:我們使用宏BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(Policy)來定義一些使用特定策略的函數。這個宏要麼用在滿足這個目的的獨一無二的名字空間,要麼用在一個匿名的名字空間中,如果你只想這個函數在當前文件的全局作用域內可見。

例如我們希望C::foo() 以一種 C-兼容的方式來工作並在出錯時設置::errno 而不是拋出異常。

我們將包含需要的頭文件:

#include <boost/math/special_functions.hpp>

打開我們的函數將要使用的「C」名字空間,並定義我們需要的策略類型:在這種情況下是設置 ::errno 而不是拋出異常的策略。我們在這裡沒有指定的策略都會繼承缺省值:

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;

現在我們需要做的就是將我們的策略類型作為唯一的一個參數調用宏 BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS :

BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(c_policy)

} // 關閉名字空間 C

現在我們需要定義一些在名字空間 C 中前向聲明的函數(forwarding functions),這些函數看起來是這樣的:

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

所以當我們調用函數C::tgamma(z)時, 我們將最終調用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;
}

輸出結果為:

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

當我們想定義一個工程範圍的策略( project-wide policy),並且不想修改Boost源代碼或設置-可能很脆弱-且很容易忘記-工程範圍編譯宏( project wide build macros)時,這種機制就很有用了。

同樣的機制在文件作用域(file scope)也運用良好,通過使用匿名名字空間,我們可以確保這些聲明不會與任何的已出現在當前編譯單元(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)

} //關閉匿名名字空間

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;
}

處理統計分佈也是很類似的,除了宏 BOOST_MATH_DECLARE_DISTRIBUTIONS 接收兩個參數:使用的浮點類型,以及使用的策略類型。例如:

BOOST_MATH_DECLARE_DISTRIBUTIONS(double, mypolicy)

效果是一系列類似於下面的typedef:

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

每個typedef的名字與這個分佈類型的類模板的名字相同,但沒有使用"_distribution" 後綴。

假設我們需要一些行為如下面描述的分佈類型:

我們將包含一些需要的頭文件:

#include <boost/math/distributions.hpp>

為我們的分佈類型打開合適的名字空間,我們需要的策略類型。任何我們沒有指定的策略類型使用缺省的策略:

namespace my_distributions{

using namespace boost::math::policies;

typedef policy<
   // 返回無限值並設置 errno 而不是拋出異常:
   overflow_error<errno_on_error>,
   // 在內部不進行從 double -> long double 的類型提升:
   promote_double<false>,
   // 從離散分佈的分位點函數中返回最接近的整數值:
   discrete_quantile<integer_round_nearest>
> my_policy;

現在我們需要做的就是使用使用浮點類型和策略作為參數調用宏 BOOST_MATH_DECLARE_DISTRIBUTIONS :

BOOST_MATH_DECLARE_DISTRIBUTIONS(double, my_policy)

} // 關閉 my_namespace 名字空間

在名字空間my_namespace中, 現在我們有一系列的typedef,類似於下面:

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

因此,當我們使用 my_namespace::normal時,我們最終使用boost::math::normal_distribution<double, my_policy>:

int main()
{
   //
   // 以一些我們知道會產生溢出錯誤的代碼開始:
   //
   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;
   //
   // 嘗試一個離散分佈:
   //
   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;
}

輸出結果為:

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

當我們想定義一個工程範圍的策略( project-wide policy),並且不想修改Boost源代碼或設置-可能很脆弱-且很容易忘記-工程範圍編譯宏( project wide build macros)時,這種機制就很有用了。

[Note] 注意

有一個很重要的需要注意:你不能在同一個名字空間中使用 BOOST_MATH_DECLARE_DISTRIBUTIONS 和BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS因為這樣做會在相同的函數名和分佈類型名字之間產生二義性。

和前面一樣,在文件作用域這個機制也運行良好:通過使用匿名名字空間,我們可以確保這些聲明不會與已出現在其它編譯單元中(translation units)的其它的策略類型相衝突:

#include <boost/math/distributions.hpp>

namespace {

using namespace boost::math::policies;

typedef policy<
   // 返回無限值並設置 errno 而不是拋出異常:
   overflow_error<errno_on_error>,
   // 在內部不進行從 double -> long double 的類型提升:
   promote_double<false>,
   // 從離散分佈的分位點函數中返回最接近的整數值:
   discrete_quantile<integer_round_nearest>
> my_policy;

BOOST_MATH_DECLARE_DISTRIBUTIONS(double, my_policy)

} // 關閉名字空間 namespace my_namespace

int main()
{
   //
   // 以一些我們知道會產生溢出錯誤的代碼開始:
   //
   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;
   //
   // 嘗試一個離散分佈:
   //
   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;
}


PrevUpHomeNext