ボレロ村上 - ENiyGmaA Code

中3女子です。

Boost.Wave を使ってみる

本記事は、Boost Advent Calendar 2011 の9日目の記事です。


本稿では Boost.Wave という C/C++ プリプロセッサライブラリについて書きます。


Boost.Wave とは

Boost.Wave は、パーサコンビネータライブラリ Boost.Spirit によって書かれた C/C++ プリプロセッサライブラリです。


C/C++ プリプロセッサは言うまでもなく、某社のサーバでも利用されているという純粋関数型言語です。
主に HTML ジェネレータとして活用されているという噂です。


Boost.Wave を使うことによって、C/C++ プリプロセッサC++ のプログラム内で処理したり、
あるいはプリプロセス中の各処理にフックして解析を行なったりすることが出来ます。


Quick Start

まずは 本家ドキュメント の Quick Start のコードを読んでみます。


    • Quick Start
#include <iostream>
#include <fstream>
#include <string>
#include <boost/wave.hpp>
#include <boost/wave/cpplexer/cpp_lex_token.hpp>
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp>

int main() {
    std::ifstream instream("input.cpp");
    std::string input(
        std::istreambuf_iterator<char>(instream.rdbuf()),
        std::istreambuf_iterator<char>());

    typedef boost::wave::cpplexer::lex_iterator<
            boost::wave::cpplexer::lex_token<> >
        lex_iterator_type;

    typedef boost::wave::context<
            std::string::iterator, lex_iterator_type>
        context_type;

    context_type ctx(input.begin(), input.end(), "input.cpp");

    ctx.add_include_path("...");
    ctx.add_macro_definition(...);

    context_type::iterator_type first = ctx.begin();
    context_type::iterator_type last = ctx.end();
    while (first != last) {
        std::cout << (*first).get_value();
        ++first;
    }
}


これは、input.cpp というソースをプリプロセッサにかけて、結果を標準出力にそのまま書き出す単純なプログラムです。


