2013年12月30日月曜日

2013年振り返り

2013年ものこりわずかとなりました。
仕事でも私生活でもいろいろありましたが、何とか、年を越せそうです。
特に 2013年は忙しかった。そんな気がします。
それでも、ブログをほぼ週一で更新できたのでよかったです。

2013年のブログを振り返るとやはりテスティングフレームワークの話題が大半を占めていました。
あとは C++ の話や Jenkins って感じでしょうか。
あぁ、あと Arudino を始めましたね。


ブログ始めて3年経ちましが、おかげさまでアクセスが順調に増えていっています。
乱雑な文章ではありますが、皆様に見て頂けて大変嬉しいです。
iutest のレビューもしてもらえたらもっと嬉しいです。)

それでは、また来年!


2013年12月27日金曜日

Travis CI 始めました

自作のプログラムのテストのために Travis CI を始めました。

これまでプログラムの検証には Jenkins を使っていたのですが、
ノートPC のローカルサーバーで動かしているのでノートPC を起動する必要がありました。
(あと clang のバージョンも古い)
Cloudbees Jenkins も使用していますが、こちらは clang でビルドができなかった。(標準ライブラリでエラーが出てしまう。やり方はあると思うが…)
というわけで、手軽に clang を使える環境として導入することにしました。

これから、ちょっとづつ使い方を覚えていこうと思います。

2013年12月25日水曜日

[C++03][iutest] 日本語テスト名機能の紹介

2013/12/16 に京都で行われた 「江添とボレロ村上の京都C++勉強会」 に参加しました。
やはり、人に解説してもらうと頭に入りやすくていいですね^^
参加者のレベルも高くてすごいなと感じました。

さて、
質疑応答の中で日本語識別子の話が出て、テスト名に使えるねって声がありました。

それ C++03 でもできるよ!
(※ それ = テスト名に日本語を使う)

iutest は私が個人的に作っている Google Test ライクなテスティングフレームワークです。
特徴としては、ヘッダーオンリー・Google Test の拡張機能・C++11 対応などがあります。

