C++er に贈るコード片

この記事は,C++ Advent Calendar jp 2010 : ATND 参加記事です.これまで C++ のコードを書いてきた中で,「あぁなるほど」と思ったコード片を紹介していきます.せっかくなのでクリスマスっぽいタイトルで.あ,クリスマスは中止でしたっけ.

各 Advent Calendar 参加記事は本家の他,C++ Advent Calendar jp 2010 でまとめています.C++ 以外は Programmer's Advent Calendar jp から探してみて下さい.まだ参加者を募ってるものもあるようです.

Type Erasure

紹介する各コードは,該当部分を見やすくするために適当に削ったり整形したりしています.

struct thread_data_base {
    
    /* 省略 */
    
    virtual ~thread_data_base();
    virtual void run()=0;
};

typedef boost::shared_ptr<thread_data_base> thread_data_ptr;
http://svn.boost.org/svn/boost/trunk/boost/thread/pthread/thread_data.hpp
template<typename F>
class thread_data : public detail::thread_data_base {
public:
    thread_data(F f_) : f(f_) {}
    
    /* 省略 */
    
    void run() {
        f();
    }
private:
    F f;
};
http://svn.boost.org/svn/boost/trunk/boost/thread/detail/thread.hpp
void* thread_proxy(void* param) {
    boost::detail::thread_data_ptr thread_info = static_cast<boost::detail::thread_data_base*>(param)->self;
    thread_info->self.reset();
    detail::set_current_thread_data(thread_info.get());
    try {
        thread_info->run();
    }
    catch(thread_interrupted const&) {}
    
    /* 省略 */
    
    return 0;
}
http://svn.boost.org/svn/boost/trunk/libs/thread/src/pthread/thread.cpp

Type Erasure のコードは,Boost.Any とか Boost.Function 辺りを見た方が良さそうですが(Boost.Function の中身は鬼畜そうなのであまりオススメできませんが),私が一番最初に Type Erasure を使っているコードを見たコードはこれでした.もっとも,Type Erasure と言う言葉自体を知ったのはかなり後になってからで,C++ テンプレートテクニックを読んだ時でしたが.

Type Erasure の使いどころは,静的型付け言語におけるダックタイピング (Type Erasure) - Life like a clown でも少し述べましたが,オブジェクトが渡されるタイミングと渡されたオブジェクトを実行するタイミングがずれる(実行する関数が異なる)場合となります.

Command パターンでは,処理を実行するために必要な環境を揃えるタイミングが,処理を実行するタイミングと異なっています.2つのタイミングの間で,プログラムはオブジェクトとしての処理要求を格納し,その引き渡しを行うわけです.タイミングに関連する要求がない場合には,Command パターンは必要ないでしょう.こういった観点から見た場合,Command オブジェクトの存在意義はタイミングにあると言っても良いくらいなのです.後で処理を行う必要があるからこそ,その時まで要求を保持しておくオブジェクトができるわけです.

Modern C++ Design

上記は,私が Modern C++ Design を読み進められるようになるきっかけを与えてくれたコードで印象に残っています.このコードのおかげで汎用ファンクタの章で言わんとしている事が理解できるようになり,そこを足がかりに他の章も少しずつ理解できるようになった記憶があります.

特殊化や関数オーバーロードの活用

template< typename C >
inline range_iterator<C>::type range_begin( C& c ) {
    return c.begin();
}

template< typename T, std::size_t sz >
inline T* range_begin( T (&a)[sz] ) {
    return a;
}

template< typename Iterator >
inline Iterator range_begin( std::pair<Iterator,Iterator>& p ) {
    return p.first;
}
http://svn.boost.org/svn/boost/trunk/boost/range/begin.hpp

他にもいっぱいありますが,取りあえず説明のために一つ選びました.Boost.Range は「Iterator の次のインターフェース」として期待されているものです.簡単に言うと「関数の引数に begin/end を指定させる代わりにコンテナ自体を指定させ,begin/end は関数内で自分で呼ぶ」と言うコンセプトのものなのですが,STL コンテナだけでなく静的配列や std::pair もまったく同じインターフェースで使用するために特殊化や関数オーバーロードを活用しているケースです.

