Borland Developer Studio 2006 / Turbo C++ 2006 から Boost を利用する

By: Hitoshi Fujii

Abstract: Borland Developer Studio 2006および Turbo C++ 2006 から Boost を利用する際のポイントを紹介します。この記事は、第3回デベロッパーキャンプのプレゼンテーション資料に対する補足記事です。


第3回デベロッパーキャンプのプレゼンテーション資料では、BCC32 5.8.x で Boost を利用する方法として基本的なコードを紹介しています。この記事では、さらに少し高度な利用例を解説します。

    randam / array

boost::array の使用例として、乱数を array に設定する方法、 for 文を用いずにarray の内容を TMemo オブジェクトの Lines プロパティに設定する方法を解説します。

boost::array は コンテナとしてのメンバーが実装されています。そのため、boost::array へは、標準C++ で実装されているコンテナを操作する関数を利用して、値を設定できます。

以下のコードは、int 型の 10 個の要素を持つ boost::array を std::generate 用いて操作する例です。dice は boost::variate_generator型の関数オブジェクトです。

  boost::array<int, 10> ar;
  std::generate(ar.begin(), ar.end(), dice);

次に、VCL クラスの取り扱い方法です。C++Builder では、コンポーネントに関連する処理にVCL クラスを用います。これらのライブラリは Delphi で作成され、Delphi との互換性を保持しているため、標準C++ のライブラリとそのままでは相互利用が難しい場合があります。

これを解決するために、インターフェイスをとりたいVCLコンポーネントやVCLクラス(例えばTMemo、TStringListなど)を内包した、コンテナクラスのテンプレートを作成します。例えば、TMemo コンポーネントに対して、boost:array の結果を差込むには、TMemo を内包したコンテナクラスのテンプレートを作成します。

//TMemo を内包したコンテナクラスのテンプレートを作成する
template <typename T>
class MemoContainer
{
  TMemo* memo;
public:
  typedef T reference;
  typedef const T const_reference;

  MemoContainer(TMemo* m) : memo(m) {}
  //push_back関数を作成し、値を Lines に Add できるようにする
  void push_back(T value) { memo->Lines->Add(AnsiString(value)); }
};

ここでは、push_back をメンバ関数に持つコンテナクラスのテンプレートを作成し、memoオブジェクトの Lines プロパティに値を設定しています。このような定義によって、イテレータによる処理が可能になります。

以下は、この手法を用いたサンプルコードです。

// -- ヘッダファイルへ記述する宣言 
#include <algorithm>
#include <iterator>

template <typename T>
class MemoContainer
{
  TMemo* memo;
public:
  typedef T reference;
  typedef const T const_reference;

  MemoContainer(TMemo* m) : memo(m) {}
  void push_back(T value) { memo->Lines->Add(AnsiString(value)); }
};

// -- フォームオブジェクトの実装部へ記述する random の実装

#include <boost/array.hpp>
#include <boost/random.hpp>

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  // random オブジェクトの生成
  boost::mt19937 gen;
  boost::uniform_int<> six(1, 6);
  boost::variate_generator<boost::mt19937&, boost::uniform_int<> > dice(gen, six);

  // int型で要素数10個のarrayオブジェクトの生成
  boost::array<int, 10> ar;

  // array オブジェクトの中身をランダムで埋める
  std::generate(ar.begin(), ar.end(), dice);

  // int を 扱える MemoContainer を生成
  MemoContainer<int> mc(Memo1);

  // back_inserter で、array の中身を MemoContainer 経由で memo オブジェクトに設定
  std::copy(ar.begin(), ar.end(), std::back_inserter(mc));
}

このような手法を採用することで、array に格納するデータ型や要素数を変更することになっても、それを利用する側ではまったく変更が発生しません。

次のような方法もまた有効です。以下のコードではint 型の引数を持つ関数addLine を宣言し、std::for_eachで走査しています。

// -- ヘッダファイルへ記述する宣言
#include <algorithm>
#include <functional>

//無名ネームスペースを利用し、スコープを翻訳単位に限定する
namespace {
  void addLine(int n) { Form1->Memo1->Lines->Add(AnsiString(n)); }
}

// -- フォームオブジェクトの実装部へ記述する random の実装
#include <boost/array.hpp>
#include <boost/random.hpp>

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  // random オブジェクトの生成
  boost::mt19937 gen;
  boost::uniform_int<> six(1, 6);
  boost::variate_generator<boost::mt19937&, boost::uniform_int<> > dice(gen, six);

  // int型で要素数10個のarrayオブジェクトの生成
  boost::array<int, 10> ar;

  // array オブジェクトの中身をランダムで埋める
  std::generate(ar.begin(), ar.end(), dice);

  // addLine を利用し、配列の中身をmemoオブジェクトに設定
  // std::ptr_funへ関数のポインタ渡し、関数オブジェクトを得ている
  std::for_each(ar.begin(), ar.end(), std::ptr_fun(addLine));
}

    リンクの問題

プレゼンテーション資料に「regex を利用する際の問題」として、リンクの問題を指摘しています。これについては、プロジェクトへ lib ファイルを追加し、手動でリンクする解決方法を紹介していますが、このほかに、BOOST_LIB_TOOLSET を用いる方法があります。今回の場合、BOOST_LIB_TOOLSET に ”bcb58”を設定することで、適切にリンクできるようになります。

#define BOOST_LIB_TOOLSET "bcb58"

また、ソースコードの移植性を考慮してBCC32だけでこの設定を有効にするには、BCC の埋め込みマクロを使ってif define で囲みます。BCC32 version 5.82 では、__BORLANDC__ の値は、0x0582 です。

#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0582)
#define BOOST_LIB_TOOLSET "bcb58"
#endif


関連情報:

第3回デベロッパーキャンプ – 資料ダウンロード


Server Response from: ETNASC04