その中の機能の一つとして日本語テスト名に対応しています。
(v1.5.0 から対応。執筆時点での最新バージョンは v1.7.0
v1.5.0 をリリースしたときの変更履歴に書いただけで、詳しく書いてなかったので今回紹介することにしました。

はじめに
この機能を紹介するまえに。

C++11 からは識別子に日本語が使えるようになりました。
このため、C++11 以降の環境では今回紹介する機能を使わずとも日本語テスト名が書けます。
なので、日本語テスト名が使いたければ素直に C++11 対応のコンパイラーを使いましょう。


サンプルコード
それでは、サンプルコードを見てみましょう。
#include "iutest.hpp"

IUTEST(JapaneseTest, IUTEST_JAPANESE_NAME(かきくけこ))
{
    IUTEST_ASSERT_STREQ("かきくけこ"
        , ::iutest::UnitTest::GetInstance()->current_test_info()->name());
}

class FixedTest : public ::iutest::Test {};

IUTEST_F(IUTEST_JAPANESE_NAME_F(FixedTest, あいうえお)
    , IUTEST_JAPANESE_NAME(かきくけこ))
{
    IUTEST_ASSERT_STREQ("あいうえお"
        , ::iutest::UnitTest::GetInstance()->current_test_info()->test_case_name());
    IUTEST_ASSERT_STREQ("かきくけこ"
        , ::iutest::UnitTest::GetInstance()->current_test_info()->name());
}

int main(int argc, char* argv[])
{
    IUTEST_INIT(&argc, argv);
    return IUTEST_RUN_ALL_TESTS();
}

日本語テスト名を使う場合は、通常テスト名を書くところで IUTEST_JAPANESE_NAME を使うだけです。
簡単ですよね?
テストフィクスチャーを使う場合は、IUTEST_JAPANESE_NAME_F を使って、第一引数にフィクスチャークラス名を指定します。

実行画面


いかがでしょうか?
ちょっとでも iutest を使ってみたくなりましたでしょうか?
iutest はヘッダーオンリーなのでソースコードをダウンロードするだけで、すぐに使えます!

是非是非、使ってみてください。
SourceForge: http://osdn.jp/projects/iutest/
github: https://github.com/srz-zumix/iutest

2013年12月16日月曜日

ポインター引数に FALSE を渡してしまった…その時 C++14 では…

こちらは C++ (fork) Advent Calendar 2013 16日目の記事になります。

Advent Calendar 向けに書いていた記事ではないのですが、空いていたので…
なので内容はちょっと薄いかもしれまんが、ご容赦ください。



本の虫: C++03とC++14の違い:標準型変換編
C++14では、リテラルのみが整数nullポインター定数として扱われる。

C++03/11では、コンパイル時に値が0になる整数型もリテラルとして扱われていた。

C++14では、無用の混乱を避けるために、リテラルのみをnullポインター定数とするように改められた。

私はしばしば以下の様なコード見ることがあります。
void SetHogeHoge(int, int, void*, BOOL, BOOL) {
}

int main()
{
    SetHogeHoge(0, 0
        , FALSE // void* に FALSE を渡している!!
        , FALSE
        , FALSE
    );
    return 0;
}

おそらく SetHogeHoge の第3引数は NULL の間違いだと思われます。
SetHogeHoge の使用者はおそらく使用方法を勘違いしているか単なる typo かのどうちらかでしょう。

ここで問題なのは、上記コードは間違いだけど正常に動いてしまうことです。
動けばいいじゃんと思う方もいるかもしれませんが、
関数の意図を理解せずに書いたのだとすると、それはちょっとマズイかもしれません。

こんな間違いをする人なんていないだろーと思っていても、見かけることがあるんですよね。。。
(特に WIN32 API の呼び出しなんかで…)




さて、ここで冒頭の内容に目を向けてみましょう。
リンク先にも書いてあるように、C++14 からは整数nullポインター定数の扱いが変更になるそうです。
詳しいことは→ 本の虫: C++03とC++14の違い:標準型変換編

#include <stdio.h>

void f( void * ) { printf(__PRETTY_FUNCTION__ ); }
void f( ... ) { printf(__PRETTY_FUNCTION__ ); }

int main()
{
    f(1-1);
    return 0;
}
上記コードは C++03 と C++14 で結果が変わります。
C++03 の場合:[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ
C++14 の場合:[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

で、ここからが本題。

FALSE は大抵こうです。
#define FALSE 0
これでは、C++14 になったとしてもnullポインター定数として解釈されてしまいます。
そこで、FALSE を以下のように定義します。
#define FALSE (1-1)
こうすることで、本来 nullptr を渡すべきところで誤って FALSE と書いてしまってもコンパイルエラーになるようになります。

#define FALSE (1-1)

void SetHogeHoge(int, int, void*, BOOL, BOOL) {
}

int main()
{
    SetHogeHoge(0, 0, FALSE, FALSE, FALSE);
    return 0;
}

C++03 の場合:[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ
C++14 の場合:[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ
(clang だと C++03 でコンパイルしても警告が出るみたい)

いや~それにしても Wandbox 便利ですね!!

2013年12月13日金曜日

[C++][クソコード] main関数に全部書くってこうですか?

こちらは C++ (fork) Advent Calendar 2013 13日目の記事になります。

さて、皆さんそろそろ本当に息抜きが欲しくなった頃ではありませんか?
というわけで、今回はネタエントリーです。

テトリスを main(WinMain)関数の中に全部書いてみましたー

#include "stdafx.h"
#include "lambda_tetris.h"

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE /*hPrevInstance*/,
                     _In_ LPTSTR    /*lpCmdLine*/,
                     _In_ int       nCmdShow)
{
    enum kConfig {
          MAX_AREA_W = 10
        , MAX_AREA_H = 25
        , PADDING_X  = 10
        , PADDING_Y  = 20
        , BLOCK_SIZE = 20
        , NEXT_AREA_SIZE=BLOCK_SIZE*6
        , WINDOW_W = 400
        , WINDOW_H = 600
    };

    struct Position {
        LONG x, y;
        Position& operator += (const Position& rhs) {
            x += rhs.x;
            y += rhs.y;
            return *this;
        }
        Position operator + (const Position& rhs) const {
            Position pos = *this;
            pos += rhs;
            return pos;
        }
    };
    struct Matrix {
        POINT x;
        POINT y;
    };
    struct Block {
        Position pos;
        COLORREF color;
    };
    class Mino {
    public:
        ::std::vector<Block> blocks;
        Position pos;
    public:
        Mino(::std::initializer_list<Block> list) : blocks(list), pos(Position{ MAX_AREA_W / 2, -4 }) {};

        int left() const {
            int x= ::std::numeric_limits<int>::max();
            ::std::for_each(blocks.begin(), blocks.end(), [&](const Block& blk){ if( x > blk.pos.x ) x = blk.pos.x; });
            return x + pos.x;
        }
        int right() const {
            int x= ::std::numeric_limits<int>::min();
            ::std::for_each(blocks.begin(), blocks.end(), [&](const Block& blk){ if( x < blk.pos.x ) x = blk.pos.x; });
            return x + pos.x;
        }
        int bottom() const {
            int y= ::std::numeric_limits<int>::min();
            ::std::for_each(blocks.begin(), blocks.end(), [&](const Block& blk){ if( y < blk.pos.y ) y = blk.pos.y; });
            return y + pos.y;
        }
        void rotate(Matrix mtx) {
            for( auto& blk : blocks ) {
                blk.pos = { mtx.x.x * blk.pos.x + mtx.x.y * blk.pos.y, mtx.y.x * blk.pos.x + mtx.y.y * blk.pos.y };
            }
        }
    };
    struct FieldBlock : public Block {
        FieldBlock(const ::std::shared_ptr<Mino> mino, const Block& blk) {
            pos = blk.pos + mino->pos;
            color = RGB(GetRValue(blk.color) / 2, GetGValue(blk.color) / 2, GetBValue(blk.color) / 2);
        }
    };
    struct Field {
        ::std::vector<FieldBlock> line[MAX_AREA_H];
    public:
        bool find(Position pos) {
            if( pos.y < 0 || pos.y >= MAX_AREA_H ) return false;
            const auto it = ::std::find_if(line[pos.y].begin(), line[pos.y].end()
                , [=](const FieldBlock& b){ return b.pos.x <= pos.x && b.pos.x >= pos.x; });
            return (it != ::std::end(line[pos.y]));
        }
        bool find(::std::shared_ptr<Mino> mino) {
            for( const auto& blk : mino->blocks ) {
                if( find(blk.pos+mino->pos) ) return true;
            }
            return false;
        }
        bool fix(::std::shared_ptr<Mino> mino) {
            bool over = false;
            for( const auto& blk : mino->blocks ) {
                if( mino->pos.y + blk.pos.y < 0 ) {
                    over = true;
                    continue;
                }
                assert(mino->pos.y + blk.pos.y < MAX_AREA_H);
                line[mino->pos.y + blk.pos.y].emplace_back(mino, blk);
            }
            return over;
        }
        int checkLine() {
            int count=0;
            for( int i=0; i < MAX_AREA_H; ++i ) {
                if( line[i].size() == MAX_AREA_W ) {
                    auto tmp(::std::move(line[i]));
                    for( int j=i; j > 0; --j ) {
                        line[j] = ::std::move(line[j-1]);
                        ::std::for_each(line[j].begin(), line[j].end(), [j](FieldBlock& blk){ blk.pos.y = j; });
                    }
                    line[0] = ::std::move(tmp);
                    line[0].clear();
                    ++count;
                }
            }
            return count;
        }
    };

    struct Game {
        ::std::shared_ptr<Mino> m_curr;
        ::std::shared_ptr<Mino> m_next;
        Field m_field;
        ::std::mt19937 m_rnd;
        float m_speed;
        float m_y;
        unsigned int m_counter;
        unsigned int m_score;
        struct KeyState {
            DWORD hold;
            DWORD trigger;
        } m_keyState;

        Game() : m_curr(nullptr), m_next(nullptr), m_speed(0.1f), m_y(0.0f), m_score(0)
        {
            ::std::random_device rd;
            m_rnd.seed(rd());

            m_curr = MakeMino();
            m_next = MakeMino();
        }

        enum {
              BUTTON_UP     = 0x00
            , BUTTON_DOWN   = 0x01
            , BUTTON_LEFT   = 0x02
            , BUTTON_RIGHT  = 0x04
            , BUTTON_SPACE  = 0x08
        };
        DWORD UpdateKeyState()
        {
            DWORD key = 0;
            if( GetKeyState(VK_UP) < 0 ) key|=BUTTON_UP;
            if( GetKeyState(VK_DOWN) < 0 ) key|=BUTTON_DOWN;
            if( GetKeyState(VK_LEFT) < 0 ) key|=BUTTON_LEFT;
            if( GetKeyState(VK_RIGHT) < 0 ) key|=BUTTON_RIGHT;
            if( GetKeyState(VK_SPACE) < 0 ) key|=BUTTON_SPACE;

            m_keyState.trigger = (m_keyState.hold ^ key) & key;
            m_keyState.hold = key;
            return key;
        }
        bool IsFloor(::std::shared_ptr<Mino> mino)
        {
            const int bottom = mino->bottom();
            if( bottom >= MAX_AREA_H-1 ) return true;

            for( const auto& blk : mino->blocks ) {
                const auto y = mino->pos.y + blk.pos.y + 1;
                const auto x = mino->pos.x + blk.pos.x;
                if( y < 0 || y >= MAX_AREA_H ) continue;
                const auto& line = m_field.line[y];
                const auto it = ::std::find_if(line.begin(), line.end(), [=](const FieldBlock& b){ return b.pos.x == x; });
                if( it != ::std::end(line) ) return true;
            }
            return false;
        }
        void MoveX(::std::shared_ptr<Mino> mino, const Position& move)
        {
            if( move.x < 0 && ( mino->left() + move.x < 0 ) ) {
                return;
            }
            if( move.x > 0 && ( mino->right() + move.x >= MAX_AREA_W ) ) {
                return;
            }
            if( move.x ) {
                for( const auto& blk : mino->blocks ) {
                    if( m_field.find({ mino->pos.x + blk.pos.x + move.x, mino->pos.y + blk.pos.y }) ) {
                        return;
                    }
                }
            }
            mino->pos.x += move.x;
        }
        void Update()
        {
            UpdateKeyState();
            if( m_curr != nullptr )
            {
                ++m_counter;
                if( m_counter >= 60*60 ) {
                    m_counter = 0;
                    if( m_speed < 2.0f ) {
                        m_speed += 0.1f;
                    }
                }
                m_y += m_speed;
                Position move{ 0, 0 };
                if( m_y > 1.0f ) {
                    move.y = 1;
                    m_y -= 1.0f;
                }

                if( m_keyState.trigger & BUTTON_LEFT ) {
                    move.x -= 1;
                }
                if( m_keyState.trigger & BUTTON_RIGHT ) {
                    move.x += 1;
                }
                if( m_keyState.hold & BUTTON_DOWN ) {
                    move.y += 1;
                }
                if( m_keyState.trigger & BUTTON_SPACE ) {
                    m_curr->rotate({ 0, 1, -1, 0 });
                    if( m_curr->left() < 0 || m_curr->right() >= MAX_AREA_W || m_field.find(m_curr) ) {
                        m_curr->rotate({ 0, -1, 1, 0 });
                    }
                }

                MoveX(m_curr, move);
                for( int i=0; i < move.y; ++i ) {
                    if( IsFloor(m_curr) ) {
                        if( m_field.fix(m_curr) ) {
                            m_curr = nullptr;
                        } else {
                            m_curr = m_next;
                            m_next = MakeMino();
                        }
                        break;
                    }
                    m_curr->pos.y += 1;
                }
            }

            const auto n = m_field.checkLine();
            m_score += n*n * 10;
        }
        ::std::shared_ptr<Mino> MakeMino()
        {
            ::std::uniform_int_distribution<int> dist(0, 6);
            switch( dist(m_rnd) )
            {
            case 0:
                {
                    COLORREF c=RGB(0, 255, 255);
                    return ::std::make_shared<Mino>(Mino{ { 0, -1, c }, { 0, 0, c }, { 0, 1, c }, { 0, 2, c } });
                }
                break;
            case 1:
                {
                    COLORREF c=RGB(255, 255, 0);
                    return ::std::make_shared<Mino>(Mino{ { 0, 0, c }, { 0, 1, c }, { 1, 0, c }, { 1, 1, c } });
                }
                break;
            case 2:
                {
                    COLORREF c=RGB(0, 255, 0);
                    return ::std::make_shared<Mino>(Mino{ { -1, 0, c }, { 0, 0, c }, { 0, 1, c }, { 1, 1, c } });
                }
                break;
            case 3:
                {
                    COLORREF c=RGB(255, 0, 0);
                    return ::std::make_shared<Mino>(Mino{ { -1, 1, c }, { 0, 1, c }, { 0, 0, c }, { 1, 0, c } });
                }
                break;
            case 4:
                {
                    COLORREF c=RGB(255, 128, 0);
                    return ::std::make_shared<Mino>(Mino{ { 0, -1, c }, { 0, 0, c }, { 0, 1, c }, { 1, 1, c } });
                }
                break;
            case 5:
                {
                    COLORREF c=RGB(0, 0, 255);
                    return ::std::make_shared<Mino>(Mino{ { 1, -1, c }, { 1, 0, c }, { 1, 1, c }, { 0, 1, c } });
                }
                break;
            case 6:
                {
                    COLORREF c=RGB(255, 0, 255);
                    return ::std::make_shared<Mino>(Mino{ { -1, 1, c }, { 0, 0, c }, { 0, 1, c }, { 1, 1, c } });
                }
                break;
            default:
                break;
            }
            return nullptr;
        }
    };

    struct Global {
        Game game;
        static Global& GetInstance() { static Global g; return g; }
    };

    auto WndProc =[](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -> LRESULT {
        DLGPROC About =static_cast<DLGPROC>([](HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lParam*/) -> INT_PTR {
            switch( message )
            {
            case WM_INITDIALOG:
                return (INT_PTR)TRUE;
            case WM_COMMAND:
                if( LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL )
                {
                    EndDialog(hDlg, LOWORD(wParam));
                    return (INT_PTR)TRUE;
                }
                break;
            }
            return (INT_PTR)FALSE;
        });

        static HDC hMemDC = nullptr;
        static HBITMAP hBitmap = nullptr;

        switch( message )
        {
        case WM_CREATE:
            {
                HDC hDC     = GetDC(hWnd);
                hMemDC      = CreateCompatibleDC(hDC);
                hBitmap     = CreateCompatibleBitmap(hDC, WINDOW_W, WINDOW_H);
                SelectObject(hMemDC, hBitmap);
                for( auto i : { DEFAULT_GUI_FONT, DC_PEN, DC_BRUSH } ) {
                    SelectObject(hMemDC, GetStockObject(i));
                }
                ReleaseDC(hWnd, hDC);
            }
            break;
        case WM_COMMAND:
            {
                switch( LOWORD(wParam) )
                {
                case IDM_ABOUT:
                    DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                    break;
                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;
                default:
                    return DefWindowProc(hWnd, message, wParam, lParam);
                }
            }
            break;
        case WM_PAINT:
            {
                Game game = Global::GetInstance().game;

                auto hFrame = CreateSolidBrush(RGB(0, 0, 0));
                auto DrawBlock = [=](HDC hdc, Position pos, COLORREF color) {
                    RECT rc ={ pos.x, pos.y, pos.x + BLOCK_SIZE, pos.y + BLOCK_SIZE };
                    auto hBrush = CreateSolidBrush(color);
                    FillRect(hdc, &rc, hBrush);
                    FrameRect(hdc, &rc, hFrame);
                    DeleteObject(hBrush);
                };
                auto DrawMino = [=](::std::shared_ptr<Mino> mino, Position base) {
                    if( mino == nullptr ) return;
                    for( const Block& blk : mino->blocks ) {
                        if( base.y + blk.pos.y < 0 ) continue;
                        DrawBlock(hMemDC, { base.x + blk.pos.x*BLOCK_SIZE, base.y + blk.pos.y*BLOCK_SIZE }
                        , blk.color);
                    }
                };
                auto DrawField =[=]() {
                    for( const auto& line : game.m_field.line ) {
                        for( const FieldBlock& blk : line ) {
                            DrawBlock(hMemDC, { blk.pos.x*BLOCK_SIZE + PADDING_X, blk.pos.y*BLOCK_SIZE + PADDING_Y }, blk.color);
                        }
                    }
                };
                auto DrawFrame = [=](RECT rc) {
                    FrameRect(hMemDC, &rc, hFrame);
                };

                {
                    RECT rc ={ 0, 0, WINDOW_W, WINDOW_H };
                    FillRect(hMemDC, &rc, NULL);
                }

                DrawFrame({ PADDING_X, PADDING_Y, PADDING_X + BLOCK_SIZE*MAX_AREA_W, PADDING_Y + BLOCK_SIZE*MAX_AREA_H });

                {
                    const LONG left = PADDING_X + BLOCK_SIZE*MAX_AREA_W + 40;
                    const LONG top = PADDING_Y;
                    DrawFrame({ left, top, left + NEXT_AREA_SIZE, top + NEXT_AREA_SIZE });
                    Position base{ left + NEXT_AREA_SIZE/2 - BLOCK_SIZE/4, top + NEXT_AREA_SIZE/2 - BLOCK_SIZE };
                    DrawMino(game.m_next, base);

                    SetTextColor(hMemDC, 0);
                    TCHAR buf[64];
                    wsprintf(buf, _T("%08d"), game.m_score);
                    TextOut(hMemDC, left, top + NEXT_AREA_SIZE + BLOCK_SIZE, buf, _tcslen(buf));
                }

                if( game.m_curr != nullptr ) {
                    Position base{ game.m_curr->pos.x*BLOCK_SIZE + PADDING_X, game.m_curr->pos.y*BLOCK_SIZE + PADDING_Y };
                    DrawMino(game.m_curr, base);
                }
                DrawField();

                if( game.m_curr == nullptr ) {
                    SetTextColor(hMemDC, RGB(255, 0, 0));
                    TextOut(hMemDC, 80, WINDOW_H/2, _T("GAME OVER"), 9);
                }

                DeleteObject(hFrame);

                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hWnd, &ps);

                BitBlt(hdc, 0, 0, WINDOW_W, WINDOW_H, hMemDC, 0, 0, SRCCOPY);
                EndPaint(hWnd, &ps);
            }
            break;
        case WM_ERASEBKGND:
            return TRUE;
        case WM_DESTROY:
            DeleteObject(hBitmap);
            DeleteDC(hMemDC);
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    };

    // アプリケーションの初期化を実行します:
    const int MAX_LOADSTRING = 100;
    TCHAR szTitle[MAX_LOADSTRING];                  // タイトル バーのテキスト
    TCHAR szWindowClass[MAX_LOADSTRING];            // メイン ウィンドウ クラス名
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_TETRIS, szWindowClass, MAX_LOADSTRING);

    [&](){
        WNDCLASSEX wcex;
        wcex.cbSize = sizeof(WNDCLASSEX);

        wcex.style          = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc    = WndProc;
        wcex.cbClsExtra     = 0;
        wcex.cbWndExtra     = 0;
        wcex.hInstance      = hInstance;
        wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TETRIS));
        wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
        wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_TETRIS);
        wcex.lpszClassName  = szWindowClass;
        wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

        return RegisterClassEx(&wcex);
    }();

    HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, WINDOW_W, WINDOW_H, NULL, NULL, hInstance, NULL);

    if( !hWnd ) {
        return FALSE;
    }

    auto TimerProc =[](HWND hWnd, UINT nMsg, UINT_PTR nIDEvent, DWORD dwTime) {
        Global::GetInstance().game.Update();
        InvalidateRect(hWnd, nullptr, TRUE);
    };
    SetTimer(hWnd, NULL, 1000 / 60, TimerProc);

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg;
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TETRIS));

    // メイン メッセージ ループ:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}

実行画面


ソースコード一式はこちらから取得できます。
https://github.com/srz-zumix/lambda_tetris

特に解説したりとかは無いです。
それでは!

2013年12月7日土曜日

[Visual C++] __if_exists を使ってみた

C++ Advent Calendar 2013
こちらは C++ Advent Calendar 2013 7日目の記事になります。
6日目は hgodai さんで「scoped_shared_ptrとstatic_allocatorを使ったstatic_map」でした。
8日目は tt_clown さんで「Boost 逆引きリファレンスのサンプルコードをテスト化してみる - Life like a clown」です。

当初、頭の中で考えていた妄想コードを書き起こしてうまくいったら記事にしようと思っていたのですが、
自分の脳内コンパイラーがヘボかったため、ダメでした。
というわけで、別件で書きためていた __if_exists の記事を使うことにしました。

「VC++ なる C++ に若干似たコンパイラ」のお話で大変恐縮です。<(・∀・)


はじめに
Visual Studio には __if_exists という独自機能があります。どういったものかというと、
__if_exists ( identifier ) { 
statements
};
identifier に渡した識別子が存在するならば、statements が実行される代物です。
否定形には __if_not_exists があります。

最近 __if_exists を使う機会があったので少しまとめてみました。
※ 前半は個人的に試してみたこと、後半は C++11/C++14 での検証になってます。
※ サンプルコードは include を省略してあります。

template + __if_exists で関数の呼び分け
まずは、MSDN の使用例にも書かれている関数の呼び分けです。

template<typename T>
class X : public T {
public:
    void Dump() {
        __if_exists(T::Dump) {
            T::Dump();
        }
        __if_not_exists(T::Dump) {
            ::std::cout << "T::Dump does not exist" << ::std::endl;
        }
    }
};

class A {
public:
    void Dump() {
        ::std::cout << "A::Dump()" << ::std::endl;
    }
};

class B {};

int main() {
    X<A> x1;
    X<B> x2;

    x1.Dump();
    x2.Dump();

    return 0;
}

