その後のその後

iOSエンジニア 堤 修一のブログ github.com/shu223

【oFセミナーメモ1】 boostライブラリの使い方

デジタルアートセミナー#3 openFrameworksで学ぶ、クリエイティブ・コーディング』という一泊二日のセミナーに参加しています。


最終的なまとめは最後に書くとして、とりいそぎ本日受けたセッションのメモを載せていきます。

セッション1 : C++テクニック

講師 : 堀口淳史、藤本直明
openFrameworksを本格的に使う上で避けて通れないC++のテクニックを学びます。
今回は、boostライブラリの使い方について学びます。

環境

  • MacOS X 10.9.5
  • Xcode 6.1 GMAIL.COM seed 2
  • oF osx 0.8.4
  • boost 1.56.0

boostとは

  • C++の高度で便利なライブラリ
    • STLを拡張
  • oFにpocoってのがもともと入っている
    • pocoとは設計思想が違う
    • boostはテンプレートを駆使
    • STLと違ってC++の開発環境に始めから入っていない
  • boostで書かれた過去の資産を利用できるようになる
  • ヘッダだけインクルードして使うライトな使い方もある
    • 正規表現とかはコンパイルしないと使えない
  • 自分でコンパイルしてMac環境で動かすのがなかなかハードルが高い

課題

  • oFは32ビットバイナリ、boostは普通にインストールすると64ビットバイナリ
  • oFとboostを同時に利用しようとすると、それぞれlibstdc++, libc++を使おうとしててこのあたりがリンクエラーとかの問題になる

「oFで動くMacのboostバイナリ」をどう作るか?

解決策

  • oFを64bitバイナリとしてコンパイルするのは簡単ではない

→ boost のコンパイル時に x86 を address-modelに `32_64` を指定する

  • oFはlibstdc++利用前提、libc++を利用するとコンパイルできない
  • boost はlibstdc++でもlibc++でもコンパイルできる

→ boost のコンパイル時に cxxflags と linflags に `-stdlib=libstdc++` を指定する

libstdc++とlibc++

  • libstdc++は GCCと共に開発される古くから使われている標準ライブラリ
    • GPLライセンス
  • libc++は LLVM/Clangと共に開発された新しい標準ライブラリ
    • MIT ライセンスと UIUC ライセンス


(2014.10.13追記)libstdc++のライセンスについては、コメント欄より下記のようにご指摘いただきました。

libstdc++のライセンスがGPLというのは少し誤解を招いてしまいかねないです。

というのも、libstdc++は特別な条項(GCC Runtime Library Exception)が追加されているからです。これにより、libstdc++を使用するアプリケーションを作成しても、それを公開する際にGPLを適用する必要がありません。その点で、通常のGPLとは大きく異なります。

oFで手っ取り早く使う

  • poco/include/ 配下に boost フォルダのヘッダを丸ごとつっこんじゃえば、パスが通ってるので、ヘッダだけならすぐに使えるようになる(行儀悪い)
  • shared pointer とかはそのまま使える

スマートポインタ

普通のポインタ
ofImage * mTestImage;
mTestImage = new ofImage( "test.jpg" );
delete mTestImage;

new したら delete が必要。


スマートポインタの場合(ofPtr は oF のスマートポインタ)

ofPtr < ofImage > mTestImageSP;
mTestImage = ofPtr< ofImage >( new ofImage( "test.jpg" ) );

delete不要。


boostだと、

boost::shared_ptr< ofImage >

って感じでスマートポインタを使える


スマートポインタに NULL 代入はできないので、

mTestImageSP.reset();

で内部でデストラクタが呼ばれて NULL と同じ状態になる。( `if (mTestImageSP)` でfalseになる)

boostインストール

https://github.com/toolbits/boost_1_56_0_xcode610_universal_binary

boost_libstdc++.dmgを解凍

  • ヘッダだけを使う場合は、includeのフォルダをパス通ってるとこにコピーする
  • libはコンパイル済みのバイナリ
    • Xcode6, MacOS X 10.9で動くようにコンパイルしたもの
  • 一番よく入れるのが、`/usr/local`
    • OS標準以外のあとから追加したライブラリとかを置く場所として(macでは)よく使われる
cd /user/local
open .

この配下にコピー

サンプルプロジェクト

サンプルを動かしつつ、boostの機能を紹介。

