Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

ポインタのキャストについてつらつらと

タグ: C++ Boost
#include <iostream>
#include <boost/implicit_cast.hpp>
#include <boost/cstdint.hpp>

// メモリ上のデータ配置例(32bit版)
// 4byte Base1::vptr
// 4byte Base1::_a = 1
class Base1
{
public:
    Base1() : _a(1) {}
    virtual ~Base1() {}

    int _a;
};

// メモリ上のデータ配置例(32bit版)
// 4byte Base2::vptr
// 4byte Base2::_b = 2
class Base2
{
public:
    Base2() : _b(2) {}
    virtual ~Base2() {}

    int _b;
};

// メモリ上のデータ配置例(32bit版)
// 4byte Base1::vptr
// 4byte Base1::_a = 1
// 4byte Derived1::_c = 3
class Derived1 : public Base1
{
public:
    Derived1() : _c(3) {}

    int _c;
};

// メモリ上のデータ配置例(32bit版)
// (0xbffff75c) 4byte Base1::vptr
// (0xbffff760) 4byte Base1::_a = 1
// (0xbffff764) 4byte Base2::vptr
// (0xbffff768) 4byte Base2::_b = 2
// (0xbffff76c) 4byte Derived2::_d = 4
class Derived2 : public Base1, public Base2
{
public:
    Derived2() : _d(4) {}

    int _d;
};

void output(const char* n, void* p)
{
    std::cout << n << " void*:" << p << std::endl;
}

void output(const char* n, Base1* p)
{
    std::cout << n << " Base1*:" << p;
    if (p)
    {
        std::cout << " _a:" << p->_a;
    }
    std::cout << std::endl;
}

void output(const char* n, Base2* p)
{
    std::cout << n << " Base2*:" << p;
    if (p)
    {
        std::cout << " _b:" << p->_b;
    }
    std::cout << std::endl;
}

void output(const char* n, Derived1* p)
{
    std::cout << n << " Derived1*:" << p;
    if (p)
    {
        std::cout << " _a:" << p->_a << " _c:" << p->_c;
    }
    std::cout << std::endl;
}

void output(const char* n, Derived2* p)
{
    std::cout << n << " Derived2*:" << p;
    if (p)
    {
        std::cout << " _a:" << p->_a << " _b:" << p->_b << " _d:" << p->_d;
    }
    std::cout << std::endl;
}

暗黙の型変換

出来ること:

  1. 任意の型のポインタ→void*
  2. 派生クラスのポインタ→基底クラスのポインタ(アップキャスト)
  3. 整数値0→任意の型のポインタ(NULLポインタ)

もっとも安全なキャスト。
元のポインタが正常なデータを指していれば、キャスト後のポインタも正常なデータを指す。

Derived2 obj;

// 1. 任意の型のポインタ→void*
void* p = &obj;
output("1.ok", p);

// 2. 派生クラスのポインタ→基底クラスのポインタ
Base1* base1 = &obj;
output("2.ok", base1);
Base2* base2 = &obj;
output("2.ok", base2); // OK: 継承関係を考慮して、&obj(=0xbffff75c)より8バイト後ろ(=0xbffff764)が指される
1.ok void*:0xbffff75c
2.ok Base1*:0xbffff75c _a:1
2.ok Base2*:0xbffff764 _b:2

boost::implicit_cast

出来ること:

  1. 暗黙の型変換と同じ

暗黙の型変換を明示的に行うテンプレート関数。
暗黙の型変換用のポインタ変数を用意するのが面倒な時に使う。
暗黙の型変換同様、キャスト前のポインタが正常ならキャスト後のポインタも正常。

Derived2 obj;

// 1. 暗黙の型変換と同じキャスト
output("1.ok", boost::implicit_cast<void*>(&obj));
output("1.ok", boost::implicit_cast<Base1*>(&obj));
output("1.ok", boost::implicit_cast<Base2*>(&obj));
1.ok void*:0xbffff75c
2.ok Base1*:0xbffff75c _a:1
2.ok Base2*:0xbffff764 _b:2

dynamic_cast

出来ること:

  1. 実行時型情報を持つクラスのポインタのアップ/ダウン/クロスキャスト