出力
A::Dump()
T::Dump does not exist

このように関数や変数が存在したらそれを使い、存在しなければ別の方法を示すことができるのが、
__if_exists/__if_not_exists です。

多重定義の抑制
自作テスティングフレームワーク(iutest)の作成中、以下のようなコードを書けるようにしたいと思いました。
// hoge.h
IUTEST_PACKAGE(hoge_test) {
  // hogehoge
}

// hoge.cpp
#include "hoge.h"

IUTEST_PACKAGE(hoge_test) {
  // hogehoge
}

IUTEST_PACKAGE の実装はこのようになっています。
#define IUTEST_PACKAGE(name)     \
    namespace name {                                    \
    class iuTest_TestCasePackage;                       \
    static ::std::string IUTEST_ATTRIBUTE_UNUSED_       \
    iuTest_GetTestCasePackageName(const iuTest_TestCasePackage*) {  \
        return iuTest_GetTestCaseParentPackageName(static_cast<iuTest_TestCaseParentPackage*>(NULL)) + #name ".";   \
    }                                                   \
    class iuTest_TestCaseParentPackage;                 \
    static ::std::string IUTEST_ATTRIBUTE_UNUSED_       \
    iuTest_GetTestCaseParentPackageName(const iuTest_TestCaseParentPackage*) {              \
        return iuTest_GetTestCasePackageName(static_cast<iuTest_TestCasePackage*>(NULL));   \
    }                                                   \
    }                                                   \
    namespace name

IUTEST_PACKAGE マクロは名前空間の定義と特定の関数を定義します。
関数を定義するため、複数の関数定義をしてしまいコンパイルエラーとなっていました。
当時は諦めたのですが・・・ __if_not_exists 使えば、実現できることに気づいたので実装してみました。

※ ちなみに、関数でなく変数であれば多重定義できるようです。
> 同じスコープ内に重複した名前を宣言する - にっき(pseudo)

2回目以降の IUTEST_PACKAGE のときに iuTest_GetTestCasePackageName 関数と iuTest_GetTestCaseParentPackageName 関数を定義しなければよいので、__if_not_exists を使って以下のように実装しました。

#define IUTEST_PACKAGE(name)         \
    namespace name {                                        \
    class iuTest_TestCasePackage;                           \
    __if_not_exists(name::iuTest_GetTestCasePackageName) {  \
        static ::std::string IUTEST_ATTRIBUTE_UNUSED_       \
        iuTest_GetTestCasePackageName(const iuTest_TestCasePackage*) {  \
            return iuTest_GetTestCaseParentPackageName(static_cast<iuTest_TestCaseParentPackage*>(NULL)) + #name ".";   \
        }                                                   \
    }                                                       \
    class iuTest_TestCaseParentPackage;                     \
    __if_not_exists(name::iuTest_GetTestCaseParentPackageName) { \
        static ::std::string IUTEST_ATTRIBUTE_UNUSED_       \
        iuTest_GetTestCaseParentPackageName(const iuTest_TestCaseParentPackage*) {              \
            return iuTest_GetTestCasePackageName(static_cast<iuTest_TestCasePackage*>(NULL));   \
        }                                                   \
    }                                                       \
    }                                                       \
    namespace name

これで、IUTEST_PACKAGE マクロが何回でも書け、統一的なソースコードにすることができるようになりました。
(※ Visual Studio でのみの機能になるのであまりオススメはしない)
※ 宣伝
iutest は Google Test ライクでヘッダーオンリーなテスティングフレームワークです。
gtest にはない機能、C++11 対応など積極的に取り込んでいますので是非一度見てもらえると嬉しいです。ご意見などいただけるとなお嬉しいです。

隠蔽の検出
「Effective C++ 33項:継承した名前を隠蔽しないようにしよう」にあるような、
名前の隠蔽を検出することができるのでは?と思って書いてみました。