https://github.com/toolbits/of_plus_boost_2014seminar

サンプル1: boosted

両端のスペースをカットする文字列処理。ヘッダの機能だけ使用。

boost::algorithm::trim()
void ofApp::setup(){
    std::string str;
    
    str = " Hello boost ";
    std::cout << str << std::endl;
    
    boost::algorithm::trim(str);
    std::cout << str << std::endl;
}

サンプル2: boost_algorithm

文字列処理のサンプル。これらもヘッダだけで可能。


両端の空白を削除

boost::algorithm::trim(str);


カンマで文字列を分割

boost::algorithm::split(vec, str, boost::is_any_of(","));
for (it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << std::endl;
}


分割した各文字列の両端の空白を削除

for (it = vec.begin(); it != vec.end(); ++it) {
    boost::algorithm::trim(*it);
}
for (it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << std::endl;
}


各文字列を|を区切りにして連結

str = boost::algorithm::join(vec, "|");


文字列の置き換え

boost::algorithm::replace_all(str, "|", " / ");


文字列を置き換えた結果を返す

str = boost::algorithm::replace_first_copy(std::string("C++ source code"), "C++", "boooooooooooooooost");

サンプル3: boost_regex

正規表現のサンプル。これは要バイナリ。このサンプルが動けばboostの全機能が使えるということ。

※プロジェクトに libboost_regex.a が追加されている

boost::regex regex("[^/]+?\\.o$");
boost::match_results<std::string::const_iterator> result;


見つかった項目をすべて表示

std::string::const_iterator bit;
std::string::const_iterator eit;
bit = str.begin();
eit = str.end();
while (boost::regex_search(bit, eit, result, regex)) {
    std::cout << "match = " << result.str() << std::endl;
    bit = result[0].second;
}


見つかった項目をすべて置換

str = boost::regex_replace(str, regex, "********.o");


※ofUtilsに同様の文字列処理機能もあるので一度眺めておくと良い

サンプル4: boost_format_lexical_cast

数値を文字列にキャスト。ヘッダだけでOK。


番号で指定された通りに値を文字列化

str = (boost::format("%1% %2% %3%") % 1 % "abc" % 3.14).str();


printf のフォーマット文も利用可能

str = (boost::format("%06X (hex)") % 12648430).str();
str = (boost::format("%d (dec)") % 0xDEADBEEF).str();


lexical_cast を利用した整数から文字列への変換

str = boost::lexical_cast<std::string>(141421356);
std::cout << str << " (string)" << std::endl;
std::cout << "---- ---- ---- ----" << std::endl;


lexical_cast を利用した文字列から整数への変換

ival = boost::lexical_cast<int>(str);


lexical_cast を利用した文字列から実数への変換

str = "0.12345";
dval = boost::lexical_cast<double>(str);


lexical_cast を利用した不正な文字列から実数への変換

str = "0.12???";
try {
    dval = boost::lexical_cast<double>(str);
}
catch (boost::bad_lexical_cast& e) {
    dval = NAN;
}

サンプル5: boost_thread

スレッドをつくる

  • libboost_system.a
  • libboost_thread.a

サンプル6: boost_mutex

変数へのアクセスを排他にするためのmutex

  • libboost_system.a
  • libboost_thread.a
boost::mutex _mutex;
// カウント変数を増加
_mutex.lock();
++_count;
_mutex.unlock();
// カウント値を取得
_mutex.lock();
count = _count;
_mutex.unlock();
// lock() / unlock() の替わりに lock_guard を利用しても同じ
boost::lock_guard<boost::mutex> guard(_mutex);

// カウント変数を減少
--_count;

サンプル7: boost_lock

shared_mutex を使うと、マルチスレッド処理において、read/writeをいい感じにロックしてくれる。

boost::shared_lock<boost::shared_mutex> rlock(_mutex);


書き込みロック(unique_lock)は1つのスレッドからだけ取れる

// カウント変数を増加
{
    boost::unique_lock<boost::shared_mutex> wlock(_mutex);
    
    ++_count;
}


読みこみロック(shared_lock)は何スレッドからでもとれる

boost::shared_lock<boost::shared_mutex> rlock(_mutex);
ofColor color;

// カウント値を表示
color.setHsb(abs(_count) % 255, 255, 255);
ofBackground(color);
ofDrawBitmapString((boost::format("%1%") % _count).str(), 10, 50);