Effective Modern C++の状況

まず、C++11/14対応のEffective C++は、『Effective Modern C++』という名前になる予定です(略称は「EMC++」)。

現在、1回目のテクニカルレビューに入ったところで、発売は10月ころになるそうです。出版社はAddison-WesleyではなくO'Reillyです。

7月の第2週には、O'Reillyにリリースページができるそうです。

time_point::min()はエポックではない

std::chrono::time_point::min()静的メンバ関数は、0ではなくstd::numeric_limits::lowest()を返すので注意。

エポックから現在時間までの経過時間を取得しようとしてnow - min()とすると、意図とは違った結果になる。代わりにtime_point::time_since_epoch()メンバ関数を使おう。

参照

角度のstrong typedef

角度のstrong typedefは今まで、tagged_realのサンプルコードとして提供していましたが、たまに使うのでライブラリ化しました。

例:

#include <iostream>
#include <shand/angle.hpp>

int main()
{
    shand::degree_f deg(45.0f);
    shand::radian_f rad = shand::angle_cast<shand::radian_f>(deg);

    std::cout << rad.get() << std::endl;

//  shand::sin(deg); // compilation error
    shand::radian_f sin_result = shand::sin(rad);
    std::cout << sin_result.get() << std::endl;
}

出力:

0.785398
0.707107

角度の単位を変換するには、angle_cast()関数テンプレートを使用します。Chronoライブラリではミリ秒や秒といった単位間の変換を型昇格として表現していますが、ディグリーとラジアンの間には昇格の関係はないと判断し、関数を通して変換することにしました。

ディグリー(度数法)とラジアン(弧度法)の角度型は、以下のように定義しています:

namespace shand {

struct degree_tag {};
struct radian_tag {};

template <class FloatingPoint>
using degree_t = shand::tagged_real<FloatingPoint, degree_tag>;
using degree_f = degree_t<float>;
using degree_d = degree_t<double>;
using degree_ld = degree_t<long double>;

template <class FloatingPoint>
using radian_t = shand::tagged_real<FloatingPoint, radian_tag>;
using radian_f = radian_t<float>;
using radian_d = radian_t<double>;
using radian_ld = radian_t<long double>;

}

degree_tradian_tが、浮動小数点数型をパラメータにとる、tagged_realエイリアステンプレートで、_f_d_ldが付いているものが、浮動小数点数型を設定済みのエイリアスになります。

その他、<cmath>ヘッダで定義されている、いくつかの数学関数で、ラジアンを受け取ったり返したりする必要のあるものは、ラップして強い型付けにしています。

namespace shand {

template <class FloatingPoint>
radian_t<FloatingPoint> sin(const radian_t<FloatingPoint>& x)
{
    return radian_t<FloatingPoint>(std::sin(x.get()));
}

...

template <class FloatingPoint>
radian_t<FloatingPoint> atan2(const FloatingPoint& y,
                              const FloatingPoint& x)
{
    return radian_t<FloatingPoint>(std::atan2(y, x));
}

}

非同期APIのリトライ

非同期APIをラップして、リトライ処理を入れる。やってることは、エラーハンドラの関数オブジェクトをラップしてリトライ状態を持たせ、再帰的に非同期APIを呼んでいる。

#include <iostream>
#include <string>
#include <functional>

void asyncApi(std::function<void(const std::string& data)> callback,
              std::function<void(const std::string& error)> errorF)
{
    std::cout << "async api" << std::endl;
    errorF("error");
//  callback("data");
}

struct AsyncRetryFunctor {
    std::size_t retry_;
    std::function<void(const std::string& data)> callback_;
    std::function<void(const std::string& error)> errorF_;

    template <class Callback, class ErrorF>
    AsyncRetryFunctor(std::size_t retry, Callback callback, ErrorF errorF)
        : retry_(retry), callback_(callback), errorF_(errorF) {}

    void operator()(const std::string& error)
    {
        if (retry_ <= 0) {
            errorF_(error);
        }
        else {
            --retry_;
            asyncApi(callback_, *this);
        }
    }
};

void asyncRetry(std::size_t retry,
                std::function<void(const std::string& data)> callback,
                std::function<void(const std::string& error)> errorF)
{
    asyncApi(callback, AsyncRetryFunctor(retry, callback, errorF));
}

int main()
{
    asyncRetry(
        3,
        [](const std::string& data) {
            std::cout << data << std::endl;
        },
        [](const std::string& error) {
            std::cout << error << std::endl;
        }
    );
}

出力:

async api
async api
async api
async api
error

生文字列リテラルで簡単なJSON

C++11の生文字列リテラル(Raw String Literals)を使うと、簡単なJSONをささっと書けて便利。文字列の中に何も考えずダブルクォートを書ける。

#include <iostream>
#include <string>

int main()
{
    std::string json = R"({"user_id": 123, "name": "Akira"})";
    std::cout << json << std::endl;
}

