参考: http://d.hatena.ne.jp/faith_and_brave/20100201/1264997004
「C++0xになると、C++03 でごちゃごちゃした部分がだいぶすっきり書けるようになる」
らしいですが、C++0xを待たなくてもBoostを使えばだいぶすっきり書けるので、
BoostでのC++入門はこんな感じだよー、という気持ちで以下略。
この記事はC言語をある程度理解していることが前提です。
1. Hello World
C++/Boostでの出力はC++標準の IOStream ライブラリと Boost.Format を組み合わせて行います。
例として、C言語のprintfを用いた Hello World を、C++/Boostを使って書き直してみます。
#include <stdio.h> int main() { printf( "%s\n", "Hello, World" ); return 0; }
以上のコードをC++/Boostで書くと、以下のようになります。
#include <iostream> // std::cout を使うのに必要 #include <boost/format.hpp> // boost::format を使うのに必要 int main() { std::cout << boost::format("%s\n") % "Hello, World"; return 0; }
std::cout というのは標準出力です。Cで言う stdout です。
「標準出力(console out)に "Hello, World" を整形(format)して出力する」と読みます。
boost::format に渡す文字列は、C言語の printf と同じスタイルで書くことが出来ます。
複数の引数を渡すには、
std::cout << boost::format("%s, %s\n") % "Hello" % "World";
のように % 演算子をつなげて書きます。
またそれとは別のスタイルとして、
std::cout << boost::format("%1%, %2%\n") % "Hello" % "World";
このように、何番目の引数を表示させるかを明記して書くことも出来ます。
その場合、
std::cout << boost::format("1, 2, %1%, 4, %2%, %1%, 7, 8, %1%, %2%\n") % "Fizz" % "Buzz";
このように引数を何回も使い回したりできます。
使い回さない場合でも、この書き方なら いちいち型を指定する必要がないのでオススメです。
2. 式
Boost.Format に渡す値は、別に文字列である必要はありません。
#include <iostream> #include <boost/format.hpp> int main() { std::cout << boost::format("%1% = %2%\n") % "1 + 1" % ( 1 + 1 ); return 0; }
このように書くことで、式の計算結果を表示させることも出来ます。
()でくるんであるのは % 演算子の優先順位の関係です。 Boost.Format に渡す引数が複雑な場合は()でくるんで渡した方が悩まないで済むと思います。
C言語と同様、整数同士の割り算を行う場合には注意が必要です。
#include <iostream> #include <boost/format.hpp> int main() { std::cout << boost::format("%1%\n") % ( 3 / 2 ); // 端数が切捨てされる std::cout << boost::format("%1%\n") % ( 3.0 / 2.0 ); // きちんと端数も考慮される return 0; }
複雑な計算を行わせることも出来ます。
#include <iostream> #include <boost/format.hpp> #include <cmath> // std::sin を使うのに必要 int main() { std::cout << boost::format("%1%\n") % ( 1 * 2 + 2 * 3 ); // * は + より優先される std::cout << boost::format("%1%\n") % ( 10 * ( 10 - 1 ) / 2 ); // () を用いて優先順位を変えられる std::cout << boost::format("%1%\n") % std::sin(1.0); // sinを求める。単位はラジアン return 0; }
3. 変数
C言語と同様ですが、C++/Boostでは基本的に const
を付けて宣言することが望ましいです。
#include <string> // std::string を使うのに必要 #include <iostream> #include <boost/format.hpp> int main() { const int i = 23; // 3 という整数(int)に a という名前を割り当てる const std::string s = "abc"; // こちらは文字列 std::cout << boost::format("i の値は'%1%', s の値は'%2%'.\n") % i % s; return 0; }
このように "const
型 変数名 =
値;
" のように書ことで、その値に対して名前をつけることが可能になります。
以降は、23
といった具体的な値の代わりに、i
といった宣言した変数の名前を書くことで、具体的な値を書いたのと同じように振舞わせることが出来ます。
"const
型 変数1 =
値1,
変数2 =
値2,
変数3 =
値3;
" のように、複数の変数を定義することもできます。
#include <iostream> #include <boost/format.hpp> int main() { const int mikan = 30, apple = 100; // みかんとりんごの値段 std::cout << boost::format("みかん7個とりんご3個で、合計 %1% 円\n") % ( mikan * 7 + ringo * 3 ); return 0; }
4. 関数
関数を使うことで処理に名前を付けることが出来ます。
"戻り値の型 関数名 (
仮引数リスト )
{
処理 }
" のように書きます。
// 引数を取らない例 int the_Answer_to_life_the_universe_and_everything() { // "return 値;" で値を返す return 42; } // 引数ひとつを取る例 int square( int x ) { return x * x; } // 複数の引数を取る例 double ave2( double x, double y ) { return ( x + y ) / 2; }
関数を呼び出すには、 "関数名 (
引数リスト )
" のように書きます。
// さっきの関数定義がここにあるとする #include <iostream> #include <boost/format.hpp> int main() { // 関数の戻り値に名前をつける const int i = the_Answer_to_life_the_universe_and_everything(); const int s = square(i); std::cout << boost::format("%1% の二乗は %2% です。\n") % i % s; // 直接使うことも出来る std::cout << boost::format("%1% と %2% の平均は %3% です。") % i % s % ave2( i, s ); return 0; }
C言語と同様、何も値を返さない関数を定義することも出来ます。その場合は void
という特別な型を使います。
#include <iostream> #include <boost/format.hpp> void disp(const std::string& s) { std::cout << boost::format("%1%\n") % s; } int main() { disp("Hello World"); return 0; }
またBoostには関数をラップした Boost.Function というものも存在し、これを使うことで、関数を変数として扱うことが出来ます。
#include <iostream> #include <boost/format.hpp> #include <cmath> // for std::sqrt #include <boost/function.hpp> int the_Answer_to_life_the_universe_and_everything() { return 42; } void disp(const std::string& s) { std::cout << boost::format("%1%\n") % s; } double norm2( double x, double y ) { return std::sqrt( x * x + y * y ); } int main() { // the_Answer_to_life_the_universe_and_everything に別名を付ける const boost::function<int()> f1 = the_Answer_to_life_the_universe_and_everything; std::cout << boost::format("%1%\n") % f1(); // f1() は the_An(中略)ing() と同じ // disp に別名を付ける const boost::function<void(const std::string&)> f2 = disp; f2("Hello World"); // disp("Hello, World"); と同じ // norm2 に別名を付ける const boost::function<double (double, double)> f3 = norm2; std::cout << boost::format("%1%\n") % f3( 3, 4 ); // norm2( 3, 4 ) と同じ return 0; }
型名は"[]boost::function<[]
戻り値の型 (
引数型リスト )[]>[]
"となります。
5. 型
C++/Boost には以下のような型が用意されています(一部です)。
基本データ型 int : 整数型(0, 1, 2, 3, -1, etc...) char : 文字型('a', 'b', 'c', '1', etc...) double : 浮動小数点数型(0.1, 3.14, etc...) bool : 論理型(true, false) 複合データ型 boost::array<T, N> : 配列(<boost/array.hpp>をインクルード) std::vector<T> : リスト(<vector>をインクルード) std::string : 文字列(<string>をインクルード) std::map<Key, Value> : 辞書(<map>をインクルード) std::set<T> : 集合(<set>をインクルード) boost::function<Signature> : 関数(<boost/function.hpp>をインクルード) boost::tuple<T1,T2, ...> : 複数の値の詰め合わせ(<boost/tuple/tuple.hpp>をインクルード) boost::shared_ptr<T> : ポインタ(<boost/shared_ptr.hpp>をインクルード) boost::optional<T> : T型か無効値(<boost/optional.hpp>をインクルード) boost::variant<T1,T2, ...> : 可変型(Cでいうunion)(<boost/variant.hpp>をインクルード) boost::any : 任意型(<boost/any.hpp>をインクルード)
6. 基本演算
C++/Boost の演算子は殆どC言語と同様です。
ただし >> 演算子および << 演算子は、従来の意味(ビットシフト)に加えて「ストリーム入出力」という意味を持ちます。
また % 演算子も、従来の意味(剰余演算)に加えて「フォーマット出力」という意味を持ちます。
これらは「演算子多重定義」という仕組みによって実現されていますが、ライブラリ製作者以外はその仕組を学習する必要はないでしょう。
7. 参照
参照は変数に別名を与えます。
// const参照 const int a = 42; const int& b = a; // b は a を参照する std::cout << boost::format("%1%\n") % b; // boost::format("%1%\n") % a と同じ // 可変な参照 int x = 1; int& y = x; // y は x を参照する y = 23; // x = 23; と同じ。参照先の x を書き換える
参照は関数の引数として使うことが出来ます。
特にconstな参照で渡された変数は、無駄なコピー動作を起こさないので、コストを気にする場合にしばしば用いられます。
また、可変な参照を、複数の値を返すために使うことも出来ます。
// http://d.hatena.ne.jp/faith_and_brave/20100201/1264997004 の例 void get_ip(int& aa, int& bb, int& cc, int& dd) { aa = 127; bb = 0; cc = 0; dd = 1; } int a = 0; int b = 0; int c = 0; int d = 0; get_ip(a, b, c, d);
しかし、その場合はconstではなくなってしまう為、 Boost.Tuple を使った方が良いです:
#include <boost/tuple/tuple.hpp> boost::tuple<int, int, int, int> get_ip() { return boost::make_tuple( 127, 0, 0, 1 ); } #include <iostream> #include <boost/format.hpp> int main() { const boost::tuple<int, int, int, int> t = get_ip(); const int& a = boost::get<0>(t); const int& b = boost::get<1>(t); const int& c = boost::get<2>(t); const int& d = boost::get<3>(t); std::cout << boost::format("%1%.%2%.%3%.%4%\n") % a % b % c % d; }
8. 制御構文
C++/Boost では、C言語の if/switch/while/for に加え、範囲 for 構文を擬似的にサポートしています。
#include <boost/foreach.hpp> // マクロBOOST_FOREACHを使えるようにする #include <iostream> #include <boost/format.hpp> int main() { const int a[] = {1, 2, 3}; BOOST_FOREACH( const int x, a ) { std::cout << boost::format("%1%\n") % x; } return 0; }
また、 if 文の条件部や for 文の初期化部等で変数を宣言することも出来ます。
#include <cmath> #include <boost/optional.hpp> #include <iostream> #include <boost/format.hpp> // boost::optional を使った例 // 詳しく説明すると脱線するので「こんな書き方もある」程度に思って下さい boost::optional<double> my_sqrt( double x ) { if( x < 0 ) { return boost::none; } else { return std::sqrt(x); } } int main() { // if文中で変数を宣言し if( const boost::optional<double> x_ = my_sqrt(-1) ) { const double& x = x_.get(); // それを使う std::cout << boost::format("%1%\n") % x; } else { std::cout << boost::format("%1%\n") % "負の値です!"; } return 0; }
9. クラス
Cで言う構造体です。 C++/Boost では、クラスを使うことで新しい型を定義することが出来ます。
今まで出てきた std::string や boost::function などは全てクラスです。これらは基本型と殆ど同じように扱うことが出来る他、 "変数名.
メンバ関数名" と書くことにより、そのクラス専用に用意された関数を使うことが出来ます。
const std::string s = "abc"; std::cout << boost::format("%1%\n") % s.length(); // s の長さを求める
ユーザ側で定義することも出来ますが、ライブラリ側で定義されたクラスを使うだけでも、十二分にプログラミングは可能です。
自分でクラスを作ることは落とし穴が多いので、慣れないうちはライブラリ定義のクラスを使いましょう。
10. テンプレート
boost::function<int()> 等で既に触れられている物です。
任意の型に対して同じことをさせたい場合に用います。
例として簡単なテンプレート関数を作ってみると、
template<typename T> void disp( T x ) { std::cout << boost::format("%1%\n") % x; }
このように書くことで、 disp
関数は、あらゆる型の変数 x を受け入れることが出来るようになります。
例えば、
disp<int>(23);
のように呼び出すと、 disp
の T
を int
で置き換えた
void disp( int x ) { std::cout << boost::format("%1%\n") % x; }
という関数がコンパイラによって自動生成され、呼び出されます。
同様に []disp<std::string>("abc");[]
のように呼び出すと、
void disp( std::string x ) { std::cout << boost::format("%1%\n") % x; }
という関数が呼び出されます。
また、これらの角括弧 <> は省略可能です。
const double d = 3.14; disp(d);
このように書けば、 d
の型である double
版の disp
が呼ばれます。
また、前項のクラスも、テンプレートにすることが出来ます。 []std::vector<int> v;[]
のように使います。
その場合、角括弧は省略不能です。
11. 固定長配列
C言語の配列はC++/Boostでも使えますが、C++/Boostにはは様々な点で便利な boost::array<T, N> というクラスが用意されています。
このクラスは通常の配列と同様に扱える他、メンバごとのコピーなどを簡単に行えます。
#include <boost/array.hpp> #include <boost/foreach.hpp> #include <iostream> #include <boost/format.hpp> int main() { const boost::array<int, 3> a = {{ 1, 2, 3 }}; // 要素数取得 const int size = a.size(); // 3 // 要素アクセス const int front = a.front(); // 1 const int back = a.back(); // 3 const int second = a[1]; // 2 // 全部表示 BOOST_FOREACH( const int x, a ) { std::cout << boost::format("%1%\n") % x; } // コピー boost::array<int, 3> b = a; // 破壊的動作 // 要素書き換え for( std::size_t i = 0; i < b.size(); ++i ) { b[i] = i; } // 全要素を 0 で埋める b.assign( 0 ); return 0; }
12. リスト
C言語では取り扱いの難しかった可変長配列は、C++/Boost では std::vector<T> というクラスにまとめられています。
#include <vector> #include <boost/assign.hpp> #include <boost/foreach.hpp> #include <iostream> #include <boost/format.hpp> int main() { // 構築は少し面倒。C++の次世代規格で改善される const std::vector<int> a = boost::assign::list_of(1)(2)(3)(4); // boost::array と同じように扱える const int size = a.size(); // 4 const int front = a.front(); // 1 const int back = a.back(); // 4 const int second = a[1]; // 2 // 以下、破壊的動作 std::vector<int> b; using boost::assign::operator+=; // += による要素追加を行うのに必要 b += 2, 3, 5, 7, 11, 13; // 連続的要素追加 b.push_back( 17 ); // 後ろに要素を追加する b.pop_back(); // 最後の要素を削除する // 要素書き換え // BOOST_FOREACH を使ってみる BOOST_FOREACH( int& x, b ) // int& とすることで、書き換えが可能になる { x *= 2; } // 全部表示 BOOST_FOREACH( const int x, b ) { std::cout << boost::format("%1%\n") % x; } return 0; }
13. 連想配列
std::map<Key, Value>, boost::unordered_map<Key, Value> という二種類の連想配列が用意されています。前者は二分木、後者はハッシュテーブルによって実装されています。
#include <boost/unordered_map.hpp> // 普段は高速なこっちを使う // #include <map> // BOOST_FOREACH で取得した時にソートされている必要がある場合用 #include <boost/assign.hpp> #include <boost/foreach.hpp> #include <iostream> #include <boost/format.hpp> int main() { // 少し面倒なので map の型を typedef する typedef boost::unordered_map<std::string, int> map_type; // typedef std::map<std::string, int> map_type; // やはり構築は少し面倒。C++の次世代規格で改善される const map_type m = boost::assign::map_list_of("mikan", 30)("ringo", 100)("meron", 2000); // m.count(要素) で、その要素が存在しているかチェックできる if( m.count("ringo") ) { // りんごの値段は? const int ringo = m.at("ringo"); std::cout << boost::format("%1%\n") % ringo; } // 要素の書き換えとか map_type m2 = m; // 要素アクセス(なければ作る) m2["iyokan"] = 100; // 全部表示する(少し面倒) BOOST_FOREACH( const map_type::value_type x, m2 ) { // x.first がキー、 x.second が値 std::cout << boost::format("%1%: %2%\n") % x.first % x.second; } return 0; }
14. 名前空間
今まで std::cout や boost::format と使ってきた中の、 std とか boost とかです。
これらは名前空間と呼ばれ、同じ名前の関数やクラスの衝突を避けるために使われます。
namespace A { int f() { return 23; } } namespace B { int f() { return 42; } } #include <iostream> #include <boost/format> int main() { std::cout << boost::format("%1%\n") % A::f(); // 23 std::cout << boost::format("%1%\n") % B::f(); // 42 }
いちいち何回も std:: や boost:: を書くのが面倒な場合は、 using 宣言というものを行えます。
#include <iostream> #include <boost/format> int main() { using std::cout; // 以降は単に cout で使えるようになる using boost::format; // 同様に format 単独で使えるようになる cout << format("%1%\n") % "Hello, World"; return 0; }
さらに using namespace std;
と書くことで、 std 名前空間にある全ての名前を std:: と書かずに使うことが出来ますが、意図しない動作になる可能性もあるため、オススメはしません。
15. その他の細かいこと
この項は随時追加するかもしれません。
- main 関数に限り、最後の
return 0;
という記述は省略できます。その場合には最後にreturn 0;
が呼ばれた事になります。 - 実は単純な出力なら boost::format を使わなくても
std::cout << "Hello, World\n";
のように書けます。複雑な出力でも[]std::cout << "i の値は" << i << std::endl;[]
的に << 演算子を連続させることで記述できます。ただし読みにくくなるので基本は Boost.Format を使うべきです。なお std::endl は「改行して出力する」という意味です。 - 実は boost::format の型指定文字 %s は型非依存です。
const int i = 42; std::cout << boost::format("%s\n") % i;
というコードもエラーにならず表示されます。