Boost.Variant そのに

http://d.hatena.ne.jp/janus_wel/20101004/1286191398 で Boost.Variant が何かがわかったところで実際に使って何か書いてみる。

とはいうもののいい例がなかったのでとりあえず sort してみた。

/*
 * main.cpp
 *  test for Boost.Variant
 *
 *  written by janus_wel<janus.wel.3@gmail.com>
 *  This source code is in public domain, and has NO WARRANTY.
 * */

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <boost/variant.hpp>

struct cmp : public boost::static_visitor<bool> {
    // the order is integers, then strings
    bool operator()(const int, const std::string&) const {
        return true;
    }
    bool operator()(const std::string&, const int) const {
        return false;
    }
    // dictionary order for strings
    bool operator()(const std::string& s, const std::string& t) const {
        return (s.compare(t) < 0);
    }
    // numerical order for integers
    bool operator()(const int m, const int n) const {
        return (m < n);
    }
};

int main(void) {
    typedef boost::variant<int, std::string> test_type;

    std::vector<test_type> v;
    v.push_back(10);
    v.push_back("bar");
    v.push_back(4);
    v.push_back("foo");
    v.push_back(-4);
    v.push_back("buz");

    std::ostream_iterator<test_type> oitr(std::cout, "\n");

    std::copy(v.begin(), v.end(), oitr);
    std::cout << std::endl;

    cmp comparison;
    std::sort(v.begin(), v.end(), boost::apply_visitor(comparison));

    std::copy(v.begin(), v.end(), oitr);
    std::cout << std::endl;

    return 0;
}

point がいくつかあって、

  • typedef 推奨
    • boost::variant<int, std::string> みたいにどうしたって型名が長くなるし、操作したいから作った data 型なので何回も書くことになるはず。というわけで typedef 推奨。
    • 上記でいう typedef boost::variant<int, std::string> test_type; のところ。名前が適当すぎる。
  • visitor pattern
    • GoF design pattern のアレ。
    • Boost.Variant を使った data structure へ access したい場合 boost::static_visitor<> という class を継承して自前で visitor class を作る。
    • 上記の cmp class がそれ。要は functor class なんだけど boost::variant<> に指定した型を引数に取る operator() 関数を定義してやればいい、と。
    • で、実際に自前 visitor class を作用させるには boost::apply_visitor() 関数を使う。上記の使い方では functor object を返させているけど普通に関数として呼び出してもいい。
    • 余談だけどこれ型の数が増えたら比較関数もすごい勢いで増えるんじゃ…。型の数を n とすると n*2 個比較関数書かないといけないなぁ。 template 使えるところはつかえってことか。
  • std::ostream
    • 普通に std::ostream 系列に流し込める。
    • でも std::ostream_iterator を使う場合は boost::variant<> 型を指定しないとうまいこといかないよ、と。

という感じで、 boost::static_visitor<> と boost::apply_visitor() の使い方さえわかってしまえば何とかなる。