実行時にオブジェクトの型情報を評価し、正常にキャストできないようならNULLを返す。
キャスト前のポインタが正常ならキャスト後のポインタが評価不能な異常なデータを指すことはない。NULLチェックさえ怠らなければ安全なキャスト。
実行時に一切コストが発生しない他のキャストと違い、dynamic_castによるダウンキャストやクロスキャストは実行時に型情報を評価するため少しだけ遅くなる。

Derived2 obj;

// 1. 実行時型情報を持つクラスのポインタのアップ/ダウンキャスト
Base1* base1 = dynamic_cast<Base1*>(&obj);
Base2* base2 = dynamic_cast<Base2*>(&obj);
output("1.ok", base1);
output("1.ok", base2);
output("1.ok", dynamic_cast<Derived2*>(base2));// OK: 継承関係を考慮して、base2(=0xbffff75c)より8バイト後ろ(=0xbffff764)が指される
output("1.ok", dynamic_cast<Derived1*>(base2)); // OK: 継承関係がないことを考慮して「正常に」NULLを返す
1.ok Base1*:0xbffff75c _a:1
1.ok Base2*:0xbffff764 _b:2
1.ok Derived2*:0xbffff75c _a:1 _b:2 _d:4
1.ok Derived1*:0

static_cast

出来ること:

  1. 暗黙の型変換と同じキャスト
  2. void*→任意の型のポインタ
  3. 基底クラスのポインタ→派生クラスのポインタ(ダウンキャスト)

void*からのキャストもダウンキャストも、オブジェクト本来の型と異なる型のポインタへとキャストしてしまうと異常なデータを指すことになる。

reinterpret_castと比べると

  1. 継承関係のない型のポインタへキャストできない
  2. 「ポインタのポインタ」から「ポインタ」へといったキャストはできない

ので多少はうっかりを防止できるが、それでも異常なポインタを容易に作れる危険なキャストであることは意識しなければならない。

Derived2 obj;

// 1. 暗黙の型変換と同じキャスト
output("1.ok", static_cast<void*>(&obj));
output("1.ok", static_cast<Base1*>(&obj));
output("1.ok", static_cast<Base2*>(&obj));

void* p = &obj;

// 2. void*→任意の型のポインタ
output("2.ok", static_cast<Derived2*>(p));
output("2.ok", static_cast<Base1*>(p));
output("2.ng", static_cast<Base2*>(p)); // NG: _a のデータ領域を _b のデータ領域と解釈する

Base1* base1 = &obj;
Base2* base2 = &obj;

// 3. 基底クラスのポインタ→派生クラスのポインタ
output("3.ok", static_cast<Derived2*>(base1));
output("3.ok", static_cast<Derived2*>(base2)); // OK: 継承関係を考慮して、base2(=0xbffff764)より8バイト手前(=0xbffff75c)が指される
output("3.ng", static_cast<Derived1*>(base1)); // NG: Base2::vptr のデータ領域を Derived::1_c のデータ領域と解釈する
1.ok void*:0xbffff75c
1.ok Base1*:0xbffff75c _a:1
1.ok Base2*:0xbffff764 _b:2
2.ok Derived2*:0xbffff75c _a:1 _b:2 _d:4
2.ok Base1*:0xbffff75c _a:1
2.ng Base2*:0xbffff75c _b:1
3.ok Derived2*:0xbffff75c _a:1 _b:2 _d:4
3.ok Derived2*:0xbffff75c _a:1 _b:2 _d:4
3.ng Derived1*:0xbffff75c _a:1 _c:12504

reinterpret_cast

出来ること:

  1. 整数値⇔ポインタ
  2. 任意の型のポインタから、他の任意の型のポインタへの強制キャスト

Cスタイルキャストを含むC++の全てのキャストの中で唯一、あらゆるポインタを「オブジェクトを参照するもの」ではなくただの「整数値」として扱う。
継承関係を完全に無視するため、アップキャストやダウンキャストには使えない。C言語的には一番分かりやすいキャストとも言える。
「void*を介したstatic_cast」に近いが、「ポインタのポインタ」を「ポインタ」へキャストできるという点でより危険。
が、これはこれでreinterpret_castが必要なほど危険なキャストをしていることが一目で分かるため、意外とstatic_castより間違いを見つけやすかったりする、こともある。

Derived2 obj;