#define CHECK_HIDING(base, name) \
  __if_exists( base::name ){ static_assert(false, #name " is hiding"); }
#define CHECK_HIDING2(name) CHECK_HIDING( __super, name )

class Base {
protected:
    int m_hoge;
public:
    int GetHoge(void) const { return m_hoge; }
};

class Derived : public Base {
public:
    int m_hoge;
    int m_fuga;

    CHECK_HIDING(Base, m_fuga);
    CHECK_HIDING(Base, m_hoge);

    CHECK_HIDING2(m_fuga);
    CHECK_HIDING2(m_hoge);

    Derived() {
    }
};

結論としては、失敗。
Visual Studio には __super という拡張機能があります。
これは自身のスーパークラス(基底クラス)を指すキーワードです。

__super を使うことで基底クラスへのスコープが統一的に書け、
マクロに明示的に基底クラス名を指定しなくて済むと思ったのですが…

実際には、CHECK_HIDING2 で以下の様なエラーが吐かれます。
error C2760: 構文エラー : '識別子' でなければなりませんが、'__super' が使われています。

残念でした。
__super を使わないにしろ基底クラスをユーザーが明記しないといけないので、あまり良いマクロとはいえないですね。

C++11 との組み合わせ
ここから後半戦。__if_exists を様々な場所で使ってみました。

型推論
auto 変数と組み合わせて問題がないか検証しました。
class A {
public:
    static float get() { return 1.2f;  }
};
class B {};

int main() {
    auto a = __if_exists(A::get) {
        A::get();
    }
    __if_not_exists(A::get) {
        "not found";
    }

    auto b = __if_exists(B::get) {
        B::get();
    }
    __if_not_exists(B::get) {
        "not found";
    }

    auto c = [](){ 
        __if_exists(A::get) { return A::get(); }
        __if_not_exists(A::get) { return "not found"; }
    }();

    ::std::cout << a << ::std::endl;
    ::std::cout << b << ::std::endl;
    ::std::cout << c << ::std::endl;
    return 0;
}

出力
B::Dump
1.2
not found

問題なく使えました。
result(C++ Compiler Farm)

lambda
int main()
{
    int x=0, y=1;
    auto fa = [__if_exists(x) { x } __if_not_exists(x) { y } ]()
    { 
        __if_exists(x) {
            ::std::cout << x << ::std::endl;
        }
        __if_not_exists(x) {
            ::std::cout << y << ::std::endl;
        }
    };
    auto fb =[__if_exists(z) { z } __if_not_exists(z) { y }]()
    {
        __if_exists(z) {
            ::std::cout << z << ::std::endl;
        }
        __if_not_exists(z) {
            ::std::cout << y << ::std::endl;
        }
    };

    fa();
    fb();
}

出力
0
1
キャプチャ[]の中でも問題なく使えました。
result(C++ Compiler Farm)

decltype
■ 基本
decltype の中で使えます。
typedef decltype( 
    __if_exists( T::Dump ) { T::Dump() }
    __if_not_exists( T::Dump ) { 0 }
    ) type;

T::Dump があれば T::Dump 関数の戻り値型、なければ decltype( 0 ) になります。

■ 継承
基底クラスの選択に decltype と __if_exists を使います。
class A {
public:
    static void Dump() { ::std::cout << "A::Dump" << ::std::endl; }
};
class B {
public:
    static void Dump() { ::std::cout << "B::Dump" << ::std::endl; }
};

#define TEST1(name, base) class name : \
    public __if_exists(base) { base } __if_not_exists(base) { A } {};
#define TEST2(name, base) class name : \
    public decltype( __if_exists(base) { base() } __if_not_exists(base) { A() }) {};

TEST1(X1, B);
TEST1(Y1, C);
TEST2(X2, B);
TEST2(Y2, C);

int main()
{
    X1::Dump();
    Y1::Dump();
    X2::Dump();
    Y2::Dump();

    return 0;
}

出力
B::Dump
A::Dump
B::Dump
A::Dump

※ Visual Studio 2013 で確認。2012 ではビルドできず。
まぁ、decltype 使わなくてもできるのですが、問題なくビルドできたので。

■ 戻り値を後ろに置く関数構文
class Test1 {
public:
    static float Dump() {
        ::std::cout << "Test1::Dump" << ::std::endl;
        return 0.1f;
    }
};
class Test2 {
public:
    static int  Print() {
        ::std::cout << "Test2::Print" << ::std::endl;
        return 1;
    }
};


template<typename T>
auto F(void) ->
    decltype( __if_exists(T::Dump) { T::Dump() }
        __if_exists(T::Print) { T::Print() } )
{
    __if_exists(T::Dump ) { return T::Dump();  }
    __if_exists(T::Print) { return T::Print(); }
}

int main(int argc, const char* argv[])
{
    auto var1 = F<Test1>();
    auto var2 = F<Test2>();

    ::std::cout << var1 << ::std::endl;
    ::std::cout << var2 << ::std::endl;

    return 0;
}

出力
Test1::Dump
Test2::Print
0.1
1

※ Visual Studio 2013 で確認。2012 ではビルドできず。
こちらも問題なく動作。

= delete
delete した関数はどう判断されるのか試してみました。

class A {
public:
    ~A() = delete;
    void F() = delete;
};

int main()
{
    __if_exists(A::~A) {
        ::std::cout << "A::~A found" << ::std::endl;
    }
    __if_not_exists(A::~A) {
        ::std::cout << "A::~A not found" << ::std::endl;
    }

    __if_exists(A::F) {
        ::std::cout << "A::F found" << ::std::endl;
    }
    __if_not_exists(A::F) {
        ::std::cout << "A::F not found" << ::std::endl;
    }

    __if_exists(A::G) {
        ::std::cout << "A::G found" << ::std::endl;
    }
    __if_not_exists(A::G) {
        ::std::cout << "A::G not found" << ::std::endl;
    }

    return 0;
}

出力
A::~A found
A::F found
A::G not found

どうやら delete したものも、__if_exists が真になるようです。
当然 delete された関数は呼ぶことができないので、
このようなコードはエラーになります。
class A {
public:
    void Dump() = delete;
};

int main() {
    A a;
    __if_exists(A::Dump) {
        a.Dump();
    }
    return 0;
}

main.cpp(10): error C2280: 'void A::Dump(void)' : 削除された関数を参照しようとしています
= delete された関数は存在しないと判別されるのが自然な感じなのですが…
まだ対応できていない、ということなのでしょうかね?

using
C++11 では別名テンプレートが使えるようになったので、それでも検証。
template<typename T, typename U>
class A {
    T t;
    U u;
};

template<typename U>
using B = A<int, U>;
using C = A<int, int>;
typedef A<int, int> D;

#define CHECK(name)     \
    __if_exists(name) { \
        ::std::cout << #name " found" << ::std::endl    \
    }                   \
    __if_not_exists(name) {\
        ::std::cout << #name " not found" << ::std::endl    \
    }


int main()
{
    CHECK(A);
    CHECK(B);
    CHECK(C);
    CHECK(D);
    CHECK(A::t);
    CHECK(B::t);
    CHECK(B::u);
    CHECK(C::t);
    CHECK(D::t);

    __if_exists(B<float>) {
        ::std::cout << "B<float> found" << ::std::endl;
    }
    __if_not_exists(B<float>) {
        ::std::cout << "B<float> not found" << ::std::endl;
    }

    __if_exists(A<int, int>::t) {
        ::std::cout << "A<int, int>::t found" << ::std::endl;
    }
    __if_not_exists(A<int, int>::t) {
        ::std::cout << "A<int, int>::t not found" << ::std::endl;
    }

    return 0;
}

出力
A found
B found
C found
D found
A::t not found
B::t not found
B::u not found
C::t found
D::t found
B<float> not found
A<int, int>::t not found

非template な型の名前でないとダメなようです。
typedef や 型が決定的な using の場合は、正しく判別できるもよう。

__if_exists(A<int, int>::t) って書けたら嬉しいんだけどなぁ

Visual C++ Compiler November 2013 CTP
Visual C++ Compiler November 2013 CTP で対応した機能も調べてみました。

constexpr
みんな大好き constexpr
static const int a=1;

constexpr int fa(void)
{
    __if_exists(a) {
        return a;
    }
    __if_not_exists(a) {
        return 0;
    }
}
constexpr int fb(void)
{
    __if_exists(b) {
        return b;
    }
    __if_not_exists(b) {
        return 0;
    }
}

int main() {
    ::std::cout << fa() << ::std::endl;
    ::std::cout << fb() << ::std::endl;
    return 0;
}

出力
1
0

問題なく使えました。

C++14 auto function return type deduction
関数の戻り値型の型推論です。
static const char a[]="test";
static const double c=0.1;

auto f(void)
{
    __if_exists(a) {
        return a;
    }
    __if_not_exists(a) {
        return 0;
    }
}
auto g(void)
{
    __if_exists(b) {
        return b;
    }
    __if_not_exists(b) {
        __if_exists(c) {
            if( c > 0 )
                return c;
            else
                return 0.2;
        }
        __if_not_exists(c) {
            return 0;
        }
    }
}

int main() {
    ::std::cout << f() << ::std::endl;
    ::std::cout << g() << ::std::endl;
    return 0;
}

出力
test
0.1

これも問題なし。

C++14 generic lambdas
汎用ラムダキャプチャで試そうと思ったが、まだ非対応でした。
int main()
{
    int x=0, y=1;
    // まだできない
    [a= __if_exist(x) { x } __if_not_exist(x) { y }]()
        { ::std::cout << a << ::std::endl; }();
    return 0;
}


まとめ
長々と書き連ねましたが、まとまりのない内容でホント申し訳ありませんm(__)m
とりあえず、C++11 と __if_exists の組み合わせに関しては
= delete したときだけ気をつければだいたい使えそうな感じ
ってのがわかりました。

若干、気になる部分もありますが __if_exists 自体は非常に強力な機能です。
ただし、コンパイラに依存するのでその辺だけ気をつけて使うようにしたいです。
以上。

C++ Advent Calendar 2013 8日目は tt_clown さんです。
よろしくお願いします。

2013年12月2日月曜日

[Jenkins] ColudBees Jenkins のRSSをブログに表示してみた

Jenkins CI Advent Calendar 2013
この記事は Jenkins CI Advent Calendar 2013 の 2日目の記事です。
小ネタですが、よろしくお願いします。
CloudBees Jenkins の RSS をブログに表示
Jenkins の「RSS 最新ビルドのみ」をこのブログに表示するようにしました。
左側の一番下にあります。

本ブログは Blogger を利用してますので、Blogger での設定方法を書いておきます。
  1. レイアウトページを開く
  2. 追加したい場所の「ガジェットを追加」をクリック
  3. ガジェットから「フィード」を選択
  4. RSS の URL を取得する
  5. 「フィード URL」に RSS の URL を入力して、「次へ」をクリック
    (https だと無効な URL と言われてしまうので http にしておくこと)
  6. 適宜設定をして「保存」をクリック
  7. 配置を修正したら「配置を保存」をクリック

これで設定が完了しましたが、これだけでは表示できません。
読者の方々にはこのRSSへのアクセス権がないためです。
次から無名アカウントへのアクセス権設定を説明します。

無名アカウントからのアクセスを許可する
無名アカウントのアクセス権の設定については、こちらの Wiki に情報がありますので、基本的にはこちらを見て頂ければ OK です。ただ、Wiki を見ろだけではこの記事の意味がないので、こちらでも設定方法を書きます。

  1. CloudBees にログイン後、「Jenkinsの管理」>「Manage Roles」を開きます。
  2. anonymous 行の「全体/Read」「ジョブ/Read」のチェックボックスにチェックを入れます。
  3. 「Save」ボタンを押します。
  4. 「Jenkinsの管理」>「システムの設定」を開きます。
  5. 「Global properties」>「Enable read-only access for anonymous users」のチェックボックスにチェックを入れます。

    (これに気づかず、できないなぁと小一時間悩みました…)
  6. 「保存」もしくは「適用」ボタンを押して設定を保存します。

以上で設定完了です。
これで、ログインしていなくてもジョブの様子が見れるようになったと思います。
また、これでRSSにもアクセスできるので、Blogger のガジェットにも以下のように表示されるようになりました。



Jenkins CI Advent Calendar 2013 まだまだ空きがあります。
ちょっとしたことでもいい(と思う)ので、みなさんも是非参加してみてください~

2013年11月25日月曜日

[Visual Studio] コードフォーマッタのスタイルを自分好みに変更する

Visual Studio Express 2013 for Windows Desktop を使ってみたのですが、
if と ( の間にスペースを自動挿入する設定になっていました。

if と ( はくっつけたい派なので、設定を変更してみました。

設定方法
  1. 「ツール」メニュー - 「オプション」を選択します。
  2. オプションダイアログから「テキストエディター」 - 「C/C++」 - 「書式設定」 - 「行間」を選択
  3. 「コントロールブロックのスペース」の「制御フローステートメント内のキーワードと始めかっこの間にスペースを挿入する」のチェックボックスからチェックを外す

これで if と ( の間にスペースが入らなくなります。

これ以外にも、細かく設定できるので自分好みのスタイルに変更してみてください。

2013年11月21日木曜日

VC++ Compiler November 2013 CTP の noexcept でバグ

Visual C++ Compiler November 2013 CTP で noexcept が使えるようになったので、早速 iutest で noexcept を有効にしてビルドしたのですが、エラーになってしまいました。

再現コードはこちら。
template<typename F>
void f(F func)
{
    func();
}

void g() noexcept
{
}
void h()
{
}

int main() {
    f(g); // OK
    f(h); // error C2664
    return 0;
}

こちらを VC++ でビルドすると下記のエラーが発生します。
error C2664: 'void f<void(__cdecl *)(void) noexcept>(F)' : cannot convert argument 1 from 'void (__cdecl *)(void)' to 'void (__cdecl *)(void) noexcept'

f(g); をコメントアウトするとビルドが通ります。
f<void (*)()>(h); と書き換えてもエラーになります。
また、以下のように g と h を入れ替えてもビルドが通ります。
int main() {
    f(h); // OK
    f(g); // OK
    return 0;
}

どうやら、先に noexcept 付きで実体化されると noexcept でないものまで noexcept を要求されるようです。

gcc では当然通る。
[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

とりあえず、バグ報告しておいた。

2013年11月20日水曜日

constexpr 勉強します

Visual C++ Compiler November 2013 CTP が公開され、constexpr が使えるようになったので、
早速自作テスティングフレームワークの iutest で試してみました。
iutest には以前から constexpr 対応を入れてきましたが、動作検証を全くしてませんでした。

見事に失敗
VC++ 2013 CTP の _MSC_FULL_VER は 180021114 なので、その場合は IUTEST_HAS_CONSTEXPR=1 とすることで、constexpr なコードになります。(なるはずだった…)

で、ビルドしたのですが内部エラーやら constexpr として評価できないよ~と言われてしまいました。。。
ひとまず、対象の関数は constexpr 外して一応ビルドは通るようになったのですが…
ご覧の有様。



warning C4592: 'iutest::detail::ToHex': 'constexpr' call evaluation failed; function will be called at run-time
コンパイルタイムになってないよ警告が出まくり…
constexpr を身に付けるため一から出直したいと思います。

第6回大阪Jenkins勉強会に参加しました

第6回大阪Jenkins勉強会に参加しました。
前回参加したのが、第2回でしたので久しぶりにおじゃましました。
Ust があれば見るようにはしてましたが、今回は Windows系の話があったので行くことにしました。

遅刻した!
仕事ががが…
そんなわけで、神谷さんのセッションははじめから聞けなかったので、
あとで資料をじっくり読むとして、感想は控えておきます。
とりあえず、スクリプト大事。

窓辺に立つ執事
今回の目的のセッションで、期待通り自分が知らなかったことを色々聞けてよかったです。
主にツール周りの情報が沢山あったので嬉しかったです。Jenkins 周りはあるある~ッて感じでした。

気になったツールを自分用メモとして書いておきます。
  • Gallio
  • SonarQube
  • White
  • SpecFlow
  • Sikuli

初耳のものが多かったので、とりあえず詳細調べて良さそうなら試してみようかと。

Sonar(Qube) で C++ の静的解析ができるって情報は知ってましたが、現在は Cppcheck を使用中。
時間があれば試す。

White は UI 操作言語?みたいなので、今使っている AutoIt とくらべてみたい。

SpecFlow はデモみた感じでは使ってみたいなと思ったが、大変そうとも思った。

Sikuli は検証してみたことがあるが、その時は狙った操作が上手く書けず、それっきり放置状態。
画像の選別や一致率とかの設定が難しかったイメージ。
試したのが1年くらいなので、もう一度トライしてみようかな。

というわけで、いろいろと収穫の多い勉強会でした。
殴り書きで申し訳ないですが、感想おわり。

2013年11月11日月曜日

Arduino はじめました(最終報告)

前回から大分時間が経ってしまいました。

結果から言うと、なんとかギリギリ出来上がりました…
(当日の朝まで、現場で調整してました…)
最終的には、このような形になりました。


前回までの配線では、点灯する LED の数で明るさに差ができてしまっていたので、
アノードにつながるシフトレジスタからの出力を、トランジスタアレイで増幅してます。


あと、原因がよくわかっていないのですが、
根本的な処理をライブラリ側に書き出していたのですが、
メイン側のデータ配列(LEDの点滅パターンとか)を増やすと、
なぜかライブラリ側のデータをぶっ壊すような挙動をすることがあったり、
単純にデータが多くてもぶっ壊れたり、よくわからない動作をしていたので、
やりたいことが全部載せられませんでした。。。
結局、その辺の調整をするために当日ギリギリまで作業することに…

色々と、点灯パターンを用意したのですが…残念。


とりあえず、目的は達成したのでしばらく Arduino からは離れますが、
そのうち、イーサネットシールドでも買って、Jenkins の XFD にでもしようかなと思います。





2013年11月5日火曜日

ClangOnWin を使ってみた

clang を Windows でも簡単に使いたいと思い、ビルド済みのものを探していたら
ClangOnWin」というものを見つけたので試してみました。

インストール
ClangOnWin のページからインストーラーがダウンロードできますので、
そちらを取得したらインストーラーを起動します。(※ この記事では LLVM-3.4.svn-win32-2013.11.01-193854.exe を使用しました)


起動したら特に変更なくデフォルト設定のままインストールをします。

ツールセット
インストールが完了すると Visual Studio のプラットフォームツールセットに LLVM が追加されます。


これを LLVM-vs2013(2010,2012) にすることで clang でのビルドができます。

ビルドしてみる
早速ビルドしてみると大量に警告がでます。

Visual Studio のコンパイラオプションがないため警告が出ているだけなので、
気にすることはないですが、うるさいのでプロジェクトの修正をしました。(メンドクサイ…)

コンパイルオプションの警告を取り除きましたが、まだ警告が・・・

今回試しにビルドしているプロジェクトでは _s 系の関数は使わず pragma で警告を抑止しているため上記のような警告が出てしまった。
今後も _s に書き換える方針はないので、今回も警告を抑止した。(pragma は使えないので _CRT_SECURE_NO_WARNINGS を define しました)
実際、この辺みんなどうしているのだろうか?

ビルドエラー
LLVM-vs2013 でビルドしたのですが、 type_traits でエラーが出てしまいました。


LLVM-vs2012 でもエラーが…


error: cannot mangle RTTI descriptors for type 'codecvt' yet
ちょっとお手上げです…
もうしばらく弄ってはみるので、何かわかったらここで報告します。
(解決策も募集!)

追記 11/5
もう少し調べてみたら ClangOnWin のファイルリストのページに説明が書いてありました。
それによると、RTTI は使えないようです。また、-D_HAS_EXCEPTIONS=0 も必要なようです。
RTTI を無効にするには、「プロジェクト」 - 「プロパティ」からプロパティダイアログを開き、「C/C++」 - 「言語」 - 「ランタイム型情報を有効にする」 を 「いいえ(/GR-)」 に設定します。

これで、ビルドが通るかと思ったのですが、まだダメでした。。。残念
error : cannot mangle the name of type 'basic_streambuf' into RTTI descriptors yet


2013年10月28日月曜日

iutest v1.7.0 をリリースしました

iutest v1.7.0 をリリースしました。
今回の変更点は以下のとおりです。

  • 追加
    • 値のパラメータを operator + で連結できるように修正
    • 値のパラメータを連結する ::iutest::Concat を追加
    • テストフィクスチャの定義が省ける値のパラメータ化テストを追加(IUTEST_AP,IUTEST_INSTANTIATE_TEST_CASE_AP(Visual Studio では IUTEST_P,IUTEST_INSTANTIATE_TEST_CASE_P でも省略可能))
    • IUTEST_TYPED_TEST,IUTEST_TYPED_TEST_P の別名マクロを追加(IUTEST_T, IUTEST_TP)
    • xml 出力に timestamp を追加
    • 前提条件の検証用マクロ IUTEST_ASSUME_* を追加
    • Windows 向けメニュー登録ユーティリティを追加
    • SSTP 通知リスナーを追加
  • 修正
    • 値のパラメータ化テストが日本語テスト名に対応していなかった問題を修正
    • ビルドエラー修正
    • バグ修正

バグ修正
いろいろと更新しているうちに、エンバグや把握していなかった不具合が見つかったので、修正しました。
テストも増強したので、安定化したのではないかと思います。

SSTP 通知リスナーを追加
以前、ブログで書いた機能になります。
「[iutest] テスト結果を「伺か」にしゃべらせてみた」
こちらはお遊び的な機能になってます。

以降から、大きな機能追加を紹介します。

前提条件の検証用マクロ IUTEST_ASSUME_* を追加
ASSERT,EXPECT,INFORM とは違うフレーバーになります。
ASSUME では、検証に失敗した場合にテストをスキップします。

テストを特定の条件下でのみ実行したい場合に使用します。
例えば、整数型のサイズを条件としたときは、以下のように書きます。
struct hoge { int a; char b; };
IUTEST(Test, Size)
{
    IUTEST_ASSUME_EQ(4, sizeof(int));
    IUTEST_ASSERT_EQ(8, sizeof(hoge));
}

この機能は gtest モードでも利用可能です。

値のパラメータの連結に対応
値のパラメータを複数連結してパラメータを作成する機能を追加しました。
これにより、より複雑なパラメーターを簡単に作成できるようになりました。
// true,false + 1,10
IUTEST_INSTANTIATE_TEST_CASE_P(A1, ConcatTest
 , ::iutest::Concat(::iutest::Bool(), ::iutest::Values(1, 10)));

// operator + も利用可能
// 1-9 + true,false + 1, 10
IUTEST_INSTANTIATE_TEST_CASE_P(P5, ConcatTest
    , ::iutest::Range(1, 10) + ::iutest::Bool() + ::iutest::Values(1, 10));

テストフィクスチャの定義が省ける値のパラメータ化テストを追加
値のパラメータ化テストを書くには、テストフィクスチャの定義が必須でした。
しかしながら、ほとんどの場合、パラメータの型を決定するために定義するだけで、その作業は手間になっていました。
class BoolParamTest : public ::iutest::TestWithParam<bool> {};

IUTEST_P(BoolParamTest, Test)
{
    bool param = GetParam();
}

IUTEST_INSTANTIATE_TEST_CASE_P(My1, BoolParamTest, ::iutest::Bool());

そこで、テストフィクスチャの定義が不要な値のパラメータ化テストを追加しました。
IUTEST_AP(BoolParamTest, Test)
{
    bool param = GetParam<bool>();
}

IUTEST_INSTANTIATE_TEST_CASE_AP(My1, BoolParamTest, ::iutest::Bool());
パラメータを取り出す際は、GetParam Template 関数に型を指定して取り出します。
パラメータの型は内部的には any となっており、GetParam Template 関数は unsafe_any_cast した結果を返す点に注意してください。
(非 Template の GetParam を使うと ::iutest::any が取得できます。)

また、Visual Studio の場合、通常の IUTEST_P でもテストフィクスチャの定義が不要になります。
もちろん、定義をした場合でも今まで通り使用できます。
(※ この実装には __if_exists を使用しています。__if_exists については、後日ブログにしたいと思います。 )

Windows 向けメニュー登録ユーティリティを追加
Windows 向けに main からのテスト実行ではなく、メニューバーから任意のテストを実行するユーティリティを追加しました。
こちらは、GUI に関わるテストや統合されたアプリケーションでテストを行いたい場合に使うことを想定しています。
(io テストのような低レベルなものや、コンソールで完結できるものは通常どおり main から実行しましょう)
ようはランチャーのようなものです。

#include <util/iutest_util_menu.hpp>

// TestMenu ユーティリティのインスタンスを作る
// コンストラクタにはメニューIDの先頭の番号を指定します
static ::iuutil::TestMenu s_test_menu(4000);

/* ~中略~ */
    hWnd = CreateWindow( // ...

/* ~中略~ */

    // ウィンドウ作成後、ウィンドウのメニューハンドルを渡して Create
    s_test_menu.Create( GetMenu(hWnd) );
}

/* ~中略~ */
    switch (message)
    {
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            // メッセージハンドラに処理を追加
            s_test_menu.OnCommand(wmId);
            return DefWindowProc(hWnd, message, wParam, lParam);


こちらは実行すると以下のようにメニューが自動生成されます。

テストの出力は標準出力に吐かれるので、コンソールウィンドウを作っておくと良いかもしれません。

今後の予定
しばらくは、 C言語版の iutest_c に集中しようかな~と思います。
iutest でバグがあった場合は対応します。
バグがあったら是非報告してくださいm(__)m

2013年10月21日月曜日

休み明け

一週間ちょっと休暇を取っておりました。
休暇といっても、やること満載・仕事以上に大変なことが盛りだくさんな一週間でありましたが、
なんやかんやで過ぎてしまえばあっと言う間、楽しいお休みになりました。

まだまだ、やることが残っていますが(事後処理てきな)
それを終えたらようやくのんびりできそうです。


休みの間は一切プログラムに触れてませんでした。
こんなにプログラムをしていないのも久しぶりでございます。
脳みそがプログラムを受け付けない、なんだかそんな感覚がありますが、
そんなことも言ってられず、お仕事でリハビリ中です。

まぁ少しのんびりしたら、ぼちぼち活動していこうと思います。
では。

2013年10月9日水曜日

[iutest] テスト結果を「伺か」にしゃべらせてみた

第5回大阪Jenkins勉強会で「Jenkins に伺かせてみた」という発表があったようで、
スライドを見て面白そうだったので作ってみました。
伺かについてはこちらを参照してください。

使い方
listener/iutest_sstpnotifier.hpp をインクルードし、
IUTEST_RUN_ALLTEST を呼ぶ前に ::iutest::SetUpSSTPNotifier を呼んでください。

実行するとこのようにゴーストが結果をしゃべってくれます。

ゴーストについて

これができたからと言って、開発が楽になるとかそういったことはないんですが、
こういうこともできると楽しいですよね(^^

こちらの機能は trunk もしくは、近日公開予定の v1.7.0 に含まれます。

2013年10月1日火曜日

Arduino 自作ライブラリを作る

Arduino では C++ でライブラリを自作できるので、試してみました。

今現在、LED マトリックスで遊んでいるところなので、
それで使っている 74HC595 シフトレジスタの操作をライブラリにしてみたいと思います。

ライブラリの作成には以下のサイトを参考にしました。
arduinoIDEの使い方:自作ライブラリの作り方

ファイル構成
今回のライブラリ名は ShiftRegister とします。
構成は以下のように、任意フォルダの下に、ヘッダー・ソース・キーワードファイルを作ります。
[ShiftRegister]
|- lib74hc595.h
|- lib74hc595.cpp
|- keywords.txt
(ディレクトリ名とソースファイル名は異なっていても問題ありません。)

ヘッダー

#ifndef lib74hc595_H_
#define lib74hc595_H_
#include "arduino.h"

class ShiftRegister
{
public:
 ShiftRegister(int latch, int clock, int data);
 
public:
 void Begin(void);
 void Output(int mode, byte data);
 void Output16(int mode, int data);
 void End(void);
 
private:
 int latchPin, clockPin, dataPin;
};

#endif

Arduino の標準関数などは ardunino.h を include することで使用できます。

ソース

#include "arduino.h"
#include "lib74hc595.h"

ShiftRegister::ShiftRegister(int latch, int clock, int data)
: latchPin(latch), clockPin(clock), dataPin(data)
{
 pinMode(latchPin, OUTPUT);
 pinMode(clockPin, OUTPUT);
 pinMode(dataPin, OUTPUT);
}

void ShiftRegister::Begin(void)
{
    digitalWrite(latchPin, LOW);
}

void ShiftRegister::Output(int mode, byte data)
{
    shiftOut(dataPin, clockPin, mode, data);
}
void ShiftRegister::Output16(int mode, int data)
{
    if( mode == LSBFIRST )
    {
        shiftOut(dataPin, clockPin, mode, (data   )&0xFF);
        shiftOut(dataPin, clockPin, mode, (data>>8)&0xFF);
    }
    else
    {
        shiftOut(dataPin, clockPin, mode, (data>>8)&0xFF);
        shiftOut(dataPin, clockPin, mode, (data   )&0xFF);
    }
}

void ShiftRegister::End(void)
{
    digitalWrite(latchPin, HIGH);
}


特に気にすることなく C++ のコードを書きます。
ピン操作などは標準で用意されていますので、ただ呼ぶだけです。

キーワード
キーワードファイルを設定することで文字に色を付けることができます。
今回は以下のように設定しました。

ShiftRegister KEYWORD1
Begin KEYWORD2
End KEYWORD2
Output KEYWORD2

セットアップ
ライブラリが書けたら、Arduino IDE に認識してもらえるようにセットアップします。
と、いってもやることは先ほど作成したライブラリをディレクトリごとコピーするだけです。

コピー先はこちら > %ProgramFiles%\Arduino\libraries

コピーしたら次は使ってみましょう。

使う
作成したライブラリを使うには IDE のメニューの「スケッチ」の「ライブラリの使用」にライブラリリストが表示されるので、その中から追加した「ShiftRegister」を選択します。(名前はディレクトリ名になります。)

すると、コードにライブラリのヘッダーが include されます。

使い方も C++ とほぼ一緒です。
#include <lib74hc595.h>

int latchPin = 8;  // 74HC595のST_CPへ
int clockPin = 12; // 74HC595のSH_CPへ
int dataPin = 11;  // 74HC595のDSへ
int ledPin = 13;

ShiftRegister sr(latchPin, clockPin, dataPin);

void setup() {
  pinMode(ledPin, OUTPUT);
}
 
void loop() {
  for (int j = 0; j < 16; j++) {
    sr.Begin();
    // シフト演算を使って点灯するLEDを選択しています
    sr.Output(LSBFIRST, (1<<j)>>8);
    sr.Output(LSBFIRST, 1<<j);
    if( j % 2 == 0 ) {
      digitalWrite(ledPin, LOW);
    } else {
      digitalWrite(ledPin, HIGH);
    }
    // 送信終了後latchPinをHIGHにする
    sr.End();
//    delay(100);
  }
}

ライブラリを使用したライブラリ
別のライブラリを使用して、新しいライブラリを作ることもできます。
その場合は、普通に使用したいライブラリのヘッダーファイルを include して、
後は普通にライブラリを書くだけです。

#ifndef ledmatrix_H_
#define ledmatrix_H_
#include "arduino.h"
#include "lib74hc595.h"

class LedMatrix
{
public:
    enum {
          RED
        , GREEN
        , ORANGE
    };
    enum {
        MODE_NORMAL
        , MODE_INVERT
    };
public:
    LedMatrix(int latch, int clock, int data
    , int latch_r, int clock_r, int data_r
    , int latch_g, int clock_g, int data_g);

public:
    void setmode(int mode);
public:
    void bitblt(const uint16_t red[16], const uint16_t green[16]);
    void bitblt(const uint16_t data[16], int color);
    
    void slideout_h(const uint16_t red[][16], const uint16_t green[][16]
                      , int num, int delay_n);
    void slideout_h(const uint16_t data[][16], int num, int delay_n, int color);
    void flashout(const uint16_t array[][16], int num, int delay_n, int color);
    
private:
    void setcol(uint16_t red, uint16_t green);
    uint16_t getclearbit();
    
public:
    ShiftRegister m_raw;
    ShiftRegister m_red;
    ShiftRegister m_grn;
    
    int m_mode;
};

#endif

まとめ
まとまったコードをライブラリにすることで、メインのコードをコンパクトに書けるようになりました。
また、使い慣れた言語で書ける点も良い所です。

さて、この LED マトリックスの納期まで残り一週間ほどになってしまいました(汗
ライブラリを使ってコードの使いましができるようにはなったので、ラストスパート頑張ります。
では。

2013年9月22日日曜日

Google Test 1.7.0 がリリースされました

Google Test 1.7.0 がリリースされました。ダウンロードはこちらから。
1.6.0 から約2年ぶりのリリースになります。

1.7.0 RC の時点での変更点は「Google Test 1.7.0 RC がリリースされました」で書いていますので、
そちらを参照してください。
1.7.0 RC から 1.7.0 で、1つだけ変更があったので書き出しておきます。

新機能
New feature: Google Test now implements a protocol to allow
a test runner to detect that a test program has exited
prematurely and report it as a failure (before it would be
falsely reported as a success if the exit code is 0).
テスト開始時に、環境変数 [TEST_PREMATURE_EXIT_FILE] に指定されたファイルパスにファイルを作成します。
テスト終了時に、このファイルは削除されます。

これにより、テストが最後まで実行されたのか、途中で中断したのかを判断することができます。

まとめ
さて、gtest 1.7.0 がリリースされたことですし、iutest も 1.7.0 をリリースしていこうかと思います。
では。

2013年9月17日火曜日

続々・Arduino はじめました

さて、前回は LED を見事に焦がしてしまいましたが、
今回は、LEDマトリックスが届いたので、シフトレジスタも買って LED 光らせたので、そのご報告でございます。

シフトレジスタ
今回買ったのは 74HC595 です。
74HC595 を選んだ理由は、チュートリアルで使わていたからです。

というわけで、チュートリアルを見ながら制御してみました。
特につまずくところはありませんでした。
チュートリアルの通りに配線して、サンプルのコードをコピペですぐにできました。

(写真は撮り忘れたので割愛)
//Pin connected to ST_CP of 74HC595
int latchPin = 8;
//Pin connected to SH_CP of 74HC595
int clockPin = 12;
////Pin connected to DS of 74HC595
int dataPin = 11;

void setup() {
  //set pins to output so you can control the shift register
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  // count from 0 to 255 and display the number 
  // on the LEDs
  for (int numberToDisplay = 0; numberToDisplay < 256; numberToDisplay++) {
    // take the latchPin low so 
    // the LEDs don't change while you're sending in bits:
    digitalWrite(latchPin, LOW);
    // shift out the bits:
    shiftOut(dataPin, clockPin, MSBFIRST, numberToDisplay);  

    //take the latch pin high so the LEDs will light up:
    digitalWrite(latchPin, HIGH);
    // pause before next value:
    delay(500);
  }
}

LED マトリックス点灯式
マトリックスの資料を見たら 5V って書いてあったので、大丈夫だろとぶっ刺しました。
(前回の反省が活かされていませんね…ゾクゾクしますね…)

アノード側にシフトレジスタからの出力をつなぎます。
とりあえず全部点灯でいいのでカソード側を GND に繋ぎました。


点いた\(^o^)/

ダイナミック点灯
さて、このままでは1行/1列ずつしか LED を制御できません。

一個一個の LED を制御するためにダイナミック点灯制御を行います。
ダイナミック点灯は、1つ(1列)ずつ点灯・消灯の操作をものすごく速いスピードでやることで、
人間の目にはすべての LED が同時に制御されているかのように見せる方法です。

シフトレジスタで 1bit ずつ出力するようにずらしながら、(制御ラインの選択)
もう一方の線で LED の ON/OFF を制御します。

int latchPin = 8;  // 74HC595のST_CPへ
int clockPin = 12; // 74HC595のSH_CPへ
int dataPin = 11;  // 74HC595のDSへ
int ledPin = 13;

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
}
 
void loop() {
  // LED1からLED8までを順に光らせます
  for (int j = 0; j < 16; j++) {
    // 送信中のlatchPinはグランド(LOW)レベル
    digitalWrite(latchPin, LOW);
    // シフト演算を使って点灯するLEDを選択しています
    shiftOut(dataPin, clockPin, LSBFIRST, (1<<j)>>8);
    shiftOut(dataPin, clockPin, LSBFIRST, 1<<j);
    if( j % 2 == 0 ) {
      digitalWrite(ledPin, LOW);
    } else {
      digitalWrite(ledPin, HIGH);
    }
    // 送信終了後latchPinをHIGHにする
    digitalWrite(latchPin, HIGH);
    delay(100);
  }
}

1行だけですが、飛び飛びに LED を光らせることに成功しました~


デイジーチェーン
シフトレジスタ(74HC595) を複数つなげることで、少ないピンでよりたくさんの LED を制御できるようになります。
74HC595 の Serial Out ピンからの出力をつなげるもう1つの 74HC595 の Serial data input ピンにつなげてやります。

詳しくは、チュートリアルの Exsample2 を参照してください。

これにより、3本の制御線で 16個、24個、32個... の LED を制御できるようになります。
すごいですねぇ~

今回の LEDマトリックスは 48ピンですので、シフトレジスタが 6個必要になります。
アノード制御と赤色/緑色LED の制御にそれぞれ 16ピンずつ使い、それぞれ独立して制御しようと思っているので、
シフトレジスタを2個ずつ、デイジーチェーンでつなげました。



これで、単純計算で 6 x 3 の 18本必要だった線が、3 x 3 の 9本で済みました。

次回
次は TLC5940 を使って調光をできるようにしたいなぁと思っていたのですが、
時間があまりないので全部シフトレジスタで制御するようにして、
実コードを書いていきたいと思います。

では。

2013年9月10日火曜日

Google Test 1.7.0 RC がリリースされました

(公開されてから少し経ちましたが…)
Google Test の version 1.7.0 release candidate がリリースされました。
約2年ぶりの更新になります。

変更履歴はこちら。
Changes for 1.7.0:

* New feature: death tests are supported on OpenBSD and in iOS
  simulator now.
* New feature: Test::RecordProperty() can now be used outside of the
  lifespan of a test method, in which case it will be attributed to
  the current test case or the test program in the XML report.
* New feature (potentially breaking): --gtest_list_tests now prints
  the type parameters and value parameters for each test.
* Improvement: char pointers and char arrays are now escaped properly
  in failure messages.
* Improvement: failure summary in XML reports now includes file and
  line information.
* Improvement: the <testsuites> XML element now has a timestamp attribute.
* Improvement: When --gtest_filter is specified, XML report now doesn't
  contain information about tests that are filtered out.
* Fixed the bug where long --gtest_filter flag values are truncated in
  death tests.
* Potentially breaking change: RUN_ALL_TESTS() is now implemented as a
  function instead of a macro in order to work better with Clang.
* Compatibility fixes with C++ 11 and various platforms.
* Bug/warning fixes.
今回の変更点をざっと見ていきたいと思います。

New feature: death tests are supported on OpenBSD and in iOS simulator now.
DEATHテストが OpenBSD と iOS シミュレータでも使えるようになった。

New feature: Test::RecordProperty() can now be used outside of the lifespan of a test method, in which case it will be attributed to the current test case or the test program in the XML report
RecordProperty がテスト期間以外でも呼べるようになります。
例えば、SetUpTestCase で呼ぶと XML の <testsuite> に保存されます。

New feature (potentially breaking): --gtest_list_tests now prints the type parameters and value parameters for each test.
--gtest_list_tests で表示されるテストリストにパラメータの型や値を表示するようになりました。

v1.6.0 の場合

v1.7.0 の場合

Improvement: char pointers and char arrays are now escaped properly in failure messages.
char* や char[] のメッセージ出力が改善されました。

Improvement: failure summary in XML reports now includes file and line information.
XML の失敗メッセージにファイルと行数を含むようになりました。

Improvement: the <testsuites> XML element now has a timestamp attribute.
XML の testsuites ノードに timestamp 属性を書き込むようになりました。

Improvement: When --gtest_filter is specified, XML report now doesn't contain information about tests that are filtered out.
--gtest_filter で除外されたテストは XML に出力しないようになりました。

Fixed the bug where long --gtest_filter flag values are truncated in death tests.
--gtest_filter の文字列が長い場合に、DEATHテストに送られる文字列が切り捨てられる不具合が修正されました。

Potentially breaking change: RUN_ALL_TESTS() is now implemented as a function instead of a macro in order to work better with Clang.
RUN_ALL_TESTS がマクロから関数になりました。

Compatibility fixes with C++ 11 and various platforms.
C++11 やその他のプラットフォームへの対応が入りました。

全くもって説明なっていませんね・・・すみません。。。

おおまかにいうと
「様々なプラットフォームへの対応と XML 出力が便利になりました」って感じでしょうか。
バグ修正や細かいところで使いやすくなっているので、
とりあえず 1.7.0 RC 使って正式版に備えておくといいかもしれません。



2013年9月3日火曜日

Google App Engine SDK for PHP を使ってみた・その2 (cron とメール送信)

前回はアップロードするところまでやりました。
今回は定期実行とメール送信に対応したいと思います。

定期実行
まずは定期実行に対応していきます。
リファレンスのConfiguration/Scheduled Tasksを参考にしました。

cron.yaml の設定
定期実行をするためには、まず cron.yaml ファイルを作成します。
作成する場所は app.yaml と同じディレクトリです。

cron.yaml には以下のように記述します。
cron:
- description: default job
  url: /tasks/default
  schedule: every 5 minutes
- description: test job
  url: /tasks/test
  schedule: every 1 hours

description は説明のテキストになります。任意の文字列を入力します。
url は cron の URL になります。他の cron 重複しないものにします。
schedule が cron の実行スケジュールになります。
every 5 minutes で 5分おきに実行されます。
この辺りの書式はリファレンスを参考にしてください。

今回は特定の時間帯に 5分間隔で実行するように設定しました。
every 5 minutes from 9:00 to 17:00
cron.yaml の設定は以上で終わりです。

app.yaml の設定
次に、app.yaml を編集します。

handlers:
- url: /.*
  script: main.php
- url: /tasks/default
  script: main.php
  login: admin

handlers: に項目を追加し、url に先ほど cron.yaml で設定した url を入力します。
script は実行したい php のファイルを指定します。
login: admin とすることで、URL を開いた時にログインを促されるようになります。

アップロード
最後にアップロードします。アップロードの手順は前回と同じです。

以下のコマンドを実行すると、cron.yaml も自動的にアップロードされます。
appcfg.py update sample/

メール送信
メール送信については Services に Mail についてのリファレンスがあり、そちらに方法が書いてあります。

まずは、メールAPI のある php を require_once で参照し、use 宣言します。

require_once("/google/appengine/api/mail/MailService.php");
use google\appengine\api\mail\Message;

API Reference には mail 関数が載ってますが、そちらは使えないようです。
以下のようにしてメールを送信します。

$mail_options = [
    "sender" => "sender@example.com",
    "to" => "your@mail.com",
    "subject" => "Subject",
    "textBody" => "test message"
];

try
{
    $message = new Message($mail_options);
    $message->send();
}
catch (\InvalidArgumentException $e)
{
    echo 'send mail error';
}

sender は自分のアカウントのメールアドレスならば特に設定する必要はありません。
もし、別のドメインを使いたい場合は登録をする必要があるようです。
今回は自分のアドレスを使うのでそのままです。

また、リファレンスでは $mail_options の本文は "body" になっていますが、それだと失敗してしまいます。
ここは "textBody" もしくは "htmlBody" にする必要があるようです。

google app engine - Sending mail of GAE for PHP - Stack Overflow

これでメール送信もできました。

目的達成
とりあえず、これで特定のサイトを監視して条件によってメール送信することができました。
目的は達したので GAE とはまたしばらくは戯れることはないでしょうが、
GAE は便利なので、また何かやりたくなったら使おうと思います。

その時にこの記事が役にたてば万々歳です。(そのころには正式版になってるかも…)

2013年8月31日土曜日

Google App Engine SDK for PHP を使ってみた

Google App Engine を1年ぶりいじることにしたのですが、さっぱり忘れていたので備忘録としてこれを残しておきます。
今回、Google App Engine を使った目的はこちらになります。

当初は Python を使うことを想定していましたが、PHP の SDK が出ていました。
preview 版のようでしたがサイト監視を PHP で書いていたので、使ってみることにしました。

では、さっそくセットアップからしていきましょう。

環境セットアップ
ダウンロード
環境のダウンロードはこちらから行います。

で、SDK をダウンロードするには Limited Preview に登録しないといけないとのこと。
リンクをクリックすると Google App Engine for PHP のページが開くので、「Register for the Limited Preview」のボタンをクリック。


ログインしていたら、「Congratulations, you've been whitelisted!」と出るので、これで終わり。
セットアップ方法のページへのリンクがあるのでそこを開きます。
Windows なので、「Installing on Windows」を開きます。

そこに書かれているとおりに Python 2.7 と PHP 5.4 をダウンロード、展開します。
PHP は「VC9 x86 Thread Safe」です。
展開後、それぞれパスを通しました。

つづいて、SDK をダウンロードします。
こちらも .zip ファイルになっているので、適当な場所に展開します。

Hello World!
ダウンロードができたら、さっそく試してみましょう。こちらを参考に進めていきます。

適当なフォルダに .php ファイルを作り、簡単なコードを記述します。
<?php
    echo 'Hello World!';
(> で閉じなくていいのか…)

続いて app.yaml ファイルを作成します。
ファイルの内容は以下のように記述します。
application: sample
version: 1
runtime: php
api_version: 1

handlers:
- url: /.*
  script: sample.php
script のところは先ほど作成した php のファイル名を設定します。
このファイルは php ファイルと同じディレクトリに保存します。

次に、コマンドプロンプトを開き、
以下のコマンドを入力してテストサーバーを起動させます。
dev_appserver.py --php_executable_path=C:\Users\hogehoge\php ./sample
php_executable_path には php-cgi のあるパスを絶対パスで、
最後の引数には php と app.yaml ファイルのあるパスを渡します。

サーバーが起動したら、ブラウザで以下の URL を開きます。
http://localhost:8080/

すると、ブラウザに Hello World! と表示されます。
のはずでしたが、エラーが出ました。


エラー文でぐぐったらドンピシャな答えがありました。
google app engine - Permission denied error when i try to start appengine php server - Stack Overflow

--php_executable_path には php-cgi.exe のあるディレクトリパスではなく、php-cgi.exe のパスそのものを指定する必要があったようです。
dev_appserver.py --php_executable_path=C:\Users\hogehoge\php\php-cgi.exe ./sample

うまくいきました。


次に目的の php に差し替えてテストし、動作することを確認しました。
(今回は使わないので、いくつかのスタートガイドを飛ばしてます。)

アップロードする
まずはアプリケーションを作成します。
https://appengine.google.com/ にアクセスし、「Create Application」のボタンを押します。
必要な項目を記入して、もう一度「Create Application」ボタンを押すと作成完了です。
作成できたら、app.yaml ファイルの application の名前を登録したアプリケーションIDに変更をしておきます。
今回は便宜上 hogehoge とします。

次に以下のコマンドを実行します。
appcfg.py update sample/
最後の引数はファイルのあるパスを指定します。
実行するとメールアドレスとパスワードを聞かれるので入力し Enter 。
正常に終了すると「PM Competed update of app: hogehoge, version 1」と出力されます。

最後に、アップロードしたアプリケーションにアクセスしてみましょう。
ブラウザで http://hogehoge.appspot.com を開くと実行されます。

次回
今回無事にアップロードでき、これで目的の php が実行できるところまで出来ました。
次回は、スケジューリングとメール送信に対応していきたいと思います。

また、つづく。

2013年8月26日月曜日

PHP で HTML 解析してみた

とあるサイトで状態の変化を監視してメール通知したいなぁーと思い作ってみました。

なぜ、PHP かというと twitter の bot を作った時にいじっていたことと、
なんか簡単にできそうだったから。

参考サイト:
たった3行のコードでひたすらアイドル水着画像をあつめる(PHPで)

PHP Simple HTML DOM Parser
PHP Simple HTML DOM Parser を使いました。
これのおかげでスゴク楽に書けました~(^0^)

Jenkins の設定
メール通知とかは Cloudbees の Jenkins 使ってやりました。
プロジェクトの設定はマトリックスにして、
定期間隔に問い合わせをするジョブ(1時間分) x 必要な時間分の構成
という形にしました。

拡張E-mail プラグインが使えないのが残念ですが、
目的は果たせたので満足満足。



然うは問屋が卸さない

無料分を使いきってしまった
Cloudbees の無料の制限って、300「ビルド」だと思っていたら 300「分」だったみたいで、
(min って書いてあるし、数値の変動が腑に落ちないとは思っていたのだけど…)
1時間 x N のジョブを実行したため、あっ…という間に制限超えてしまいましたorz...

Google App Engine を使うことにした
無料でメール送信可能で cron として利用可能なサービスということで、Google App Engine を使うことにしました。
bot 作ったときも利用したのですが、メール送信も可能らしいのでこちらを選択しました。

使ったことあるしーと思ったのですが、何しろ1年以上前のことなのでサッパリ覚えてません。
というわけで、備忘録がてらにまとめつつやろうかと思います。

つづく。

2013年8月20日火曜日

iutest_c v1.3.0 をリリースしました

iutest_c 1.3.0 をリリースしました。

今回の変更点は以下のとおりです。

  • iuCombine を追加
  • IUTEST_*_FATAL_FAILURE, IUTEST_*_NON_FATAL_FAILURE を追加
  • IUTEST_*_NO_FATAL_FAILURE, IUTEST_*_NO_FAILURE を追加
  • 明示的な成功と失敗を追加
  • 値のパラメータテストの明示的登録に対応

iuCombine を追加
今回の変更での目玉です。
v1.2.0 で値のパラメータテストに対応しましたが、Values と Range しか使えませんでした。
今回 v1.3.0 で iuCombine を追加し、パラメータの組み合わせテストを書けるようになりました。

iuCombine の使い方は iutest や gtest のそれとは少し違うので簡単にですが説明しておきます。

typedef IUTEST_P_TYPE((int, char, unsigned int)) TestCombineParamType;
IUTEST_INSTANTIATE_TEST_CASE_P((int, char, unsigned int), A
                               , TestCombine, iuCombine
                               , (iuRange, ( 0, 2)), (iuStepRange, (100, 110, 2)), (iuValues, (0, 4, 2)) );

IUTEST_P(TestCombineParamType, TestCombine, Test)
{
    const int x0 = param.value0;
    const char x1 = param.value1;
    const unsigned int x2 = param.value2;
    iuConsole_Output("%d %d %d\n", x0, x1, x2);
}
まずはじめに、IUTEST_P_TYPE マクロを使ってパラメータの型を定義します。
第一引数に、型のリストを()で括った形(以下、タプルと呼びます)で渡します。
これは、IUTEST_P のテスト関数に渡されるパラメータ変数の型として使います。

次にテスト関数を作成します。
こちらは通常の値のパラメータテストと同じように IUTEST_P で定義します。
第一引数に先ほど定義した型を指定、
第二引数がテストケース名、第三引数がテスト名です。

次に組み合わせを作成します。
IUTEST_INSTANTIATE_TEST_CASE_P を使うのは通常の値のパラメータテストと同じです。
第一引数には、IUTEST_P_TYPE で渡したものと同じ型のリストをタプルで渡します。
(※ 2回も書く必要があって面倒ですが…)
第二引数には、このパラメータのインスタンスのプレフィックス名を渡します。
これは重複しない名前ならなんでも構いません。
第三引数にはテストケース名、第四引数に iuCombine を指定します。

ここからが iuCombine の特殊な書き方になります。
第五引数以降には、iuCombine のパラメータを渡します。
個々のフォーマットは、
( ジェネレータ名, (ジェネレータのパラメータ) )
のようになります。
ジェネレータとジェネレータのパラメータを () で一纏めにして渡しています。
また、ジェネレータのパラメータもタプル形式でまとめています。
ジェネレータには iuValues や iuRange などが使用できます。

最後にパラメータの取り出し方ですが、
IUTEST_P のテスト関数に param 変数名でパラメータを保持した構造体が渡されます。
各パラメータは param.value0,param.value1... のように格納されているので、
そこから参照することができます。

実行するとこんな感じになります。


IUTEST_*_FATAL_FAILURE, IUTEST_*_NON_FATAL_FAILURE を追加
gtest における spi にあたる機能に対応しました。
若干ログメッセージがあれですが、とりあえずテストが失敗することを検証できるようになりました。

IUTEST_*_NO_FATAL_FAILURE, IUTEST_*_NO_FAILURE を追加
今度は逆に、テストが失敗していないことを検証するマクロです。
サブルーチンで発生したテスト失敗を検知するために使ったりします。

明示的な成功と失敗を追加
表題通り、明示的な成功と失敗に対応しました。

値のパラメータテストの明示的登録に対応
v1.2.0 のバグ修正です。
テスト関数の自動登録が使えない環境では、テストを明示的に登録する必要がありますが、
値のパラメータテストの関数が登録できないバグを修正しています。

次のバージョン
次のバージョンでは組込み環境で使ってみたりして、コードサイズとか細かいところをケアしていこうと思います。

2013年8月12日月曜日

続・Arduino はじめました

Arduino の続報です。

あれから、ちょっと忙しくて全然コードを書けていない状態です…

とりあえず、ブレッドボード使って LED チカチカさせました。
そもそもブレッドボードってなんだ
そもそもブレッドボードの使い方がわからない、ってことでググったらいっぱい情報がでてきた。


とりあえず、+(赤) と -(青)は一直線につながっている。
それ以外は縦のラインがつながっているってことは理解しました。


(これは別のブレッドボードですが、こうやってみるとわかりやすいですね。)

調子に乗った…
13pin からの出力で LED チカチカはできたので、他のこともしてみようと
Arduino の 5V 出力から電源を取って、LED を接続してみたところ、

見事に焼き切れたΣ(゜□゜)っっ

とっても芳しい香りが…
あぁ、そうか。ですよねぇ…
抵抗つけなきゃダメですよねぇ…

LED マトリックスも届いたけど、先が不安…

2013年8月6日火曜日

[Visual Studio] 検索対象が「現在のドキュメント」に戻る

Visual Studio で検索をかけようとして [Ctrl + Shift + F] して検索文字列入力してリターン!タタターン!



だが、ヒットするはずなのに何もヒットしないっ…




と思ったら、検索対象が「現在のドキュメント」に戻ってる…





なんてことがたまにおこる。
発生条件を調べようと思って、色々試してもその時は再現しなくて、忘れた頃に発生する…


誰か原因・条件知りませんか?

2013年7月31日水曜日

Arduino はじめました

ちょっと個人的にやりたいことがあったので、Arduino をはじめました。
最終的な目標は LED マトリックス使って文字や絵を表示することです。
電子工作自体はじめてですが、頑張って納期までに完成させたいと思います。

まずは購入
というわけで、まずは Arduino を購入しました。
今回購入したのはこちら。


とりあえず、初心者なので入門キットを買いました。
LED マトリックスは目星をつけているのがあるのですが、Arduino に慣れてから購入するつもり。

早速 LED チカチカ
早速、届いたので開発環境をインストールして、サンプルを実行しました。

開発環境のインストールはこちらからしました。
http://arduino.cc/en/Guide/windows
サンプルの実行まで、手順が書いてあるのでそのとおりにやるだけでーす。


できた~(^O^)
delay の数値を弄って点滅スピードが変わるのも確認。

非常に簡単ですね!

次へ
今回購入した Arduinoをはじめようキット はこちらの本の内容を実践するためキットなのですが、

この本は買ってないのです。(2,100円だし買ってもいいのだけど…)

というわけで、早速次に何するか迷子になっているわけですが…
タクトスイッチと LED があるので、スイッチに反応して LED チカチカでも、やりましょうかね。

…えーと、ではまた次回!

2013年7月30日火曜日

[gtest] テストの書き方 TIPS

Google Test でテストを書くときのちょっとしたテクニックを紹介します。
(※ こちらの記事では gtest 1.6.0 を使用しています。)

テストフィクスチャの再利用1
テストフィクスチャは typedef で別名を定義することで、再利用できます。
class TestHoge : public ::testing::Test {};

TEST_F(TestHoge, Test)
{
}

typedef TestHoge TestFoo;

TEST_F(TestFoo, Test)
{
}
複数のテストケースで同じセットアップ処理を実行する場合に便利です。

テストフィクスチャの再利用2
値のパラメータ化テスト用に定義したテストフィクスチャは、通常のテストのテストフィクスチャとして利用できます。
class TestHoge : public ::testing::TestWithParam {};

TEST_P(TestHoge, A)
{
}

TEST_F(TestHoge, B)
{
}

INSTANTIATE_TEST_CASE_P(A, TestHoge, ::testing::Range(0, 2));
TEST_P 用の TestWithParam を継承したテストフィクスチャは、TEST_F でも使用できます。
一部分だけパラメータテストをしたい場合に便利です。

複雑なパラメータの作成
パラメータに乱数を使用したり、複雑な計算結果を利用する場合、
パラメータを格納した ::std::vector を返す関数を作成することで、楽に書けます。
::std::vector MakeParam()
{
  ::std::vector v(10);
  ::std::mt19937 engine;
  ::std::uniform_int_distribution dist(0, 1000);
  ::std::generate(v.begin(), v.end(), [&](){ return dist(engine); });
  return v;
}

INSTANTIATE_TEST_CASE_P(A, TestHoge, ::testing::ValuesIn( MakeParam() ) );

まとめ
以上で、TIPS 終わりです…
書き出してから気づきましたが、
なんか、あんま大したこと書いてないですね…

2013年7月23日火曜日

cygwin の clang パッケージで標準ライブラリが見つからないエラー

新しいノートパソコンが来たので、cygwin をインストールしなおしたら、
gcc が 4.7 系になっていたので喜んでいたら、 clang でビルドできなくなって大変困ってしまった。

エラーの内容としては、
../include/iutest_config.hpp:106:14: fatal error: 'set' file not found
#    include 
             ^
と、いう感じで標準ライブラリが見つからないようです。


皆目検討がつかず、WEB を彷徨っていたらそれっぽい情報を見つけました。
clang++ broken by recent GCC update

i386 がサポートされなくなったのか?
ん~困った。

2013/12/16 追記
-v オプションつけて詳細表示させたところ、4.5.3 を参照していることがわかりました。
clang -cc1 version 3.1 based upon LLVM 3.1 default target i386-pc-cygwin
ignoring nonexistent directory "/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++"
ignoring nonexistent directory "/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/i6
86-pc-cygwin"
ignoring nonexistent directory "/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/ba
ckward"
ignoring nonexistent directory "/usr/local/include"
ignoring nonexistent directory "/usr/lib/gcc/i686-pc-cygwin/4.5.3/include"

古い cygwin 環境から 4.5.3 一式をコピペしたところ無事ビルドできました。
(これでいいのかはわかりません。)

2013年7月18日木曜日

[iutest] TDDBC の課題の経過報告(その1)

ちょこちょこ進めていた TDDBC の課題ですが、ステップ0が終わったので経過報告でもしておこうと思います。

環境のおさらい
家の PC が壊れたこともあって、タブレットのみで作業できるように環境を整えました。
ビルドとテストは CloudBees の Jenkins を使用しました。
コミットは Red/Green/Refactoring ごとになるべく細かく行うようにしました。

解説
svn のリビジョンにそって解説していきます。
r1~r4
このへんは最初の環境セットアップをしています。
Makefile の準備やファイルの追加を行なっています。

r5
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 4)
+++ test/VendingMachineTest.cpp (revision 5)
@@ -1,3 +1,13 @@
 #include "gtest/iutest_switch.hpp"
+#include "VendingMachine.h"

+class VMTest : public ::iutest::Test
+{
+public:
+    VendingMachine vm;
+};

+IUTEST_F(VMTest, totalAmount)
+{
+    IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
+}

まずは、投入金額の取得関数のテストを書きました。
この時点ではビルドは通りません。
テストフィクスチャのことは知っているので、初めからフィクスチャを使っています。
テストフィクスチャを使うことで VendingMachine の構築などテストで共通の処理をまとめて書くことができます。

r6
Makefile を修正しました。
r7
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 6)
+++ src/VendingMachine.cpp      (revision 7)
@@ -1,2 +1,6 @@
 #include "VendingMachine.h"

+int VendingMachine::GetTotalAmount(void)
+{
+    return -1;
+}
Index: src/VendingMachine.h
===================================================================
--- src/VendingMachine.h        (revision 6)
+++ src/VendingMachine.h        (revision 7)
@@ -1,4 +1,10 @@
 #ifndef INCG_VendingMachine_H_
 #define INCG_VendingMachine_H_

+class VendingMachine
+{
+public:
+    int GetTotalAmount(void);
+};
+
 #endif
GetTotalAmount 関数を実装しました。
ここではテストが失敗するように実装をします。
テストが正しく失敗することを確認することで、テスト自体が正しく書けているかがわかります。
ここで仮にテストに成功した場合、テストの方が間違っているはずです。

こちらが Jenkins の結果。


r8
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 7)
+++ src/VendingMachine.cpp      (revision 8)
@@ -2,5 +2,5 @@

 int VendingMachine::GetTotalAmount(void)
 {
-    return -1;
+    return 0;
 }

テストが通るように実装しました。

ここで重要なのが、テストが通る最小の実装に留めることです。
分かっていてもついつい実装が進み過ぎてしまうことがあります。
今回もやってしまったので、これについては後ほど解説します。


r9
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 8)
+++ test/VendingMachineTest.cpp (revision 9)
@@ -11,3 +11,9 @@
 {
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }
+
+IUTEST_F(VMTest, insert)
+{
+    vm.Insert(100);
+    IUTEST_ASSERT_EQ(100, vm.GetTotalAmount());
+}
特にリファクタリングすることもないので、次のテストを書きました。
次はお金の投入のテストを書きました。

r10
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 9)
+++ src/VendingMachine.cpp      (revision 10)
@@ -4,3 +4,7 @@
 {
     return 0;
 }
+
+void VendingMachine::Insert(int money)
+{
+}
Index: src/VendingMachine.h
===================================================================
--- src/VendingMachine.h        (revision 9)
+++ src/VendingMachine.h        (revision 10)
@@ -5,6 +5,7 @@
 {
 public:
     int GetTotalAmount(void);
+    void Insert(int money);
 };

 #endif
テストが失敗する最小の Insert 関数を実装しました。

Jekins で失敗を確認。

r11~r13
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 10)
+++ src/VendingMachine.cpp      (revision 11)
@@ -2,9 +2,10 @@

 int VendingMachine::GetTotalAmount(void)
 {
-    return 0;
+    return m_TotalAmount;
 }

 void VendingMachine::Insert(int money)
 {
+    m_TotalAmount += money;
 }
Index: src/VendingMachine.h
===================================================================
--- src/VendingMachine.h        (revision 10)
+++ src/VendingMachine.h        (revision 11)
@@ -6,6 +6,8 @@
 public:
     int GetTotalAmount(void);
     void Insert(int money);
+private:
+    int m_TotalAmount;
 };

 #endif

テストが通るように実装しました。
が、実はここでミスをしました。
これではテストが通るための最小の実装でないからです。

r12 で意気揚々とリファクタリングしてますが、次のテストを考えた時に間違いに気づきました。
次のテストとして、100円以外のお金が投入された場合のテストを書こうと思ったのですが、
r11 の実装では、テストを失敗させることができないのです。

テストを失敗させることができないとテストの正当性が確認できなくなってしまい、
たとえそのテストが成功したとしても、誤ったテストで誤った結果を確認しているだけになってしまう恐れがあります。

というわけで、r13 で最小の実装に退行させています。
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 12)
+++ src/VendingMachine.cpp      (revision 13)
@@ -12,5 +12,5 @@

 void VendingMachine::Insert(int money)
 {
-    m_TotalAmount += money;
+    m_TotalAmount = 100;
 }

