Technical Specificationの状況

C++17の標準ライブラリに入れる予定の機能を、std::expreimental名前空間で先行してリリースし、使用経験を得るためのTechnical Specificationの状況。

C++14は8月15日まで、各国の投票待ち。

以下のTechnical Specificationを、投票のために提出した。

  • Filesystem TS : 最終投票のための準備ができたので、もう少しでリリース。
    • 最新のドラフト仕様はN3940
  • Parallelism TS
    • 最新のドラフト仕様はN3989
  • Library Fundamentals TS : not1not2を汎用化したnot_fn(N4076)と、optionalの修正(N4078)を承認した。
    • この変更が入る前の、最新のドラフト仕様はN4023

そのほか、

  • Library Fundamentals TS バージョン2が始動した:このTSのバージョン1はこれから各国のコメントを募集するが、それは微調整程度になるので、バージョン2の準備を始める。
  • Networking TSを再始動した:このTSではネットワークバイトオーダーの変換、IPアドレスURIといった小さく基本的な部分から入れていこうとしていたが、Boost.ASIOをベースにした大きな提案を作成する、という方向に変わった。
  • Concurrency TS : executorの改良を進めているが、なかなか合意がとれない状況。なのでexecutorを一旦TSから取り除いた。
  • Concept TS : コア言語のワーキンググループで、徹底したレビューを行った。この秋には投票を開始できる見込み。

どの派生クラスのオブジェクトが代入されているかを調べる

#include <iostream>
#include <typeinfo>

struct Base {
    virtual void member_function() {} // 仮想関数を持たせておく必要がある
};
struct Derived : public Base {};

int main()
{
    Base* p = new Derived();

    if (typeid(*p) == typeid(Base)) {
        std::cout << "Base" << std::endl;
    }
    if (typeid(*p) == typeid(Derived)) {
        std::cout << "Derived" << std::endl;
    }
}

出力:

Derived

tagged_intとtagged_realのコンストラクタをexplicitにした

破壊的変更なので、ブログに載せておきます。

以下のコミットで、tagged_inttagged_realコンストラクタを、explicitに変更しました。

この変更により、これらのクラスを使っていたコードのコンパイルが通らなくなる可能性があります。

この変更をしたのは、以下のようなコードをコンパイルエラーにしたかったからです。

#include <iostream>
#include <shand/strong_typedef/tagged_int.hpp>

struct Tag {};
using integer = shand::tagged_int<int, Tag>;

void f(integer x) {}

int main()
{
    f(3);          // コンパイルエラーになってほしい
//  f(integer(3)); // こう渡してほしい
}

つまり、関数の引数としてリテラルを渡す際に、それがどの用途の数値型なのかを明示してほしかったのです。

Chronoライブラリのdurationも、コンストラクタexplicitになっているので、それに合わせた形になります。

最近のBoost

Boost 1.56.0のリリースまでもう少しかかりそうなので、Boostの近況を書きます。

  • Boostの開発リポジトリが、SubversionからGit(GitHub)に移行しました。
  • 「Boostの一部の機能を使いたいだけなのに、全部入れないといけないのはしんどい」というのに応えよう、という活動を始めました。
    • GitHubでモジュールごとにリポジトリを分けてるのも、その一環。
    • 現在、モジュール間の依存関係を整理中。そのうち何らかの方法で、「自分が使いたいモジュール(とその依存モジュール)だけを持ってくる」というのができるようになるでしょう。(現在もbcpツールを使えばできることはできる)
  • コミュニティメンテナンスチーム
    • 一人のメンテナにライブラリのメンテナンスを丸投げすると、小さなバグもなかなか直されないことがあって困る、というのがあって、その状況を改善するために、コミュニティメンテナンスチーム(CMT)というのが立ち上げられました。
    • https://svn.boost.org/trac/boost/wiki/CommunityMaintenance
    • CMTに合意したライブラリは、メンテナに限らずBoost開発者コミュニティが、バグ修正、ドキュメント修正、機能追加を行えます。
  • Boostへのライブラリリクエストの方法を改善する。

    • 今までの、Boostにライブラリを入れてもらうようリクエスト/レビューする仕組みは、なかなかレビューが始められない/採択されないという問題がありました。
    • それを改善するために、Boost Library Incubatorというのが動き出しています。
    • http://rrsd.com/blincubator.com/
  • Boostを進化させるための新たなガイドライン

    • BoostはC++03とC++11の両対応をしていますが、C++03をサポートしなければならないことが足かせになってきています。その状況を改善するために、これからのライブラリ開発のガイドラインの見直しが行われています。
    • https://svn.boost.org/trac/boost/wiki/BoostEvo
    • 概要としては、Boostは革新的なライブラリを書くことを奨励するので、新しいライブラリは新しい言語機能をどんどん使っていいよ、としていきたい、としています。
  • Boost.Asioの標準化が進められそう。
    • C++標準のNetworking TSでは、ネットワークバイトオーダーの変換、IPアドレスといった小さな機能から入れていこうとしていましたが、先日の会議では、「いいからBoost.Asioを丸ごと入れようよ」という意見が圧倒的だった、とのこと。
    • http://thread.gmane.org/gmane.comp.lib.boost.devel/252494

浮動小数点数のduration型

C++11標準で提供されているdurationエイリアスは整数のみですが、浮動小数点数もそれなりに使うので、エイリアスを用意しました。

「何秒間で処理を行う」というAPIの引数がdoubleになってる場合がけっこう多いです。

namespace shand {

template <class Rep>
using nanoseconds_t = std::chrono::duration<Rep, std::nano>;
using nanoseconds_f = nanoseconds_t<float>;
using nanoseconds_d = nanoseconds_t<double>;
using nanoseconds_ld = nanoseconds_t<long double>;

template <class Rep>
using microseconds_t = std::chrono::duration<Rep, std::micro>;
using microseconds_f = microseconds_t<float>;
using microseconds_d = microseconds_t<double>;
using microseconds_ld = microseconds_t<long double>;

template <class Rep>
using milliseconds_t = std::chrono::duration<Rep, std::milli>;
using milliseconds_f = milliseconds_t<float>;
using milliseconds_d = milliseconds_t<double>;
using milliseconds_ld = milliseconds_t<long double>;

template <class Rep>
using seconds_t = std::chrono::duration<Rep>;
using seconds_f = seconds_t<float>;
using seconds_d = seconds_t<double>;
using seconds_ld = seconds_t<long double>;

template <class Rep>
using minutes_t =  std::chrono::duration<Rep, std::ratio<60>>;
using minutes_f = minutes_t<float>;
using minutes_d = minutes_t<double>;
using minutes_ld = minutes_t<long double>;

template <class Rep>
using hours_t = std::chrono::duration<Rep, std::ratio<3600>>;
using hours_f = hours_t<float>;
using hours_d = hours_t<double>;
using hours_ld = hours_t<long double>;

} // namespace shand

関連

会議でのレビュー資料

今日は、C++標準化委員会(WG21)の会議で、C++14のDISをレビューしていました。C++14に入る機能を、細かいIssueを除いて全体的にレビューし、日本が投票する際のNational Bodyコメントを決めました。

私は、以下の2件をレビューしました。とくに問題はないです。

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