2014年6月25日水曜日

[C11] _Generic を使ってみた

gcc 4.9 で _Generic が使えるようになったので、使ってみました。
(clang では結構前から使えたみたいですね)

_Generic とは?
_Generic(制御式, 型名1: 式1, 型名2: 式2…)
_Generic は与えられた型によって評価される式を決定する式です。
これはコンパイル時に決定されます。

例えば、以下のように使用できます。
#define abs(x) _Generic((x), long: labs, double: fabs, default: abs)(x)

_Generic 自体については他のサイトも参考にしてください。

やろうとしてできなかったこと

#define printf_dec_format(x) _Generic((x), \
    char: "%c", \
    signed char: "%hhd", \
    unsigned char: "%hhu", \
    signed short: "%hd", \
    unsigned short: "%hu", \
    signed int: "%d", \
    unsigned int: "%u", \
    long int: "%ld", \
    unsigned long int: "%lu", \
    long long int: "%lld", \
    unsigned long long int: "%llu", \
    float: "%f", \
    double: "%f", \
    long double: "%Lf", \
    char *: "%s", \
    void *: "%p")
Rob's Programming Blog: C11 - Generic Selections

#define Message(a, b) \
    printf("a = " printf_dec_format(a) "\nb = " \
         printf_dec_format(b) "\n", a, b)

文字列リテラルは以下のように途切れていても1つの文字列リテラルとして扱われるので、それを期待したのですがダメでした。
printf( "aaaa" "bbbb" "\n");

メンドクサイですが、このように書く必要があるようです。
#define PRINTF_FORMAT_PARAM(prefix, x, postfix) _Generic((x) \
    , char : prefix "%c" postfix                \
    , signed char: prefix "%hhd" postfix        \
    , unsigned char: prefix "%hhu" postfix      \
    , signed short: prefix "%hd" postfix        \
    , unsigned short: prefix "%hu" postfix      \
    , signed int: prefix "%d" postfix           \
    , unsigned int: prefix "%u" postfix         \
    , signed long: prefix "%ld" postfix         \
    , unsigned long: prefix "%lu" postfix       \
    , char* : prefix "%s" postfix               \
    , void* : prefix "%p" postfix               \
    , default: prefix "%p" postfix              \
    )

#define PRINTF_FORMAT_PARAM2(str0, x, str1, y, str2) _Generic((x)    \
    , char : PRINTF_FORMAT_PARAM(y, str0 "%c" str1, str2)            \
    , signed char: PRINTF_FORMAT_PARAM(y, str0  "%hhd" str1, str2)   \
    , unsigned char: PRINTF_FORMAT_PARAM(y, str0  "%hhu" str1, str2) \
    , signed short: PRINTF_FORMAT_PARAM(y, str0  "%hd" str1, str2)   \
    , unsigned short: PRINTF_FORMAT_PARAM(y, str0  "%hu" str1, str2) \
    , signed int: PRINTF_FORMAT_PARAM(y, str0  "%d" str1, str2)      \
    , unsigned int: PRINTF_FORMAT_PARAM(y, str0  "%u" str1, str2)    \
    , signed long: PRINTF_FORMAT_PARAM(y, str0  "%ld" str1, str2)    \
    , unsigned long: PRINTF_FORMAT_PARAM(y, str0  "%lu" str1, str2)  \
    , char* : PRINTF_FORMAT_PARAM(y, str0  "%s" str1, str2)          \
    , void* : PRINTF_FORMAT_PARAM(y, str0  "%p" str1, str2)          \
    , default: PRINTF_FORMAT_PARAM(y, str0  "%p" str1, str2)         \
    )

#define Message(a, b) \
    printf(PRINTF_FORMAT_PARAM2("a = ", a , "\nb = ", b, "\n"), a, b)
もっと効率的な書き方があればコメント下さい。

指定した名前が型であるかどうか調べるマクロ


というわけで、C11 でも書いてみた。

#include <stdio.h>

#define IS_TYPE_NAME(name) _Generic((void (*)(int (name)))0, void (*)(int):0, default: 1)

typedef float f;
int main()
{
    printf("%d\n", IS_TYPE_NAME(int));
    printf("%d\n", IS_TYPE_NAME(f));
    printf("%d\n", IS_TYPE_NAME(main));
    printf("%d\n", IS_TYPE_NAME(aaa));
    return 0;
}

1
1
0
0

なにかに使えそうな気がする。

iutest_c での利用
はじめに iutest_c を紹介しておきます。
iutest_c は「C言語のテスティングフレームワーク」です。C++テスティングフレームワークの iutest もありますが、こちらはC言語で書かれています。C++ のと比べると非常に貧弱なフレームワークですが、軽量であることは間違いありません。(小さなデバイスなどに載せることを想定していますが、あまり検証もできていませんし、そこに載せることを考えると軽量とは言い難いかも)

printer の改善
貧弱な点の一つとして、printer がビミョーでした。
アサーションに失敗した場合、期待値と実値を出力します。この時の出力が値ではなくメモリダンプを出力します。
IUTEST(Test, Sample)
{
    int zero=0;
    IUTEST_EXPECT_NE(zero, zero);
}
sample.c(16):error: Expected of :
zero != zero
Actual: 0x00000000 vs 0x00000000