// 1. 整数値⇔ポインタ
boost::int64_t n = reinterpret_cast<boost::int64_t>(&obj);
output("1.ok", reinterpret_cast<Derived2*>(n));

Base1* base1 = &obj;
Base2* base2 = &obj;

// 2. 任意の型のポインタから、他の任意の型のポインタへの強制キャスト
output("2.ok", reinterpret_cast<Base1*>(&obj));
output("2.ng", reinterpret_cast<Base2*>(&obj)); // NG: _a のデータ領域を _b のデータ領域と解釈する
output("2.ok", reinterpret_cast<Derived2*>(base1));
output("2.ng", reinterpret_cast<Derived2*>(base2)); // NG: 継承関係が考慮されずに型だけ変わる
output("2.ng", reinterpret_cast<Derived1*>(&obj)); // NG: Base2::vptr のデータ領域を Derived::1_c のデータ領域と解釈する
1.ok Derived2*:0xbffff75c _a:1 _b:2 _d:4
2.ok Base1*:0xbffff75c _a:1
2.ng Base2*:0xbffff75c _b:1
2.ok Derived2*:0xbffff75c _a:1 _b:2 _d:4
2.ng Derived2*:0xbffff764 _a:2 _b:-1606460800 _d:-1606460800
2.ng Derived1*:0xbffff75c _a:1 _c:12504

Cスタイルキャスト

static_castできるならstatic_cast。そうでないならreinterpret_cast。みたいな感じの挙動。だったと思う。たしか。

Hippo Mocksなるものを見つけた

タグ: C++ Boost

http://www.assembla.com/wiki/show/hippomocks/
Hippo Mocks

( ´∀`) 逆立ちしたカバなーんだ

Excite ウェブページ翻訳
カバは馬鹿にします。

( ゚д゚ )


まあ名前はさておき。
C++のMockフレームワークはmockppくらいしか使ったことがないが、

  • ヘッダファイルを一つincludeするだけで使える
  • ライブラリのリンクは必要なし
  • Mockクラスを定義する必要もなし

という使い勝手はなかなか魅力的に見える。
試しに最新版(Hippo Mocks 3.1)をVC8上でBoost 1.40.0のユニットテストフレームワークと組み合わせて使ってみよう。

#define BOOST_TEST_MAIN
#include <boost/test/included/unit_test.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/foreach.hpp>
#include <hippomocks.h>

// まだ実装のないインタフェース
class Receiver
{
public:
    virtual ~Receiver() {}

    virtual void Number(size_t n) = 0;
    virtual void Value(const char* value) = 0;
};

// アルファベットをスペースでトークン分割して receiver に渡す関数
bool tokenize(const char* source, Receiver& receiver)
{
    using namespace boost::spirit;

    std::vector<std::string> values;
    bool result = qi::parse(source, source + strlen(source), raw[*ascii::alpha] % ' ', values);

    receiver.Number(values.size());
    BOOST_FOREACH(const std::string& value, values)
    {
        receiver.Value(value.c_str());
    }
    return result;
};

// Hippo Mocksの文字列ポインタ比較を特殊化
template <>
struct comparer<const char*>
{
    static inline bool compare(const char* a, const char* b)
    {
        return strcmp(a, b) == 0;
    }
};

BOOST_AUTO_TEST_CASE(Test)
{
    MockRepository mocks;
    Receiver* iamock = mocks.InterfaceMock<Receiver>();
    mocks.ExpectCall(iamock, Receiver::Number).With(3);
    mocks.ExpectCall(iamock, Receiver::Value).With("foo");
    mocks.ExpectCall(iamock, Receiver::Value).With("bar");
    mocks.ExpectCall(iamock, Receiver::Value).With("baz");
    // 以下のように呼ばれることを期待
    // iamock->Number(3); 
    // iamock->Value("foo"); 
    // iamock->Value("baz"); 
    // iamock->Value("baz"); 
    BOOST_CHECK(tokenize("foo bar  baz", *iamock));
}
Running 1 test case...
unknown location(0): fatal error in "Test": std::exception: Function Receiver::Number(4) called with
 mismatching expectation!
Expections set:
test.cpp(47) Expectation for Receiver::Number(3) on the mock at 0x003AB3F8 was not satisfied.
test.cpp(48) Expectation for Receiver::Value(foo) on the mock at 0x003AB3F8 was not satisfied.
test.cpp(49) Expectation for Receiver::Value(bar) on the mock at 0x003AB3F8 was not satisfied.
test.cpp(
test.cpp(56): last checkpoint

*** 1 failure detected in test suite "Master Test Suite"

Hippo Mocksのメッセージが見切れてる(´・ω・`)
Boost.Testがカットしちゃったのか。
「期待される関数呼び出しが成されたか」を全て列挙してくれるのは親切といえば親切だが、見切れると辛い。