Jenkins の結果も確認しておきます。

r14~r17
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 13)
+++ test/VendingMachineTest.cpp (revision 14)
@@ -7,13 +7,20 @@
     VendingMachine vm;
 };

+class VMParamTest : public VMTest, public ::iutest::TestWithParam<int>
+{};
+
 IUTEST_F(VMTest, totalAmount)
 {
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }

-IUTEST_F(VMTest, insert)
+IUTEST_P(VMParamTest, insert)
 {
-    vm.Insert(100);
-    IUTEST_ASSERT_EQ(100, vm.GetTotalAmount());
+    int money=GetParam();
+    vm.Insert(money);
+    IUTEST_ASSERT_EQ(money, vm.GetTotalAmount());
 }
+
+IUTEST_INSTANTIATE_TEST_CASE_P(Accept, VMParamTest
+    , ::iutest::Values(10, 50, 100, 500));
100円以外のお金の投入テストに対応するため、値のパラメータ化テストを使用しました。(1000円抜けてますが…)

Insert の実装は既にあるのでビルドは通る状態のはずでしたが、
テストフレームワークの使い方を間違えたため、ビルドエラーとなってしまいました。
r15,r16 とで修正を試みていますが、そもそもテストフレームワーク側がテストフィクスチャのダイヤモンド継承に対応していませんでした。