ビミョー過ぎて説明するのもアレなんですが、一番ダメなところとして lvalue しか扱えないところです。
IUTEST_EXPECT_NE(0, 0); と書けません。

そこで _Generic!!

前述した printf_dec_format のように、型ごとに出力をしてあげることで lvalue 制限を取り払い、わかりやすい出力を実現しました。

IUTEST(Test, Sample)
{
    IUTEST_EXPECT_NE(0, 0);
    float fa = 2.0f/2;
    IUTEST_EXPECT_FLOAT_EQ(1.1f, fa);
}
sample.c:15:error: Expected of :
zero != zero
Actual: 0 vs 0
sample.c:17:error: Value of : fa
Actual: 1.000000
Expected: 1.1f
Which is: 1.100000



ネタ切れ
このブログを書きつつ、_Generic の利用方法を考えていたのですが、ネタ切れです。

_Generic 使ってもっと何かできるんじゃないかと思っていたのですが、なかなかうまくいかず。。。
例えば、decltype とか書けるんじゃないか?と思ったんですが、式しか扱えないのでダメでした。
関数オーバーロードの代用として使えるので、それで…と思ったんですが使いドコロがなかった。
IS_TYPE_NAME 使って何かできそうな気はするんですがねぇ…

まとめ
_Generic 便利だと思いますが、どの位需要があるんでしょうね?
C11 使うことができるんなら、C++ 使えばいいんじゃない?と思ってしまいます。

とはいえ、_Generic は便利です。iutest_c でも今後も使っていこうと思います。

2014年6月17日火曜日

[Jenkins][SVN plugin] svn:externals がある場合に Authentication failed することがある問題への対応