ちなみにこの例は「tokenize()が連続するスペースを一つのデリミタと見なさない」のが期待に沿わない部分なので、parse部分を次のように変更すればテストに合格するようになる。

// qi::phrase_parse を使えばいい話だが
bool result = qi::parse(source, source + strlen(source), raw[*ascii::alpha] % +char_(' '), values);
Running 1 test case...

*** No errors detected

Boost.Testの使い方再確認

タグ: C++ Boost

どうにも私のBoost.Testに関する知識が古い(1.34.1のドキュメント相当?)ので更新を図る。

とりあえず基本的なことは

この辺を読めば大体解る。
あとややこしいのは「unit_test.hppをincludeする前に何をdefineするか」だが、これはおおよそ以下の様にまとめられる。

#if boost_unit_test_frameworkライブラリをリンクする
# if ライブラリを動的リンクする
#  define BOOST_TEST_DYN_LINK
# endif
#else
# define BOOST_TEST_NO_LIB
#endif

// 以上はどちらかというとソースよりコンパイラオプションで定義

#if (! テストモジュールを複数のcppに分けて実装する) || 複数のcppの中のどれか一つ

# if (! ライブラリを動的リンクする) && テストモジュールの初期化を定義する
#  if 初期化にはinit_unit_test_suiteではなくinit_unit_testを使う
#   define BOOST_TEST_ALTERNATIVE_INIT_API
#  endif
# elif マスターテストスイートに名前を付ける
#  define BOOST_TEST_MODULE 名前
# else
#  define BOOST_TEST_MAIN
# endif

# if (! boost_unit_test_frameworkライブラリをリンクする)
#  include <boost/test/included/unit_test.hpp>
# else
#  include <boost/test/unit_test.hpp>
# endif

#else
# include <boost/test/unit_test.hpp>
#endif

例えば以下のように書き始めると、わざわざboost_unit_test_frameworkライブラリをビルドしておかなくても、ヘッダをincludeするだけでユニットテストフレームワークを使うことができる。
「とりあえず使う」分には楽だが、その分コンパイルは遅い。

// 複数のcppの中のどれか一つ
#define BOOST_TEST_NO_LIB // boost/test/included/unit_test.hpp をincludeする場合は省略可
#define BOOST_TEST_MAIN
#include <boost/test/included/unit_test.hpp>
// それ以外
#define BOOST_TEST_NO_LIB
#include <boost/test/unit_test.hpp>

Boost.Spirit V2.1用Shift JISパーサ

タグ: C++ Boost

Boost.SpiritにV2が導入されてからしばらく経つし、そろそろclassicじゃないSpiritにも手を出してみよう(`・ω・´)
ただし使用するのは1.36.0?1.40.0 β1 RC1に入っているV2ではなく、trunk@55547のV2.1。
boost::spirit::qi::int_parserの実装を参考に、まずはShift JIS1文字にマッチするプリミティブパーサを作ってみる。

#include <boost/static_assert.hpp>
#include <boost/spirit/include/qi.hpp>

namespace client {

struct sjis_parser_impl : boost::spirit::qi::primitive_parser<sjis_parser_impl>
{
    template <typename Context, typename Iterator>
    struct attribute
    {
        typedef unsigned short type;
    };

    template <typename Iterator, typename Context, typename Skipper, typename Attribute>
    bool parse(Iterator& first, Iterator const& last, Context& /*context*/, Skipper const& skipper, Attribute& attr) const
    {
        // wchar_t とか渡されても(´・ω・`)知らんがな
        BOOST_STATIC_ASSERT(sizeof(typename std::iterator_traits<Iterator>::value_type) == 1);

        boost::spirit::qi::skip_over(first, last, skipper);
        if (first == last)
            return false;

        const unsigned char high = *first;
        if ((high & ~0x7f) == 0 || (high >= 0xa1 && high <= 0xdf))
        {
            // 1バイト文字(ASCIIおよび半角カナ)にマッチ
            attr = high;
            ++first;
            return true;
        }
        else if ((high >= 0x81 && high <= 0x9f) || (high >= 0xe0 && high <= 0xef))
        {
            // 2バイト文字上位1バイトにマッチ
            const Iterator save = first++;
            if (first != last)
            {
                const unsigned char low = *first;
                if (low != 0x7f && (low >= 0x40 && low <= 0xfc))
                {
                    // 2バイト文字下位1バイトにもマッチ
                    attr = ((high << 8) | low);
                    ++first;
                    return true;
                }
            }
            first = save;
        }
        return false;
    }
};