出力:

{"user_id": 123, "name": "Akira"}

Boost.Formatと組み合わせる例:

#include <iostream>
#include <string>
#include <boost/format.hpp>

int main()
{
    std::string json = (boost::format(R"({"user_id": %1%, "name": "%2%"})")
                            % 123
                            % "Akira"
                        ).str();
    std::cout << json << std::endl;
}

special_value

整数 + 特殊な値を表現する型

これをクラステンプレート化した。

インタフェース:

namespace shand {
  template <class ValueType, class Enum>
  class special_value {
  public:
    using value_type = ValueType;
    using enum_type = Enum;

    special_value(); // initialize ValueType
    special_value(ValueType value);
    special_value(Enum e);

    special_value& operator=(ValueType value);
    special_value& operator=(Enum e);

    // どちらの値が入っているかの判定
    bool has_value() const;
    bool has_enum() const;

    // 値の取り出し
    // それぞれの型の値が入っていない場合は、boost::bad_get例外を送出する
    ValueType& get_value();
    const ValueType& get_value() const;
    Enum& get_enum();
    const Enum& get_enum() const;

    // それぞれの型の値が入っていたときに指定された関数オブジェクトを呼び出す。
    // 入っていなかったら関数オブジェクトを呼び出さない。
    // *thisを返す。
    template <class F> // void(ValueType& value) or void(ValueType& value)
    special_value& on_value(F f);
    template <class F>
    const special_value& on_value(F f) const;

    template <class F> // void(Enum value) or void(Enum& value)
    special_value& on_enum(F f);
    template <class F>
    const special_value& on_enum(F f) const;
  };
}

例:

#include <iostream>
#include <shand/special_value.hpp>

enum class PopType { Root };
std::string popToString(PopType) { return "Root"; }

int main()
{
    // size_tかPopTypeのどちらかの型の値が入る
    shand::special_value<std::size_t, PopType> pop_count = 1u;
//  pop_count = PopType::Root;

    // size_tの値が入っていたときの処理
    pop_count.on_value([](std::size_t value) {
        std::cout << value << std::endl;
    })
    // PopTypeの値が入っていたときの処理
    .on_enum([](PopType pop) {
        std::cout << popToString(pop) << std::endl;
    });
}

出力:

1

実装はboost::variantのラッパー。設計は、expectedを参考にした。クラス名はこれでいいのか、まだ悩み中。もしかしたら変えるかも。

switchを式にする in C++11

switch (and other control flow) as expressions? - Rust inspired

switch文でこんな感じに値を返したいな!という話。

auto x = switch (something) {
    case FOO: break 1;
    case BAR: break 2;
    default:  break 3;
}

C++11ならラムダ式でそういうことできるよ!、と。

auto x = [&]{ switch (something) {
    case FOO: return 1;
    case BAR: return 2;
    default:  return 3;
}}();

スマートポインタのアトミック型を許可する提案

N4058 Atomic Smart Pointers

std::atomic<T>クラスはC++11(とC++14)現在、memcpy可能な型しか入れられないのですが、以下の特殊化を追加し、スマートポインタを入れられるようにしようという提案。

  • atomic<shared_ptr<T>>
  • atomic<weak_ptr<T>>
  • atomic<unique_ptr<T>>

これができないと、アトミック型にユーザー定義型を入れようとしたときに、atomic<T*>にして生ポインタのnewdeleteを自分で書かないといけないので、事故が起きやすいという問題がありました。

関連

order_by

order_by - std-proposals

ソートするときに、左辺と右辺を比較するラムダ式を書くのがめんどくさいので、「どの要素を比較対象とするか」だけを書きたい、という目的のためのラッパー、order_by

template <class F>
struct OrderBy {
    F f;

    template <class T>
    bool operator()(const T& x, const T& y) const
    {
        return f(x) < f(y);
    }
};

template <class F>
OrderBy<F> order_by(F f)
{
    return {f};
}

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

int main()
{
    std::vector<std::pair<int, std::string>> v = {
        {3, "Alice"},
        {1, "Bob"},
        {4, "Carol"}
    };

    // firstをキーにして並び替え
    std::sort(v.begin(), v.end(), order_by([](auto x) { return x.first; }));

    for (const auto& x : v) {
        std::cout << x.first << std::endl;
    }
}

出力:

1
3
4

前に全く同じのを書いた気がする。

order_byという名前はデータベースっぽすぎる気もするので、もうちょっと考えた方がいいかもしれない。そんなこともないかもしれない。

コメント

C++14でのreturnとreturn (…)の違い

C++14で導入されるdecltype(auto)を使うと、returnreturn (…)で戻り値の型が変わる。前者は値、後者は参照となる。

decltype(auto) f()
{
    static int value = 3;
    return value;
}

decltype(auto) g()
{
    static int value = 3;
    return (value);
}

int main()
{
    int f_result = f();
    int& g_result = g();
}

参照