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 まだまだ空きがあります。
ちょっとしたことでもいい(と思う)ので、みなさんも是非参加してみてください~