というわけで、テストフレームワーク側に手を加えて解決しました。
Jenkins でテストが失敗することも確認。

r18
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 17)
+++ src/VendingMachine.cpp      (revision 18)
@@ -12,5 +12,5 @@

 void VendingMachine::Insert(int money)
 {
-    m_TotalAmount = 100;
+    m_TotalAmount = money;
 }
テストが通るように修正しました。
この実装では不十分なことは分かりきっていますが、ここでも最小の実装に留めています。

r19,r20
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 18)
+++ test/VendingMachineTest.cpp (revision 20)
@@ -1,26 +1,23 @@
 #include "gtest/iutest_switch.hpp"
 #include "VendingMachine.h"

-class VMTest : virtual public ::iutest::Test
+class VMTest : public ::iutest::TestWithParam<int>
 {
 public:
     VendingMachine vm;
 };

-class VMParamTest : public VMTest, public ::iutest::TestWithParam<int>
-{};
-
 IUTEST_F(VMTest, totalAmount)
 {
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }

-IUTEST_P(VMParamTest, insert)
+IUTEST_P(VMTest, insert)
 {
     int money=GetParam();
     vm.Insert(money);
     IUTEST_ASSERT_EQ(money, vm.GetTotalAmount());
 }

-IUTEST_INSTANTIATE_TEST_CASE_P(Accept, VMParamTest
+IUTEST_INSTANTIATE_TEST_CASE_P(Accept, VMTest
     , ::iutest::Values(10, 50, 100, 500));

テストのリファクタリングをしました。
ダイヤモンド継承の問題をテストフレームワーク側に解決させましたが、
そもそもそんなことをしなくてもテストフィクスチャの再利用は可能でした。
※1 その辺は後日まとめます。
※2 Google Test でもダイヤモンド継承できなかったので、移植性も考慮。

r21
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 20)
+++ test/VendingMachineTest.cpp (revision 21)
@@ -14,10 +14,19 @@

 IUTEST_P(VMTest, insert)
 {
-    int money=GetParam();
+    const int money=GetParam();
     vm.Insert(money);
     IUTEST_ASSERT_EQ(money, vm.GetTotalAmount());
 }

