<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE section PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
  "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
<!--
  Hamigaki.Binary Library Document Source

  Copyright Takeshi Mouri 2007.
  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE_1_0.txt or copy at
  http://www.boost.org/LICENSE_1_0.txt)

  See http://hamigaki.sourceforge.jp/libs/binary for library home page.
-->
<section id="binary.tutorial">
  <title>チュートリアル</title>
  <using-namespace name="hamigaki"/>

  <section id="binary.tutorial.simple">
    <title>簡単な例</title>

    <para>簡単な例として、Microsoft Windowsで用いられるビットマップファイルのヘッダ、<code>BITMAPFILEHEADER</code>構造体のバイナリ出力を考える。</para>

    <programlisting><![CDATA[typedef struct tagBITMAPFILEHEADER
{
    WORD bfType;
    DWORD bfSize;
    WORD bfReserved1;
    WORD bfReserved2;
    DWORD bfOffBits;
} BITMAPFILEHEADER;
]]></programlisting>

    <para>ここで、<code>WORD</code>は16ビット符号なし整数を、<code>DWORD</code>は32ビット符号なし整数を意味している。</para>

    <para>この構造体の意図している利用法は次のようなものである。</para>

    <programlisting><![CDATA[void write_header(std::FILE* fp, ::DWORD size, ::DWORD offset)
{
    ::BITMAPFILEHEADER header;
    std::memset(&header, 0, sizeof(header));
    header.bfType = static_cast< ::WORD>('B') | (static_cast< ::WORD>('M') << 8);
    header.bfSize = size;
    header.bfOffBits = offset;
    std::fwrite(&header, sizeof(header), 1, fp);
}
]]></programlisting>

    <para>この方法には次のような問題がある。</para>

    <itemizedlist>
      <listitem>
        <simpara>エンディアン</simpara>
        <para>この構造体の各フィールドは2バイトまたは4バイトであり、出力の際にはバイト順を考慮する必要がある。</para>
        <para>ビットマップファイルの想定するエンディアンはリトルエンディアンであり、ビッグエンディアンの環境では正しいバイト順で出力されない。</para>
      </listitem>
      <listitem>
        <simpara>構造体のパディング</simpara>
        <para>C/C++標準は、ハードウェア上の制約や処理効率の向上のため、構造体の各フィールドの間にパディングを挿入することを許している。</para>
        <para>ビットマップファイルでは、フィールド間にパディングがあってはならないため、パディングが挿入される環境では意図した出力は得られない。実際、Microsoftの提供するヘッダファイルでは、独自のpragma指令を用いてパディングの挿入を防ぐようになっている。</para>
      </listitem>
    </itemizedlist>

    <para>これらの問題は、<code>BITMAPFILEHEADER</code>のメモリイメージをファイルに書き出していることに原因がある。ここで重要なのはファイルに書き出されるバイト列であって、メモリ上のレイアウトではない。正しい出力を得るためには、出力時のエンディアンやフィールドの配置を指示する必要がある。Hamigaki.Binaryでは、構造体に対し、テンプレート<classname>struct_traits</classname>の特殊化を用意することでこれを行う。</para>

    <programlisting><![CDATA[#include <hamigaki/binary/struct_traits.hpp>
#include <boost/mpl/list.hpp>

namespace hamigaki
{

template<>
struct struct_traits< ::BITMAPFILEHEADER>
{
private:
    typedef ::BITMAPFILEHEADER self;

public:
    typedef boost::mpl::list<
        member<self, ::WORD,  &self::bfType, little>,
        member<self, ::DWORD, &self::bfSize, little>,
        member<self, ::WORD,  &self::bfReserved1, little>,
        member<self, ::WORD,  &self::bfReserved2, little>,
        member<self, ::DWORD, &self::bfOffBits, little>
    > members;
};

} // namespace hamigaki
]]></programlisting>

    <para>特殊化された<classname>struct_traits</classname>では、<code>members</code>という名前の型を用意しなければならない。<code>members</code>はBoost.MPLの<code>Sequence</code>の形を取る。通常は、<code>boost::mpl::list</code>を使う。</para>

    <para><code>members</code>の要素は、<classname>member</classname>である。これが構造体のフィールドの出力方法を決定する。<classname>member</classname>のテンプレート引数は、順に「構造体の型」、「フィールドの型」、「フィールドのメンバポインタ」、「エンディアン」である。</para>

    <para>また、<code>members</code>中の要素順は、出力の際の順序として用いられる。</para>

    <para>これを使ってバイナリ出力を行うには、次のようにする。</para>

    <programlisting><![CDATA[#include <hamigaki/binary/binary_io.hpp>

void write_header(std::FILE* fp, ::DWORD size, ::DWORD offset)
{
    ::BITMAPFILEHEADER header;
    std::memset(&header, 0, sizeof(header));
    header.bfType = static_cast< ::WORD>('B') | (static_cast< ::WORD>('M') << 8);
    header.bfSize = size;
    header.bfOffBits = offset;

    char buffer[hamigaki::struct_size< ::BITMAPFILEHEADER>::value];
    hamigaki::binary_write(buffer, header);

    std::fwrite(buffer, 1, sizeof(buffer), fp);
}
]]></programlisting>

    <para>関数<code><functionname>binary_write</functionname>()</code>は、<classname>struct_traits</classname>の指示に従い、第一引数のバッファに第二引数の構造体オブジェクトをバイナリ出力する。バッファに必要なサイズは、メタ関数<classname>struct_size</classname>で調べることができる。</para>

    <para>今度のコードは、実行環境のエンディアンや構造体のパディングに影響されないため、常に正しい出力を得ることができる。</para>

    <para>同様に、関数<code><functionname>binary_read</functionname>()</code>を用いることでバイナリ入力を行うこともできる。</para>

    <programlisting><![CDATA[#include <hamigaki/binary/binary_io.hpp>

::BITMAPFILEHEADER read_header(std::FILE* fp)
{
    char buffer[hamigaki::struct_size< ::BITMAPFILEHEADER>::value];
    std::fread(buffer, 1, sizeof(buffer), fp);

    ::BITMAPFILEHEADER header;
    hamigaki::binary_read(buffer, header);

    return header;
}
]]></programlisting>
  </section>

  <section id="binary.tutorial.padding">
    <title>パディング</title>

    <para>前の例では、<code>BITMAPFILEHEADER</code>構造体を取り上げた。この構造体のメンバのうち、<code>bfReserved1</code>と<code>bfReserved2</code>は常に0を設定することになっている。これらのプログラム中で必要ないメンバを削った新しい構造体<code>bitmap_file_header</code>を考える。</para>

    <programlisting><![CDATA[struct bitmap_file_header
{
    boost::uint16_t type;
    boost::uint32_t size;
    boost::uint32_t offset;
};
]]></programlisting>

    <para><classname>struct_traits</classname>は次のように変更する。</para>

    <programlisting><![CDATA[#include <hamigaki/binary/struct_traits.hpp>
#include <boost/mpl/list.hpp>

namespace hamigaki
{

template<>
struct struct_traits< ::bitmap_file_header>
{
private:
    typedef ::bitmap_file_header self;

public:
    typedef boost::mpl::list<
        member<self, boost::uint16_t, &self::type, little>,
        member<self, boost::uint32_t, &self::size, little>,
        padding<2>,
        padding<2>,
        member<self, boost::uint32_t, &self::offset, little>
    > members;
};

} // namespace hamigaki
]]></programlisting>

    <para>ここで注目すべきは<classname>padding</classname>である。<classname>padding</classname>は、対応するメンバ変数が存在しない場合に、<classname>member</classname>の代わりとして使用できる。テンプレート引数はパディングのバイト数である。<code><functionname>binary_write</functionname>()</code>で書き出した際は、そのバイト数だけ0が出力される。</para>

    <para>ここでは、2バイトのパディングを２個使用しているが、当然4バイトのパディングを１個使用しても結果は同じである。</para>
  </section>

  <section id="binary.tutorial.complex">
    <title>複雑な例</title>

    <para>今度は少し複雑な例を取り上げる。</para>

    <programlisting><![CDATA[typedef struct tagRGBQUAD
{
    BYTE rgbBlue;
    BYTE rgbGreen;
    BYTE rgbRed;
    BYTE rgbReserved;
} RGBQUAD;

typedef struct tagBITMAPINFOHEADER
{
    DWORD biSize;
    LONG biWidth;
    LONG biHeight;
    WORD biPlanes;
    WORD biBitCount;
    DWORD biCompression;
    DWORD biSizeImage;
    LONG biXPelsPerMeter;
    LONG biYPelsPerMeter;
    DWORD biClrUsed;
    DWORD biClrImportant;
} BITMAPINFOHEADER;

namespace hamigaki
{

template<>
struct struct_traits< ::RGBQUAD>
{
private:
    typedef ::RGBQUAD self;

public:
    typedef boost::mpl::list<
        member<self, BYTE, &self::rgbBlue>,
        member<self, BYTE, &self::rgbGreen>,
        member<self, BYTE, &self::rgbRed>,
        member<self, BYTE, &self::rgbReserved>
    > members;
};

template<>
struct struct_traits< ::BITMAPINFOHEADER>
{
private:
    typedef ::BITMAPINFOHEADER self;

public:
    typedef boost::mpl::list<
        member<self, DWORD, &self::biSize, little>,
        member<self, LONG, &self::biWidth, little>,
        member<self, LONG, &self::biHeight, little>,
        member<self, WORD, &self::biPlanes, little>,
        member<self, WORD, &self::biBitCount, little>,
        member<self, DWORD, &self::biCompression, little>,
        member<self, DWORD, &self::biSizeImage, little>,
        member<self, LONG, &self::biXPelsPerMeter, little>,
        member<self, LONG, &self::biYPelsPerMeter, little>,
        member<self, DWORD, &self::biClrUsed, little>,
        member<self, DWORD, &self::biClrImportant, little>
    > members;
};

} // namespace hamigaki
]]></programlisting>

    <para>ここで、<code>BYTE</code>は8ビット符号なし整数を、<code>LONG</code>は32ビット符号付き整数を意味している。</para>

    <para>上記のような構造体を組み合わせて構造体<code>bitmap_info_256</code>を定義する。</para>

    <programlisting><![CDATA[struct bitmap_info_256
{
    BITMAPINFOHEADER header;
    RGBQUAD colors[256];
};
]]></programlisting>

    <para>これに対する<classname>struct_traits</classname>は、次のように書くことができる。</para>

    <programlisting><![CDATA[namespace hamigaki
{

template<>
struct struct_traits< ::bitmap_info_256>
{
private:
    typedef ::bitmap_info_256 self;

public:
    typedef boost::mpl::list<
        member<self, BITMAPINFOHEADER, &self::header>,
        member<self, RGBQUAD[256], &self::colors>
    > members;
};

} // namespace hamigaki
]]></programlisting>

    <para><classname>member</classname>に指定するフィールドの型には、整数値以外にも構造体や配列も指定できる。<code><functionname>binary_write</functionname>()</code>で出力する際には、入れ子となった構造体や配列の各要素に対して再帰的に<code><functionname>binary_write</functionname>()</code>が呼び出される。</para>

  </section>
</section>