個人的には,静的配列のサイズを上記のような形で取れる事をこのコードを見て初めて知りました.

初期化処理の隠蔽

template <int Major = 2, int Minor = 0>
class winsock_init : private winsock_init_base {
public:
    winsock_init(bool allow_throw = true) {
        startup(data_, Major, Minor);
        if (allow_throw) throw_on_error(data_);
    }
    
    winsock_init(const winsock_init&) {
        startup(data_, Major, Minor);
        throw_on_error(data_);
    }
    
    ~winsock_init() {
        cleanup(data_);
    }

private:
  static data data_;
};

static const winsock_init<>& winsock_init_instance = winsock_init<>(false);
http://svn.boost.org/svn/boost/trunk/boost/asio/detail/winsock_init.hpp

Winsock に限らず,何らかのライブラリを使っていると「最初に 1回だけ初期化関数を呼び出して下さい」のような制約のあるものにしばしば遭遇します.こういったライブラリを使ったラッパーライブラリを作成する際にはできるだけ同じ制約(最初に初期化関数を呼び出す)は設けたくないと思うのですが,そう言ったときに役立つ方法です.

コンストラクタで初期化関数を呼び出してデストラクタ終了関数を呼び出すようなクラスを作成し,そのクラスの static な変数を 1 つ定義しておきます.こうする事によって,ユーザがライブラリを使うために各種ヘッダファイルをインクルードした時点で「最初に 1回だけ初期化関数を呼び出す」と言う制約をクリアする事ができます.

型変換演算子の活用

#if !defined(_TX)
template<class char_T, class wide_T>
class TX_literal_holder
{
public:
    char_T char_literal;
    wide_T wide_literal;
    TX_literal_holder(char_T a_char_literal, wide_T a_wide_literal)
        :char_literal(a_char_literal), wide_literal(a_wide_literal)
    {
    }
    operator char_T () const
    {
        return char_literal;
    }
    operator wide_T () const
    {
        return wide_literal;
    }
};
inline TX_literal_holder<const char, const wchar_t> make_TX_literal_holder
(
    const char a_char_literal,
    const wchar_t a_wide_literal
)
{
    return TX_literal_holder<const char, const wchar_t>
    (
        a_char_literal,
        a_wide_literal
    );
}
inline TX_literal_holder<const char *, const wchar_t *> make_TX_literal_holder
(
    const char * a_char_literal,
    const wchar_t * a_wide_literal
)
{
    return TX_literal_holder<const char *, const wchar_t *>
    (
        a_char_literal,
        a_wide_literal
    );
}
#define _TX(X) make_TX_literal_holder(X, L##X)
#endif
マルチ文字列リテラル - TrickDiary

C++ では operator 型() と言う記述で「型変換演算子」が定義できます.上記のコードは,この型変換演算子を活用して Visual C++ でよく使用する _T() マクロと同等のものを作成しています._T() マクロは UNICODE の定義の有無によってどちらか一方しか使えないと言うものでしたが,上記のコードは指定された型 (char or wchar_t) によってどちらでも使用可能なようになっています.

型変換演算子はうまい使いどころがなかなか思い浮かばないのですが,まぁうまく使っていきたいなと思います.

ぷりぷろせっさ

const bbl_code sjis_euc_map[] = {
#include "sjis-euc.csv"
};
http://tricklib.com/cxx/ex/babel/babel.cpp

#include の魔力 - Life like a clown のアレ.上記はファイル内容をぶち込んでいるだけなので,「その発想はなかった」と思うだけで魔力と言うほどのものではないかとは思います.プリプロセッサの魔力と言うか「へ,変態だー!」のようなネタはきっと id:DigitalGhost さんが投稿してくれると思いますzzZ

広告を非表示にする