+IUTEST_P(VMTest, insert2)
+{
+    const int money=GetParam();
+    vm.Insert(money);
+    IUTEST_ASSERT_EQ(money, vm.GetTotalAmount());
+    vm.Insert(money);
+    IUTEST_ASSERT_EQ(money*2, vm.GetTotalAmount());
+}
+
 IUTEST_INSTANTIATE_TEST_CASE_P(Accept, VMTest
     , ::iutest::Values(10, 50, 100, 500));

続いて、2枚お金を投入したときのテストを作成しました。
(※ 1000円が抜けているのはミスです。)

テストが失敗することを確認。

r22
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 21)
+++ src/VendingMachine.cpp      (revision 22)
@@ -12,5 +12,5 @@

 void VendingMachine::Insert(int money)
 {
-    m_TotalAmount = money;
+    m_TotalAmount += money;
 }

テストが通るように修正します。
ここでようやくそれっぽくなりましたね。

r23
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 22)
+++ test/VendingMachineTest.cpp (revision 23)
@@ -29,4 +29,4 @@
 }

 IUTEST_INSTANTIATE_TEST_CASE_P(Accept, VMTest
-    , ::iutest::Values(10, 50, 100, 500));
+    , ::iutest::Values(10, 50, 100, 500, 1000));

1000円のテストが抜けていたので追加しました。

