stringstreamのstr()メンバ関数は、バッファの消費に関係なく全ての文字列を返す

ちょっと嵌ってた。

std::stringstream::str()メンバ関数は、事前にバッファを消費しても、消費に関係なく全文字列を返すようです。

#include <iostream>
#include <sstream>

int main()
{
    std::istringstream ss("0123456789");

    const std::size_t N = 3;
    char buffer[N + 1] = {};
    ss.read(buffer, N); // 3バイト読む&消費する

    std::cout << buffer << std::endl;
    std::cout << ss.str() << std::endl; // 消費してない(残りの全部文字列を取得することを意図してる)
}

出力:

012
0123456789

バッファの消費を利用したコードを書くときは、このメンバ関数は使えなさそう。

コンテナに不完全型の要素を許可する提案

コンテナの要素型Tが不完全型(宣言だけされて、まだ定義されていない、まだ定義が完了していない型)でも許容しよう、という提案。以下のような状況で使う:

struct Entry {
    std::list<Entry> messages;
    // ...
};

この仕様は、Boost.Containerが初期リリースからサポートしている。

Containers of Incomplete Types - Boost Container Library

Boost.Graph 出辺の数を取得する

num_out_edges()関数とかはないので、out_edges()で得られたRangeの要素をカウントする。

#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/range/distance.hpp>

using Graph = boost::adjacency_list<
    boost::listS,
    boost::vecS,
    boost::directedS
>;

using Edge = std::pair<int, int>;

enum {A, B, C, D, N};

int main()
{
    const std::vector<Edge> edges {
        {A, B},
        {A, C},
        {B, D},
        {C, D}
    };

    const Graph g(edges.begin(), edges.end(), N);

    const std::size_t num_out_edges = boost::distance(out_edges(A, g));
    std::cout << num_out_edges << std::endl;
}

出力:

2

追記:2014/06/06 17:54

out_degree()(出次数)を使えばいいようです。

#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/range/distance.hpp>

using Graph = boost::adjacency_list<
    boost::listS,
    boost::vecS,
    boost::directedS
>;

using Edge = std::pair<int, int>;

enum {A, B, C, D, N};

int main()
{
    const std::vector<Edge> edges {
        {A, B},
        {A, C},
        {B, D},
        {C, D}
    };

    const Graph g(edges.begin(), edges.end(), N);

    std::size_t num_out_edges = out_degree(A, g);
    std::cout << num_out_edges << std::endl;
}

出力:

2

Boost.Range 新しい出力フォーマット

だいぶ前のチケットですが、完了しました。今までのBoost.Rangeの出力フォーマットは、{1, 2, 3}というRangeがあったら"123"という文字列を出力するので、使いものになりませんでした。パースできない。

これを解決するために、boost::adaptors::formattedというフォーマット設定用のRangeアダプタが新しく入ります。

#include <boost/range/adaptor/formatted.hpp>
using namespace boost::adaptors;

vector<int> v = {1, 2, 3};

// デフォルトのフォーマット
cout << (v | formatted()) << endl; // "{1,2,3}"

// 区切り文字(列)を指定
cout << (v | formatted(',')) << endl;

// 区切り文字(列)と、プレフィックス/サフィックスを指定
cout << (v | formatted(',', '{', '}')) << endl;

// パイプ演算子を使わない関数表記
cout << format(v) << endl;

Boost 1.56.0のリリースにはギリギリすぎて間に合わないので、その次のリリースで入れるそうです。

unique_ptr<void>でデストラクタを呼ぶ

std::shared_ptr<void>は、代入された任意の型のデストラクタが適切に呼び出されるが、std::unique_ptr<void>はできない。この提案は、それをなんとかしようというもの。

技術的には、こういうことをする:

#include <iostream>
#include <memory>

struct X {
    ~X()
    {
        std::cout << "destructor" << std::endl;
    }
};

struct void_deleter {
    std::function<void(void*)> deleter;

    template <class F>
    void_deleter(F f)
    {
        deleter = f;
    }

    void operator()(void* p) const noexcept
    {
        deleter(p);
    }
};

int main()
{
    std::unique_ptr<void, void_deleter> p(
        new X(),
        [](void* p) { delete static_cast<X*>(p); }
        );
}

出力:

destructor

つまり、任意の型のポインタが代入された時点で、「void*型のポインタから任意の型のポインタにキャストしてdeleteする」関数オブジェクトを持たせる。これも一種のType Erasure。

ここではstd::functionでそのような関数オブジェクトを保持しているが、実際の提案では、キャプチャなしのラムダ式を関数ポインタに変換して持っているため、ほぼコストがない。

このような実装を、std::unique_ptrvoidに対する特殊化として用意し、std::unique_ptrコンストラクタ/代入演算子内部でこういうことをする、というのがこの提案。

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

ウィンドウをpopしていくような処理の場合、{1, 2, 3...}のようなpopする回数に加えてRootのような特殊な値が必要になる。