ヘッダ boost/wave.hpp がプリプロセッサの主たるコンテキストを、
boost/wave/cpplexer/* がレキサ(字句解析)の各コンポーネントを提供しています。

    typedef boost::wave::cpplexer::lex_iterator<
            boost::wave::cpplexer::lex_token<> >
        lex_iterator_type;


プリプロセスは字句レベルで処理されるので、まずトークンを走査するイテレータを用意します。

    typedef boost::wave::context<
            std::string::iterator, lex_iterator_type>
        context_type;

    context_type ctx(input.begin(), input.end(), "input.cpp");


context は、これがプリプロセッサの本体です。
wave::context はポリシーベースになっており、テンプレート引数に目的に応じたクラスを渡すことで、
特定の処理をフックするなど動作をカスタマイズすることができます。
これについて詳しくは後ほど具体的に見ていきます。

    ctx.add_include_path("/usr/include");
    ctx.add_macro_definition("__GNUC__=4");


include ディレクティブで実際にインクルードされるファイルは環境や設定によって異なります。
また、コンパイラが独自定義する事前定義マクロもあります。

こうしたものは、add_include_path() や add_macro_definition() メンバ関数によって、
コンテキストに設定してやることが出来ます。

    context_type::iterator_type first = ctx.begin();
    context_type::iterator_type last = ctx.end();
    while (first != last) {
        std::cout << (*first).get_value();
        ++first;
    }


プリプロセスされた結果は、イテレータから取得することができます。
context::iterator_type の指す型はこの場合 wave::cpplexer::lex_token<> で、
これにトークンの種類や文字列などの情報が格納されています。


文字列は get_value() メンバ関数で参照できます。


なお、プリプロセスはイテレータを進めることで処理が行なわれるので、
逆に言えばイテレータを進めない限り処理は行なわれないままなので注意してください。


さて、実際に次のような input.cpp を用意して実行してみます。

    • input.cpp
#define MESSAGE Hello, C Preprocessor!
MESSAGE
    • 標準出力
#line 2 "/input.cpp"
Hello, C Preprocessor!


見ての通り、MESSAGE マクロが展開されてそのまま表示されています。
プリプロセスは完了しているので、define ディレクティブはもはやコードに残っていません。


いろいろ試してみるには、Wave 自身でつくられたドライバがパッケージに含まれているので
それを使うのも良いでしょう。


インクルード構造をグラフ化してみる


さて、少し実践的な Wave の使い方を見てみます。


Boost.Wave は、コンテキストのポリシーを組み替えることによって、
マクロの展開処理やディレクティブの処理をフックすることが出来ます。


Sample には、インクルード階層をリストにして書き出す例などが載っています。
サンプル通りでも面白くないので、ここでは Boost.Graph を併用してグラフに書き出してみることにします。

    • wavegraph.cpp
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <utility>
#include <iterator>
#include <algorithm>
#include <boost/wave.hpp>
#include <boost/wave/preprocessing_hooks.hpp>
#include <boost/wave/cpplexer/cpp_lex_token.hpp>
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/filesystem.hpp>

// インクルードをグラフ化するフッククラス
class include_graph_hooks
    : public boost::wave::context_policies::default_preprocessing_hooks
{
private:
    // グラフのノード
    struct node_type {
        boost::filesystem::path absolute;    // 絶対パス
        boost::filesystem::path filename;    // ファイル名
    public:
        node_type(boost::filesystem::path const& absolute, boost::filesystem::path const& filename)
            : absolute(absolute)
            , filename(filename)
        {}
        friend bool operator==(node_type const& lhs, node_type const& rhs) {
            return lhs.absolute == rhs.absolute;
        }
        friend bool operator==(node_type const& lhs, boost::filesystem::path const& rhs) {
            return lhs.absolute == rhs;
        }
        friend bool operator==(boost::filesystem::path const& lhs, node_type const& rhs) {
            return lhs == rhs.absolute;
        }
        template<typename Elem, typename Traits>
        friend std::basic_ostream<Elem, Traits>& operator<<(std::basic_ostream<Elem, Traits>& lhs, node_type const& rhs) {
            return lhs << rhs.filename.generic_string();
        }
    };
    // グラフのエッジ
    typedef std::pair<int, int> edge_type;
    // グラフ型
    typedef boost::adjacency_list<
        boost::vecS,
        boost::vecS,
        boost::directedS,
        boost::no_property
    > graph_type;
public:
    explicit include_graph_hooks(boost::filesystem::path const& start)
        : node_list_(1, node_type{boost::filesystem::absolute(start), start.filename()})
        , current_(1, 0)
    {}
    // グラフを作成する関数
    template<typename Graph>
    Graph make_graph() const {
        return Graph(edge_list_.begin(), edge_list_.end(), node_list_.size());
    }
    // graphviz 形式で出力する関数
    void write_graphviz(std::ostream& out) const {
        boost::write_graphviz(
            out,
            make_graph<graph_type>(),
            boost::make_label_writer(&node_list_[0])
        );
    }
public:
    // インクルードファイル解析開始をフック
    template<typename Context>
    void opened_include_file(
        Context const& ctx,
        std::string const& relname, 
        std::string const& absname,
        bool is_system_include
        )
    {
        boost::filesystem::path abspath(boost::filesystem::absolute(absname));    // 絶対パス
        auto found = std::find(node_list_.begin(), node_list_.end(), abspath);
        auto to = std::distance(node_list_.begin(), found);                        // インクルードファイルのインデックス
        if (found == node_list_.end()) {                                        // 最初のインクルードならばノードに追加
            node_list_.emplace_back(
                abspath,
                abspath.filename()
                );
        }
        edge_list_.emplace_back(current_.back(), to);                            // エッジに追加
        current_.push_back(to);                                                    // カレントを更新
    }
    // インクルードファイル解析完了をフック
    template<typename Context>
    void returning_from_include_file(Context const& ctx) {
        current_.pop_back();                                                    // カレントを戻す
    }
private:
    std::vector<node_type> node_list_;
    std::vector<edge_type> edge_list_;
    std::vector<int> current_;
};

int main(int argc, const char* argv[]) {
    using namespace boost::wave;
    try {
        // ファイル argv[1] の内容を全部 text に読み込む
        std::ifstream ifs( argv[1] );
        std::string text(
            std::istreambuf_iterator<char>(ifs.rdbuf()),
            std::istreambuf_iterator<char>()
            );

        // プリプロセッサを用意
        typedef context<
            std::string::iterator,
            cpplexer::lex_iterator<cpplexer::lex_token<> >,
            iteration_context_policies::load_file_to_string,
            include_graph_hooks                                // インクルードをグラフ化するフッククラスを使う
        > context_type;
        context_type ctx(text.begin(), text.end(), argv[1], include_graph_hooks(argv[1]));

        // オプションやマクロを設定
        ctx.set_language(
            language_support(
                support_cpp                                    // C++として処理
                | support_option_long_long                    // long long 型サポート
                | support_option_variadics                    // 可変長引数マクロサポート
                | support_option_include_guard_detection    // インクルードガード検出
                )
            );
        ctx.add_sysinclude_path("/usr/local/include/c++/4.7.0");
        ctx.add_sysinclude_path("/usr/local/include/c++/4.7.0/i686-pc-linux-gnu");
        ctx.add_sysinclude_path("/usr/local/include/c++/4.7.0/backward");
        ctx.add_sysinclude_path("/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/include");
        ctx.add_sysinclude_path("/usr/local/include");
        ctx.add_sysinclude_path("/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/include-fixed");
        ctx.add_sysinclude_path("/usr/include");

        // プリプロセスを走らせる
        for (auto it = ctx.begin(), last = ctx.end(); it != last; ++it) ;

        // out.dot にグラフ出力
        {
            std::ofstream ofs("out.dot");
            ctx.get_hooks().write_graphviz(ofs);
        }
    } catch(cpp_exception& e) {
        // プリプロセスでエラー発生
        std::cerr << e.file_name() << "(" << e.line_no() << "):" << e.description() << std::endl;
    }
}
template <
    typename IteratorT,
    typename LexIteratorT, 
    typename InputPolicyT = iteration_context_policies::load_file_to_string,
    typename HooksT = context_policies::eat_whitespace<typename LexIteratorT::token_type>,
    typename DerivedT = this_type
>
class context;


まず、wave::context クラスの宣言を見てみます。


IteratorT は、入力ソースのイテレータを渡します。
LexIteratorT は、字句解析されたトークンのイテレータを渡します。


InputPolicyT は、これはインクルード時のファイル読み込み処理を定義します。
デフォルトの iteration_context_policies::load_file_to_string は名の通り、
設定されたインクルードディレクトリから実体のファイルを読み込みます。
例えばメモリ上の文字列を読み込んだり、アーカイブ化されたファイルを読み出したいなどの場合は、
独自の InputPolicy を渡すことで挙動をカスタマイズできます。


HooksT は、処理の際に呼び出される様々なフック関数を定義します。
デフォルトの context_policies::eat_whitespace は、空白文字を読み飛ばします。
空白文字を残したい場合は context_policies::default_preprocessing_hooks を指定することが出来ます。
これらを継承して独自のフック関数をオーバーライドすることで、簡単にフック処理を書くことが出来ます。


ここでは context_policies::default_preprocessing_hooks を継承して
include_graph_hooks クラスを定義しています。

    std::vector<node_type> node_list_;
    std::vector<edge_type> edge_list_;
    std::vector<int> current_;


node_list_ はグラフの頂点になります。
これはそれぞれのヘッダ/ソースファイルを示しています。


edge_list_ はグラフの辺であり、インクルード関係を表わしています。
有向のグラフで、矢印の向きは『インクルード元→インクルード先』です。


current_ は処理中のファイルを記録するノードリストのインデックスです。
インクルードはスタック的な階層になるので、一番最後の要素が現在処理中のファイルになります。


フックできる処理は数多くありますが、この場合はインクルードを解析するので、
インクルードファイル解析開始をフックする opened_include_file() と
インクルードファイル解析完了をフックする returning_from_include_file() をオーバーライドしています。

    // インクルードファイル解析開始をフック
    template<typename Context>
    void opened_include_file(
        Context const& ctx,
        std::string const& relname, 
        std::string const& absname,
        bool is_system_include
        )
    {
        boost::filesystem::path abspath(boost::filesystem::absolute(absname));    // 絶対パス
        auto found = std::find(node_list_.begin(), node_list_.end(), abspath);
        auto to = std::distance(node_list_.begin(), found);                        // インクルードファイルのインデックス
        if (found == node_list_.end()) {                                        // 最初のインクルードならばノードに追加
            node_list_.emplace_back(
                abspath,
                abspath.filename()
                );
        }
        edge_list_.emplace_back(current_.back(), to);                            // エッジに追加
        current_.push_back(to);                                                    // カレントを更新
    }


まずインクルードが発生したら、インクルード先のファイルをノードリストに追加します。
インクルードを記録するために、『インクルード元→インクルード先』のエッジを追加します。
処理はインクルード先に移るのでスタックを積みます。

    // インクルードファイル解析完了をフック
    template<typename Context>
    void returning_from_include_file(Context const& ctx) {
        current_.pop_back();                                                    // カレントを戻す
    }


インクルード先から戻ってきたらスタックを戻します。


これらを繰り返すだけで、グラフの元が作成されます。
もちろんインクルード等の処理は Wave によって適切に行なわれます。

    // グラフを作成する関数
    template<typename Graph>
    Graph make_graph() const {
        return Graph(edge_list_.begin(), edge_list_.end(), node_list_.size());
    }
    // graphviz 形式で出力する関数
    void write_graphviz(std::ostream& out) const {
        boost::write_graphviz(
            out,
            make_graph<graph_type>(),
            boost::make_label_writer(&node_list_[0])
        );
    }


後はグラフに書き出すだけです。
graphviz は DOT言語というプレーンテキストによるグラフ記述方法です。
ツールを使うことで簡単に dot 形式から画像に変換することが出来ます。


ということで実際にファイルをこのプログラムに食わせてみましょう。


かなりもの凄いことになっていますが、どのヘッダがどういったヘッダをインクルードしているか、
或いはあるヘッダがどこからどれだけインクルードされているかが、グラフィカルに解ると思います。


開発者がヘッダ同士の依存関係を見たり、循環インクルードや必要なヘッダのインクルード忘れを
発見するのに役立てられるかもしれません。


このように Boost.Wave を、C/C++ プログラムの解析ツールの作成に使うことが出来ます。


C/C++ プリプロセッサを他言語のインタプリタに組み込んでみる

そもそもプリプロセッサC/C++ の言語の一部ではありますが、
(良くも悪くも)完全な字句レベルの処理でありソースの意味論に関係しないものです。


そのため、プログラムの意味論的には独立した存在であるとも言えます。
ここでは、C/C++ プリプロセッサを他言語と組み合わせることを考えてみます。


Boost には Boost.Python という Python と簡単に連携できるライブラリがあるので、それと一緒に使ってみます。

    • wavepython.cpp
// Boost.Pythonを静的リンクする
#define BOOST_PYTHON_STATIC_LIB

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <boost/wave.hpp>
#include <boost/wave/cpplexer/cpp_lex_token.hpp>
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp>
#include <boost/python.hpp>

int main(int argc, const char* argv[]) {
    using namespace boost::wave;
    std::string preprocessed;
    try {
        std::ostringstream os;

        // ファイル argv[1] の内容を全部 text に読み込む
        std::ifstream ifs( argv[1] );
        std::string text(
            std::istreambuf_iterator<char>(ifs.rdbuf()),
            std::istreambuf_iterator<char>()
            );

        // プリプロセッサを用意
        typedef context<
            std::string::iterator,
            cpplexer::lex_iterator<cpplexer::lex_token<> >,
            iteration_context_policies::load_file_to_string,
            context_policies::default_preprocessing_hooks
        > context_type;
        context_type ctx(text.begin(), text.end(), argv[1]);

        // オプションやマクロを設定
        ctx.set_language(
            language_support(
                support_cpp                                    // C++として処理
                | support_option_long_long                    // long long 型サポート
                | support_option_variadics                    // 可変長引数マクロサポート
                | support_option_include_guard_detection    // インクルードガード検出
                )
            );
        ctx.add_sysinclude_path("/usr/local/include/c++/4.7.0");
        ctx.add_sysinclude_path("/usr/local/include/c++/4.7.0/i686-pc-linux-gnu");
        ctx.add_sysinclude_path("/usr/local/include/c++/4.7.0/backward");
        ctx.add_sysinclude_path("/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/include");
        ctx.add_sysinclude_path("/usr/local/include");
        ctx.add_sysinclude_path("/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/include-fixed");
        ctx.add_sysinclude_path("/usr/include");

        // プリプロセスを走らせる
        for (auto it = ctx.begin(), last = ctx.end(); it != last; ++it) {
            if (*it == T_NEWLINE) {
                std::cout << "T_NEWLINE!\n";
            }
            os << it->get_value();
        }

        // プリプロセス結果を記録
        preprocessed = os.str();
        //std::cout << preprocessed;
    } catch(cpp_exception& e) {
        // プリプロセスでエラー発生
        std::cerr << e.file_name() << "(" << e.line_no() << "):" << e.description() << std::endl;
    }

    // Python インタプリタ初期化
    Py_Initialize();
    boost::python::object global_ns = boost::python::import("__main__").attr("__dict__");

    // Python のコードとして実行
    boost::python::exec(boost::python::str(preprocessed.c_str()), global_ns, global_ns);
}


実装は至極簡単です。
Wave でプリプロセスしたソースをそのまま Pythonインタプリタに流し込んでやるだけです。


では実際にやってみます。

    • hello.py
#define MESSAGE 'Hello, C Preprocessor!'

for i in range(5):
    print( MESSAGE )

#pragma # これはコメントです
    • 標準出力
Hello, C Preprocessor!
Hello, C Preprocessor!
Hello, C Preprocessor!
Hello, C Preprocessor!
Hello, C Preprocessor!


ご覧の通り、Python のコードとプリプロセッサがキメラになっています。


ところで Python は、C言語系統のフリースタイル式の記法とは異なり、
インデントが意味を持つので、インデントや改行を制御できないプリプロセッサとは正直相性が良くありません。


また Python では # が行コメントになるので、プリプロセッサディレクティブと混同して
コメントを書けないのではと言う懸念がありますが、
規格ではコンパイラの解釈できない pragma ディレクティブは単に無視されるので、
pragma に続けて書けばコメントと同じくプリプロセス時にスキップされることになります。


もう一つ、定番の 99 bottles を Python + プリプロセッサ で書いてみます。

    • 99 bottles.py
#include <boost/preprocessor.hpp>

#define NUM(z, n, d) 99-n

for i in [ BOOST_PP_ENUM(100, NUM, _) ]:
    if i == 0:
        print( 'No more bottles of beer on the wall, no more bottles of beer.' )
        print( 'Go to the store and buy some more, 99 bottles of beer on the wall.' )
    elif i == 1:
        print( '1 bottle of beer on the wall, 1 bottle of beer.' )
        print( 'Take one down and pass it around, no more bottles of beer on the wall.' )
    else:
        print( str(i) + ' bottles of beer on the wall, ' + str(i) + ' bottles of beer!' )
        print( 'Take one down, and pass it around, ' + str(i-1) + ' bottles of beer on the wall!' )
    • 標準出力
99 bottles of beer on the wall, 99 bottles of beer!
Take one down, and pass it around, 98 bottles of beer on the wall!
98 bottles of beer on the wall, 98 bottles of beer!
Take one down, and pass it around, 97 bottles of beer on the wall!
97 bottles of beer on the wall, 97 bottles of beer!
Take one down, and pass it around, 96 bottles of beer on the wall!
96 bottles of beer on the wall, 96 bottles of beer!
Take one down, and pass it around, 95 bottles of beer on the wall!
95 bottles of beer on the wall, 95 bottles of beer!
Take one down, and pass it around, 94 bottles of beer on the wall!
94 bottles of beer on the wall, 94 bottles of beer!
Take one down, and pass it around, 93 bottles of beer on the wall!
93 bottles of beer on the wall, 93 bottles of beer!
Take one down, and pass it around, 92 bottles of beer on the wall!
92 bottles of beer on the wall, 92 bottles of beer!
Take one down, and pass it around, 91 bottles of beer on the wall!
91 bottles of beer on the wall, 91 bottles of beer!
Take one down, and pass it around, 90 bottles of beer on the wall!
90 bottles of beer on the wall, 90 bottles of beer!
Take one down, and pass it around, 89 bottles of beer on the wall!
89 bottles of beer on the wall, 89 bottles of beer!
Take one down, and pass it around, 88 bottles of beer on the wall!
88 bottles of beer on the wall, 88 bottles of beer!
Take one down, and pass it around, 87 bottles of beer on the wall!
87 bottles of beer on the wall, 87 bottles of beer!
Take one down, and pass it around, 86 bottles of beer on the wall!
86 bottles of beer on the wall, 86 bottles of beer!
Take one down, and pass it around, 85 bottles of beer on the wall!
85 bottles of beer on the wall, 85 bottles of beer!
Take one down, and pass it around, 84 bottles of beer on the wall!
84 bottles of beer on the wall, 84 bottles of beer!
Take one down, and pass it around, 83 bottles of beer on the wall!
83 bottles of beer on the wall, 83 bottles of beer!
Take one down, and pass it around, 82 bottles of beer on the wall!
82 bottles of beer on the wall, 82 bottles of beer!
Take one down, and pass it around, 81 bottles of beer on the wall!
81 bottles of beer on the wall, 81 bottles of beer!
Take one down, and pass it around, 80 bottles of beer on the wall!
80 bottles of beer on the wall, 80 bottles of beer!
Take one down, and pass it around, 79 bottles of beer on the wall!
79 bottles of beer on the wall, 79 bottles of beer!
Take one down, and pass it around, 78 bottles of beer on the wall!
78 bottles of beer on the wall, 78 bottles of beer!
Take one down, and pass it around, 77 bottles of beer on the wall!
77 bottles of beer on the wall, 77 bottles of beer!
Take one down, and pass it around, 76 bottles of beer on the wall!
76 bottles of beer on the wall, 76 bottles of beer!
Take one down, and pass it around, 75 bottles of beer on the wall!
75 bottles of beer on the wall, 75 bottles of beer!
Take one down, and pass it around, 74 bottles of beer on the wall!
74 bottles of beer on the wall, 74 bottles of beer!
Take one down, and pass it around, 73 bottles of beer on the wall!
73 bottles of beer on the wall, 73 bottles of beer!
Take one down, and pass it around, 72 bottles of beer on the wall!
72 bottles of beer on the wall, 72 bottles of beer!
Take one down, and pass it around, 71 bottles of beer on the wall!
71 bottles of beer on the wall, 71 bottles of beer!
Take one down, and pass it around, 70 bottles of beer on the wall!
70 bottles of beer on the wall, 70 bottles of beer!
Take one down, and pass it around, 69 bottles of beer on the wall!
69 bottles of beer on the wall, 69 bottles of beer!
Take one down, and pass it around, 68 bottles of beer on the wall!
68 bottles of beer on the wall, 68 bottles of beer!
Take one down, and pass it around, 67 bottles of beer on the wall!
67 bottles of beer on the wall, 67 bottles of beer!
Take one down, and pass it around, 66 bottles of beer on the wall!
66 bottles of beer on the wall, 66 bottles of beer!
Take one down, and pass it around, 65 bottles of beer on the wall!
65 bottles of beer on the wall, 65 bottles of beer!
Take one down, and pass it around, 64 bottles of beer on the wall!
64 bottles of beer on the wall, 64 bottles of beer!
Take one down, and pass it around, 63 bottles of beer on the wall!
63 bottles of beer on the wall, 63 bottles of beer!
Take one down, and pass it around, 62 bottles of beer on the wall!
62 bottles of beer on the wall, 62 bottles of beer!
Take one down, and pass it around, 61 bottles of beer on the wall!
61 bottles of beer on the wall, 61 bottles of beer!
Take one down, and pass it around, 60 bottles of beer on the wall!
60 bottles of beer on the wall, 60 bottles of beer!
Take one down, and pass it around, 59 bottles of beer on the wall!
59 bottles of beer on the wall, 59 bottles of beer!
Take one down, and pass it around, 58 bottles of beer on the wall!
58 bottles of beer on the wall, 58 bottles of beer!
Take one down, and pass it around, 57 bottles of beer on the wall!
57 bottles of beer on the wall, 57 bottles of beer!
Take one down, and pass it around, 56 bottles of beer on the wall!
56 bottles of beer on the wall, 56 bottles of beer!
Take one down, and pass it around, 55 bottles of beer on the wall!
55 bottles of beer on the wall, 55 bottles of beer!
Take one down, and pass it around, 54 bottles of beer on the wall!
54 bottles of beer on the wall, 54 bottles of beer!
Take one down, and pass it around, 53 bottles of beer on the wall!
53 bottles of beer on the wall, 53 bottles of beer!
Take one down, and pass it around, 52 bottles of beer on the wall!
52 bottles of beer on the wall, 52 bottles of beer!
Take one down, and pass it around, 51 bottles of beer on the wall!
51 bottles of beer on the wall, 51 bottles of beer!
Take one down, and pass it around, 50 bottles of beer on the wall!
50 bottles of beer on the wall, 50 bottles of beer!
Take one down, and pass it around, 49 bottles of beer on the wall!
49 bottles of beer on the wall, 49 bottles of beer!
Take one down, and pass it around, 48 bottles of beer on the wall!
48 bottles of beer on the wall, 48 bottles of beer!
Take one down, and pass it around, 47 bottles of beer on the wall!
47 bottles of beer on the wall, 47 bottles of beer!
Take one down, and pass it around, 46 bottles of beer on the wall!
46 bottles of beer on the wall, 46 bottles of beer!
Take one down, and pass it around, 45 bottles of beer on the wall!
45 bottles of beer on the wall, 45 bottles of beer!
Take one down, and pass it around, 44 bottles of beer on the wall!
44 bottles of beer on the wall, 44 bottles of beer!
Take one down, and pass it around, 43 bottles of beer on the wall!
43 bottles of beer on the wall, 43 bottles of beer!
Take one down, and pass it around, 42 bottles of beer on the wall!
42 bottles of beer on the wall, 42 bottles of beer!
Take one down, and pass it around, 41 bottles of beer on the wall!
41 bottles of beer on the wall, 41 bottles of beer!
Take one down, and pass it around, 40 bottles of beer on the wall!
40 bottles of beer on the wall, 40 bottles of beer!
Take one down, and pass it around, 39 bottles of beer on the wall!
39 bottles of beer on the wall, 39 bottles of beer!
Take one down, and pass it around, 38 bottles of beer on the wall!
38 bottles of beer on the wall, 38 bottles of beer!
Take one down, and pass it around, 37 bottles of beer on the wall!
37 bottles of beer on the wall, 37 bottles of beer!
Take one down, and pass it around, 36 bottles of beer on the wall!
36 bottles of beer on the wall, 36 bottles of beer!
Take one down, and pass it around, 35 bottles of beer on the wall!
35 bottles of beer on the wall, 35 bottles of beer!
Take one down, and pass it around, 34 bottles of beer on the wall!
34 bottles of beer on the wall, 34 bottles of beer!
Take one down, and pass it around, 33 bottles of beer on the wall!
33 bottles of beer on the wall, 33 bottles of beer!
Take one down, and pass it around, 32 bottles of beer on the wall!
32 bottles of beer on the wall, 32 bottles of beer!
Take one down, and pass it around, 31 bottles of beer on the wall!
31 bottles of beer on the wall, 31 bottles of beer!
Take one down, and pass it around, 30 bottles of beer on the wall!
30 bottles of beer on the wall, 30 bottles of beer!
Take one down, and pass it around, 29 bottles of beer on the wall!
29 bottles of beer on the wall, 29 bottles of beer!
Take one down, and pass it around, 28 bottles of beer on the wall!
28 bottles of beer on the wall, 28 bottles of beer!
Take one down, and pass it around, 27 bottles of beer on the wall!
27 bottles of beer on the wall, 27 bottles of beer!
Take one down, and pass it around, 26 bottles of beer on the wall!
26 bottles of beer on the wall, 26 bottles of beer!
Take one down, and pass it around, 25 bottles of beer on the wall!
25 bottles of beer on the wall, 25 bottles of beer!
Take one down, and pass it around, 24 bottles of beer on the wall!
24 bottles of beer on the wall, 24 bottles of beer!
Take one down, and pass it around, 23 bottles of beer on the wall!
23 bottles of beer on the wall, 23 bottles of beer!
Take one down, and pass it around, 22 bottles of beer on the wall!
22 bottles of beer on the wall, 22 bottles of beer!
Take one down, and pass it around, 21 bottles of beer on the wall!
21 bottles of beer on the wall, 21 bottles of beer!
Take one down, and pass it around, 20 bottles of beer on the wall!
20 bottles of beer on the wall, 20 bottles of beer!
Take one down, and pass it around, 19 bottles of beer on the wall!
19 bottles of beer on the wall, 19 bottles of beer!
Take one down, and pass it around, 18 bottles of beer on the wall!
18 bottles of beer on the wall, 18 bottles of beer!
Take one down, and pass it around, 17 bottles of beer on the wall!
17 bottles of beer on the wall, 17 bottles of beer!
Take one down, and pass it around, 16 bottles of beer on the wall!
16 bottles of beer on the wall, 16 bottles of beer!
Take one down, and pass it around, 15 bottles of beer on the wall!
15 bottles of beer on the wall, 15 bottles of beer!
Take one down, and pass it around, 14 bottles of beer on the wall!
14 bottles of beer on the wall, 14 bottles of beer!
Take one down, and pass it around, 13 bottles of beer on the wall!
13 bottles of beer on the wall, 13 bottles of beer!
Take one down, and pass it around, 12 bottles of beer on the wall!
12 bottles of beer on the wall, 12 bottles of beer!
Take one down, and pass it around, 11 bottles of beer on the wall!
11 bottles of beer on the wall, 11 bottles of beer!
Take one down, and pass it around, 10 bottles of beer on the wall!
10 bottles of beer on the wall, 10 bottles of beer!
Take one down, and pass it around, 9 bottles of beer on the wall!
9 bottles of beer on the wall, 9 bottles of beer!
Take one down, and pass it around, 8 bottles of beer on the wall!
8 bottles of beer on the wall, 8 bottles of beer!
Take one down, and pass it around, 7 bottles of beer on the wall!
7 bottles of beer on the wall, 7 bottles of beer!
Take one down, and pass it around, 6 bottles of beer on the wall!
6 bottles of beer on the wall, 6 bottles of beer!
Take one down, and pass it around, 5 bottles of beer on the wall!
5 bottles of beer on the wall, 5 bottles of beer!
Take one down, and pass it around, 4 bottles of beer on the wall!
4 bottles of beer on the wall, 4 bottles of beer!
Take one down, and pass it around, 3 bottles of beer on the wall!
3 bottles of beer on the wall, 3 bottles of beer!
Take one down, and pass it around, 2 bottles of beer on the wall!
2 bottles of beer on the wall, 2 bottles of beer!
Take one down, and pass it around, 1 bottles of beer on the wall!
1 bottle of beer on the wall, 1 bottle of beer.
Take one down and pass it around, no more bottles of beer on the wall.
No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.


プリプロセッサが使えるということは、つまり Boost.PP もそのまま使えるということです。


このように、相性にもよりますがプリプロセッサを他言語に組み込むことも出来ます。


まとめ

Boost.Wave は Boost の他のライブラリのスマートポインタやコンテナや文字列処理のように
どんなケースでも使えるというような汎用的なライブラリではありませんが、
C/C++ プリプロセッサという専門役割を果たすのに必要十分な機能を持ったライブラリです。


また Boost.Wave は Boost.Spirit でフルスクラッチされており、
Boost.Spirit そのもののポテンシャルを示していると言っても良いでしょう。


ということで、次回 @AmaiSaeta 氏の記事にバトンタッチします。
よろしくお願いします。