r24
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 23)
+++ test/VendingMachineTest.cpp (revision 24)
@@ -12,6 +12,11 @@
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }

+IUTEST_F(VMTest, refund)
+{
+    IUTEST_ASSERT_EQ(0, vm.Refund());
+}
+
 IUTEST_P(VMTest, insert)
 {
     const int money=GetParam();

払い戻しのテストを追加しました。
r25
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 24)
+++ src/VendingMachine.cpp      (revision 25)
@@ -14,3 +14,8 @@
 {
     m_TotalAmount += money;
 }
+
+int VendingMachine::Refund(void)
+{
+    return -1;
+}
Index: src/VendingMachine.h
===================================================================
--- src/VendingMachine.h        (revision 24)
+++ src/VendingMachine.h        (revision 25)
@@ -9,6 +9,7 @@
 public:
     int GetTotalAmount(void);
     void Insert(int money);
+    int Refund(void);
 private:
     int m_TotalAmount;
 };

払い戻しの実装をしました。

もちろんテストが失敗するように実装しています。

r26
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 25)
+++ src/VendingMachine.cpp      (revision 26)
@@ -17,5 +17,5 @@

 int VendingMachine::Refund(void)
 {
-    return -1;
+    return 0;
 }

テストが通る最小の実装です。

r27
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 26)
+++ test/VendingMachineTest.cpp (revision 27)
@@ -12,9 +12,13 @@
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }

-IUTEST_F(VMTest, refund)
+IUTEST_P(VMTest, refund)
 {
     IUTEST_ASSERT_EQ(0, vm.Refund());
+
+   const int money=GetParam();
+    vm.Insert(money);
+    IUTEST_ASSERT_EQ(money, vm.Refund());
 }

 IUTEST_P(VMTest, insert)

続いてお金を投入した後の払い戻しテストを追加しました。

実装は既にあるので、テストが失敗することを確認します。

r28
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 27)
+++ src/VendingMachine.cpp      (revision 28)
@@ -17,5 +17,5 @@

 int VendingMachine::Refund(void)
 {
-    return 0;
+    return m_TotalAmount;
 }
テストが通るように実装。

r29
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 28)
+++ test/VendingMachineTest.cpp (revision 29)
@@ -15,10 +15,12 @@
 IUTEST_P(VMTest, refund)
 {
     IUTEST_ASSERT_EQ(0, vm.Refund());
+    IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());

    const int money=GetParam();
     vm.Insert(money);
     IUTEST_ASSERT_EQ(money, vm.Refund());
+    IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }

 IUTEST_P(VMTest, insert)

最後に払い戻しした後の投入金額のテストを追加しました。
払い戻しをしたあとなので、当然金額は 0円になります。

ここでも、実装は既にありますのでテストが失敗することを確認します。


r30
Index: src/VendingMachine.cpp
===================================================================
--- src/VendingMachine.cpp      (revision 29)
+++ src/VendingMachine.cpp      (revision 30)
@@ -17,5 +17,7 @@

 int VendingMachine::Refund(void)
 {
-    return m_TotalAmount;
+    const int refund = m_TotalAmount;
+    m_TotalAmount = 0;
+    return refund;
 }

払い戻し後の投入金額の処理を実装しました。

テストも通りました。

r31
Index: test/VendingMachineTest.cpp
===================================================================
--- test/VendingMachineTest.cpp (revision 30)
+++ test/VendingMachineTest.cpp (revision 31)
@@ -12,12 +12,15 @@
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
 }

-IUTEST_P(VMTest, refund)
+IUTEST_F(VMTest, refund)
 {
     IUTEST_ASSERT_EQ(0, vm.Refund());
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());
+}

-   const int money=GetParam();
+IUTEST_P(VMTest, insertAndRefund)
+{
+    const int money=GetParam();
     vm.Insert(money);
     IUTEST_ASSERT_EQ(money, vm.Refund());
     IUTEST_ASSERT_EQ(0, vm.GetTotalAmount());

最後にリファクタリングをしています。
投入をしていない場合の払い戻しのテストを別のテストに分けました。


次へ
以上で、ステップ0が完了しました。
テストの推移ばこんな感じになりました。

オーバーフローなど考慮しないといけない点がまだありますが、次のステップ1に進みたいと思います。


まとめ
ここまでは TDDBC でやったことのおさらいなのでスムーズにいきました。
大事なことは「テストを書いたら失敗させる」・「テストを成功させるときは最小の実装で」。
TDDBC の時にも言われましたが、慣れたら一気に数回転進めるのもいいらしいです。

私はまだまだ初心者なので1つ1つやっていきたいと思います。

最後に、ここに書いた手順が正解ではありません。
むしろ間違っていたらツッコミいれてください m(__)m