こういう場合は、boost::variant<int, PopEnum>が使えるかもしれない。

型名をデマングルする

毎回デマングルの方法を調べて使うのがめんどうなので、ラップした。デバッグ用。環境ごとに型名を統一的な表記にすることは目的にしていない。

libstdc++とlibc++の環境で、abi::__cxa_demangle()関数を使って型名をデマングルする。メモリリークしないように、かつ例外安全にしてある。

宣言:

// <shand/demangle_typename.hpp>
namespace shand {
    template <class T>
    std::string demangle_typename();
}

サンプル:

#include <iostream>
#include <vector>
#include <shand/demangle_typename.hpp>

int main()
{
    std::cout << shand::demangle_typename<int>() << std::endl;
    std::cout << shand::demangle_typename<std::vector<int>>() << std::endl;
}

出力例:

int
std::vector<int, std::allocator<int> >

エラー値と正常値を表す汎用的な型:expected

Andrei Alexandrescuが考案したExpected<T>というクラスを、いまBoostとC++標準に提案する動きがあります。

Vicente Botet EscribaさんとPierre Talbotさんが現在開発を進めているexpectedクラスは、エラー値と正常値を汎用的に表す型です。boost::optionalboost::variantの亜種で、HaskellScalaにあるEitherをエラーに特化させたものです。Haskell的にはMonadErrorに相当するそうです。私も以前、似たようなのを書きました:「エラー許容型を作った」。

ゼロ割りを適切にハンドリングする、安全な割り算関数に例外を使うと、以下のようなコードになります:

struct DivideByZero: public std::exception { … };

double safe_divide(double i, double j)
{
    if (j == 0) throw DivideByZero();
    else return i / j;
}

expectedクラスを使う場合は、以下のようになります:

enum class arithmetic_errc
{
    divide_by_zero, // 9/0 == ?
    not_integer_division // 5/2 == 2.5 (which is not an integer)
};

expected<error_condition, double> safe_divide(double i, double j)
{
    if (j == 0) return make_unexpected(arithmetic_errc::divide_by_zero);
    else return i / j;
}

expectedはテンプレート引数として、エラーを表す型と、正常値を表す型をとります。エラー値を持つexpectedオブジェクトを作るには、make_unexpected()関数を使用します。

正常値を取り出す方法はいくつかあり、まずoperator bool()value()メンバ関数を使うという、optionalと同じアプローチのものがあります:

if (auto result = safe_divide(a, b)) {  // 正常値が入っているかを判定
    double x = result.value();          // 正常値を取り出す
}
else {
    error_condition x = result.error(); // エラー値を取り出す
}

このほかに、モナド的なアプローチとして、map()メンバ関数を使うものもあります。これは、エラーになるかもしれない処理を連鎖させるときに、とくに有効です。

expected<error_condition, double> f1(double i, double j, double k)
{
    return safe_divide(j, k).map([&](double q) {
        return i + q; // safe_divide()の結果が正常値だったらこの関数が呼ばれ、
                      // エラー値だったらそれがそのまま返る。
    });
}

エラー値をハンドリングしたい場合は、catch_error()メンバ関数を使います。

expected<error_condition, double> f1(double i, double j, double k)
{
    return safe_divide(j, k).catch_error([](error_condition e) {
        std::cout << e.message() << std::endl;
    });
}

また、エラーの型はなんでも入れられるので、エラーを表す文字列を入れてもいいですし、exception_ptrを入れてもいいです。

よく考えられた、とても使いやすいクラスになってますね。

参照

汎用的なABIを定義できるようにする提案

N4028 Defining a Portable C++ ABI

C++で汎用的なABI(Application Binary Interface)を定義できるようにする提案。提案者は、MicrosoftのHerb Sutter。

いま考えられているのは、extern "C"に加えてextern "abi"を追加し、その中にC++として公開するインタフェースを定義するというもの。

extern “abi” { 
    template <class T> // テンプレートを使える
    class gadget { ... }; // クラスを使える

    class widget { 
    public: 
        virtual int add( gadget<int>& ); // 仮想関数を使える
        …
    }; 

    bool overload( widget& ); // オーバーロードもできる
    bool overload( int, gadget<float>*& ); 
}

この提案の目標としては、以下のようなものが考えられています:

  • 同じプラットフォームの、異なるコンパイラコンパイラのバージョン違いといった場合でも、オブジェクトファイルをリンクできるようにする。
  • FFIを使って、他言語からC++のライブラリを使えるようにする。(JavaのJNIや.NETのPInvokeのように)

C++14によるコンセプト定義のライブラリ:Tick

SFINAEによるメタ関数定義をラップしたマクロを提供するライブラリです。is_incrementableコンセプトの定義は、以下のように書けます:

TICK_TRAIT(is_incrementable)
{
    template<class T>
    auto requires(T&& x) -> TICK_VALID(
        x++,
        ++x
    );
};

使いやすくていいです。