struct sjis_parser : boost::proto::terminal<sjis_parser_impl>::type {};

} // namespace client

V2とV2.1ではプリミティブパーサの実装が違っているので、上記のコードはV2(現行リリース版)では使えない。
ま、いずれV2.1になるさ。いずれ……

#include <string.h>
#include <vector>
#include <string>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
// この辺でsjis_parserを実装(実装をインクルード)

namespace client {

void test()
{
    const char source[] =
#if 0
        "ヽ(・∀・)ノ";
#else
        { -127, 82, 40, -91, -127, -51, -91, 41, -125, 109, 0 };
#endif

    const char* first = source;
    const char* last = source + strlen(source);

    using boost::spirit::qi::parse;
    using boost::spirit::qi::_1;

    // セマンティックアクションで標準出力に出力
    std::cout << std::hex;
    parse(first, last, *sjis_parser()[std::cout << _1 << ',']);
    std::cout << std::endl;

    using boost::phoenix::push_back;
    using boost::phoenix::ref;

    std::vector<int> codes;
    // セマンティックアクションでcodesにpush_back
    first = source;
    parse(first, last, *sjis_parser()[push_back(ref(codes), _1)]);
    std::copy(codes.begin(), codes.end(), std::ostream_iterator<int>(std::cout, ","));
    std::cout << std::endl;

    // パーサの属性が1つ(この場合std::vector<unsigned short>)ならこうも書ける
    first = source;
    parse(first, last, *sjis_parser(), codes);
    std::copy(codes.begin(), codes.end(), std::ostream_iterator<int>(std::cout, ","));
    std::cout << std::endl;

    std::string str("low bytes -> ");
    // std::stringにpush_backすると上位バイトが欠ける
    first = source;
    parse(first, last, *sjis_parser(), str);
    std::cout << str << std::endl;

    using boost::spirit::qi::raw;

    // rawディレクティブを使うと、マッチした範囲をiterator_rangeで求められる
    first = source;
    parse(first, last, raw[*sjis_parser()], str);
    std::cout << str << std::endl;
}

} // namespace client
8152,28,a5,81cd,a5,29,836d,
8152,28,a5,81cd,a5,29,836d,
8152,28,a5,81cd,a5,29,836d,8152,28,a5,81cd,a5,29,836d,
low bytes -> R(・ヘ・)m
ヽ(・∀・)ノ

cmake Boost

タグ: Xcode Boost iPhone

そういえばBoostは1.38.0以降、CMakeによるビルド環境構築に対応しているんだっけ。
ちょっと使ってみるか。

まずはBoostのルートディレクトリでcmake。

$ cmake -GXcode .

「これはCMakeベースのビルド環境で未だ開発途上にある試したければ云々」と但し書きが表示されるので、指示に従って(引数を追加して)再度cmake。

$ cmake -DCMAKE_IS_EXPERIMENTAL=YES_I_KNOW .

生成されたプロジェクトファイルを開く。

$ open Boost.xcodeproj
boost_cmake1.png

んー、このプロジェクトはiPhone向けのビルドもできるのかな?
構成を「Release」、ベースSDKを「iPhone Device 3.0」にして、

boost_cmake2.png

「libboost_serialization-mt.a」をビルドしてみよう。

boost_cmake3.png

デキタワァ(n'∀')η゚*。:*

うん、ちゃんとリンクできるし、問題なく動作する。
必要なライブラリをちまちまビルドする分にはこれで十分かな。

Appendix

タグ

Blog内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。