発生していた問題はこちら。
svn:externals を含むリポジトリで、外部参照先の更新があった場合に、Authentication failed が発生することがありました。
[#JENKINS-21785] Check for changes in folders linked via svn:externals fails due to missing credentials - Jenkins JIRA

こちらの問題ですが、外部参照での更新があった場合に一度は失敗するものの、もう一度ビルドすれば問題なく成功するので運用でカバーしてました。
しかしながら、それでは不満が上がってきます。

というのも、この問題は既に Fixed になっていました。なのに、なぜ未だに失敗するのか。

This bug has been resolved in Subversion Plugin 2.3. You just need to reconfigure your jobs. Please read the comments below.


コメント見ろ!ってことです。

プロジェクトの設定が必要
この問題を解決するには、プロジェクト設定を更新する必要がありました。
プロジェクトの設定を開き、「ソースコード管理」-「Subversion」-「Additional Credentials」のところの、「Add additional credentials...」ボタンを押して、認証情報を登録します。

ようは、外部参照用にも認証情報必要ってことでしょうかね。


ローカルで Jenkins 立てて検証したら問題なく動いていたので、環境で発生したりしなかったりするのかも。
参照先は違えど、認証は全く同じなのでいい感じにして欲しい。
Jenkins は SCM 関係でトラブルことが多い気がするのは気のせい?


とりあえず、これで解決したので同じ問題に頭を抱えている方の助けになれば幸いです。

2014年6月9日月曜日

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

iutest v1.9.1 をリリースしました。
今回はバグ修正がメインです。変更点は以下のとおりです。

  • 変更
    • TestFixutre を定義しなくても IUTEST_P が使えるように対応
  • 修正
    • IUTEST_P,IUTEST_TYPED_TEST_P で DISABLED_ 指定が機能しない問題を修正
    • Visual Studio 14 CTP 対応

お恥ずかしいことに、パラメーター化テストで DISABLED_ が機能しないという致命的な不具合がずーっと放置されていました。サンプルコードを修正していてようやく気づきました…orz

カバレッジ率が高いからといって慢心してましたね。
慢心・ダメ・ゼッタイ。


あとは、Visual Studio 14 CTP が公開されたので、ビルドが通るように対応しました。
基本的には VS2013 と変わらないですが、IUTEST_HAS_PEEP_FUNC が 0 になったのでご注意。


というわけで、予定外に iutest をリリースすることになりましたが、次回リリースは C言語版の iutest_c を予定しております。
では。

2014年6月2日月曜日

C++ Testing Framework の Catch を使ってみた - その2(Reporter)

第一回から大分時間が経ちましたが、第二回です。
今回は予定を変更して、先に Reporter 関係を見ていこうと思います。

デフォルト Reporter
Catch ではコマンドライン引数で Reporter の選択ができます。
-r, --reporterconsole
xml
junit
compact

よく使う Reporter は予め用意されていますので、十分だとは思いますが、
これら Reporter はユーザーが独自に定義することもできます。
今回はそこを解説します。

カスタム Reporter
Reporter は IReporter もしくは IStreamingReporter を継承して作ります。
どちらもいくつかの純粋仮想関数を持っているので、それぞれ必要な実装を定義します。
catch_legacy_reporter_adapter.h
catch_interfaces_reporter.h
IReporter と IStreamingReporter の使い分けについては調べきれていないので割愛します。

まずは、コンソールに出力するところからやりたいと思うので、IStreamingReporter を使った場合から説明します。

IStreamingReporter
#define CATCH_CONFIG_MAIN
#include <catch.hpp>
using namespace Catch;

class MyStreamingReporter : public StreamingReporterBase
{
public:
    MyStreamingReporter(ReporterConfig const& _config) : StreamingReporterBase(_config) {}

    static std::string getDescription() {
        return "MyStreamingReporter";
    }

    virtual ReporterPreferences getPreferences() const {
        ReporterPreferences prefs;
        prefs.shouldRedirectStdOut = false;
        return prefs;
    }

    virtual void assertionStarting(AssertionInfo const&) {
        stream << "ASSERTION START" << ::std::endl;
    }
    virtual bool assertionEnded(AssertionStats const& _assertionStats) {
        stream << "ASSERTION END" << ::std::endl;
        return true;
    }

    virtual void sectionStarting(SectionInfo const& _sectionInfo) {
        stream << "SECTION START: " << _sectionInfo.name << ::std::endl;
        StreamingReporterBase::sectionStarting(_sectionInfo);
    }
    virtual void sectionEnded(SectionStats const& _sectionStats) {
        stream << "SECTION END: " << _sectionStats.sectionInfo.name << ::std::endl;
        StreamingReporterBase::sectionEnded(_sectionStats);
    }
};

REGISTER_REPORTER("myreporter", MyStreamingReporter);

TEST_CASE("Test", "[sample]")
{
    int a=0;
    int b=1;
    SECTION("A")
    {
        REQUIRE(a == 0);
    }
    SECTION("B")
    {
        REQUIRE(b == 0);
    }
}

IStreamingReporter を継承した StreamingReporterBase が用意されているので、そちらを利用しました。
ポイントは3つで、
1つ目は、 Reporter のコンストラクタは ReporterConfig を受け取ること。
2つ目は、getDescription static 関数を定義すること。
最後に、REGISTER_REPORTER マクロで Reporter の登録をすることです。

登録した Reporter はコマンドライン引数で名前を指定することで使用されます。
>catch_sample -r myreporter
SECTION START: Test
SECTION START: A
ASSERTION END
SECTION END: A
SECTION END: Test
SECTION START: Test
SECTION START: B
ASSERTION END
SECTION END: B
SECTION END: Test
SECTION START: Test
SECTION END: Test

コンソールへの出力は、StreamingReporterBase クラスメンバの stream に対してするだけなので非常に簡単です。
また、出力をカスタマイズするだけなら ConsoleReporter を継承して書くと楽だと思います。

IReporter
続いて、IReporter を継承した場合です。
#define CATCH_CONFIG_MAIN
#include <catch.hpp>
using namespace Catch;

class MyReporter : public SharedImpl<IReporter>
{
    std::ostream& stream;
public:
    MyReporter(ReporterConfig const& _config) : stream(_config.stream()) {}

    static std::string getDescription() {
        return "MyStreamingReporter";
    }

    virtual bool shouldRedirectStdout() const
    {
        return true;
    }

    virtual void StartTesting() {}
    virtual void EndTesting(Totals const& totals) {}
    virtual void StartGroup(std::string const& groupName) {}
    virtual void EndGroup(std::string const& groupName, Totals const& totals) {}
    virtual void StartTestCase(TestCaseInfo const& testInfo) {}
    virtual void EndTestCase(TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr) {}
    virtual void StartSection(std::string const& sectionName, std::string const& description)
    {
        stream << "SECTION START: " << sectionName << ::std::endl;
    }
    virtual void EndSection(std::string const& sectionName, Counts const& assertions)
    {
        stream << "SECTION END: " << sectionName << ::std::endl;
    }
    virtual void NoAssertionsInSection(std::string const& sectionName) {}
    virtual void NoAssertionsInTestCase(std::string const& testName) {}
    virtual void Aborted() {}
    virtual void Result(AssertionResult const& result) {}
};

REGISTER_LEGACY_REPORTER("myreporter", MyReporter);

TEST_CASE("Test", "[sample]")
{
    int a=0;
    int b=1;
    SECTION("A")
    {
        REQUIRE(a == 0);
    }
    SECTION("B")
    {
        REQUIRE(b == 0);
    }
}

IStreamingReporter の場合とほとんど同じです。
異なる点は、REGISTER_LEGACY_REPORTER で登録することと、仮想関数の種類・引数が違うくらいです。
既存の Reporter では、XmlReporter が IReporter を継承して作られていますが、使い分けについてはよくわかってません。
使いやすい方を使えばいいと思います。

一応出力結果も載せておきます。
>catch_sample -r myreporter
SECTION START: Test
SECTION START: A
SECTION END: A
SECTION END: Test
SECTION START: Test
SECTION START: B
SECTION END: B
SECTION END: Test
SECTION START: Test
SECTION END: Test

まとめ
Catch でも GoogleTest のようなイベントリスナーが書けます。
登録したら勝手に、コマンドライン引数で呼び分けられるようになるのも嬉しいですね。

では、次回は(いつになるかわからんが) BDD-style か test-fixture について書こうと思います。