生文字列リテラルで簡単な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();
}

参照

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のリリースにはギリギリすぎて間に合わないので、その次のリリースで入れるそうです。