boost::enable_ifの居所について

C++11のstd::enable_ifが使えない環境だと、boost::enable_ifなしにはテンプレート関数をガンガン使っていくのは厳しい。しかし、boost::enable_ifは途中で定義されている場所が変わっている。 boost 1.65.1のドキュメントを見てみよう。 Boost Utility Library - 1.65.1 enable_ifはBoost.Coreに移したと書かれている。ついでにboost/utility/enable_if.hppにも、以下のように書かれている。

/*
 * Copyright (c) 2014 Glen Fernandes
 *
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 */

#ifndef BOOST_UTILITY_ENABLE_IF_HPP
#define BOOST_UTILITY_ENABLE_IF_HPP

// The header file at this path is deprecated;
// use boost/core/enable_if.hpp instead.

#include <boost/core/enable_if.hpp>

#endif  

ではboost/core/enable_if.hppを使おう、と思って書いた所、別の環境に行くとコンパイルに失敗した。古いBoostが入っていたのだ。 その時はどうせ新しめのBoostにしかない機能をいくつか使っていたので野良で入れたものの、できれば古いBoostでも動くようにWorkaroundを設けたい。

Workaround自体は非常に簡単に実装できる。

#if BOOST_VERSION >= WHEN_ENABLE_IF_MOVED_TO_CORE
#include <boost/core/enable_if.hpp>
#else
#include <boost/utility/enable_if.hpp>
#endif

問題は、どのバージョンで移動したのかぱっと見わからなかったことだ。リリースノートも少し見たが、見つけられなかった(探し方が足りないのかも知れない)。

仕方がないのでcommit logを検索して2014年8月頃の話だということを突き止めた後(boostorg/utility: 492fd7f091c73494fb0062de586adc792f97c14)、boost.1.55.0, 1.56.0, 1.57.0をダウンロードしてきて確認した。 すると、boost 1.55.0ではutility/enable_if.hppに実装が書かれているのに対し、boost 1.56.0ではdeprecatedになっている。 というわけで、Workaroundができた。

#if BOOST_VERSION >= 105600
#include <boost/core/enable_if.hpp>
#else
#include <boost/utility/enable_if.hpp>
#endif

謎の演算子

よく知られたネタだと思うが、while文中でカウントダウンするための演算子がある。

int N = 10;
while(N --> 0)
{
    std::cout << N << ' ';
}
9 8 7 6 5 4 3 2 1 0 

逆方向もあるが、カウントダウン演算子にもインクリメント・デクリメントの前置・後置と同様の違いがある。 左向きカウントダウン演算子は、値が等しくなる時にfalseになるので、この場合最後の0は出力されない。

int N = 10;
while(0 <-- N)
{
    std::cout << N << ' ';
}
9 8 7 6 5 4 3 2 1 

というのはウソで、while(N --> 0)は正しく書くとwhile((N--) > 0)だ。

続きを読む

動的メモリ確保なしで派生クラスを格納できるクラスを作った

久方ぶりにdomain-specificでないコードを書いた気がする。

Qiitaに基本的なアイデアだけ書いたので、こちらでは少し立ち入った話をする。

tl;dr

先に最大サイズを決めておくことで、そのサイズ以下のオブジェクトを格納できるクラスを作った。 格納できるクラスはあるBaseクラスから派生している必要がある。

github.com

続きを読む

constな変数の初期化とラムダ

この前、少し初期化が面倒な数値のリストを作らなければならなくなった。しかも結構急いでいた。

適当な例として以下のようなものを考える。

const std::vector<std::size_t> indices{1,4,7,10,13, ... 298}; //流石に手では書けない

こんなもの、for文を使えばいいのでは?

std::vector<std::size_t> indices;
for(std::size_t i=0; i<100; ++i)
    indices.push_back(i*3+1);

上のコードは完璧ではない(ex. reserveするべきだ)が、とりあえず動きはする。

しかしその後少し面倒なことをするので、できれば私はこれをconstにしたかった。不注意で書き換えてしまうのが怖かった。

外部でCSVを作って、

const std::vector<std::size_t> indices{
#include "indices.csv"
};

とするのも一瞬考えたが、流石に却下した。

例えばBoost Iteratorを使えば、これは普通にIteratorを2つ渡してその範囲を使って初期化するコンストラクタが使える。

const auto f = [](std::size_t i){return i*3+1;};
const std::vector<std::size_t> indices(
    boost::make_transform_iterator(
        boost::make_counting_iterator<std::size_t>(1), f),
    boost::make_transform_iterator(
        boost::make_counting_iterator<std::size_t>(100), f)
    );

だが長い。正しいやり方だが、その時私は本当に急いでいたので(実際、マクロ定義をして一部のコードを生成した程だ)、これはちょっと許容できなかった。

そして、関数の返却値としてならconstで受けられる、と思ったあと、一瞬関数を作ろうとして、すぐに一時的なその場で使える関数の存在を思い出した。

lambdaだ。

const std::vector<std::size_t> indices = [](){ // ラムダ定義開始
    std::vector<std::size_t> v;
    for(std::size_t i=0; i<100; ++i)
        v.push_back(i*3+1);
    return v;
}(); // lambda定義を終了し "}"、即座に呼び出す "()"。
// その場でラムダの中身が実行され、値が返る。それが`indices`になる。

その場でlambdaを作って、その中で必要なものを生成し、その場でconstなものに代入した。

今までstd::algorithmとの組み合わせとか、関数を渡すというような状況でばかり使ってきたが、lambdaにはこんな使い方もある。綺麗な書き方かどうかはおいておいて。

現代のC++

C++17での機能追加について、ネット上では「項目一覧見ただけで読む気失せる」「入門不可能」「闇」「誰も使いこなせない」「絶対に使いたくない」などと盛り上がっている。

だが私は一概にはC++はどんどん初心者を見捨てているとは言えないと思っている。C++は規格のアップデートの度に、その場しのぎだったり一貫性を欠いたりするルールを廃し(vector<vector<int> >の空白や、template template parameterにtypenameを許可など)、より意図が明快で細かいことを考えなくていい文法が追加され(型推論、構造化束縛、range-based forなど)、古式ゆかしい形式よりも安全で使いやすいライブラリ(optional、string_viewなど)が追加されてきた。たまにC++98を使うとよくわかるが、最新のC++はかなりユーザーに優しい。

つまりどちらかというとC++はアップデートの度にユーザーの方に寄ってきているのだ。ユーザーが欲していた機能を追加し、ユーザーの評判が悪かった機能を捨てて。捨てることになったものを中々本当には切り離さないので(トライグラフは誰が使っていたのだろう)外から見ていると肥大化しているように見えるが、使っている側としてはどんどん便利になってきていると感じている。むしろ、単に使うだけの静的スクリプト言語という用途にすらなり得ると思っている。インタプリタないけど(CLing使ったことなし)。

続きを読む

バイナリファイルの取り扱い

データの保存のために、バイナリフォーマットは広く使われている。なのでそれを読まなければならないケースが多い。

C++におけるバイナリの読み方の一番の基本はstreamの関数std::basic_istream::read(char_type*, streamsize)だろう。 この関数は第二引数で示された文字数だけ読み込み、第一引数で示された位置へその内容を書き込む。 使い方は以下だ。

std::ifstream ifs("example.bin", std::ios::in | std::ios::binary);
std::int32_t i;
ifs.read(reinterpret_cast<char*>(std::addressof(i)),
         sizeof(std::int32_t)/* or simply 4 */);

なので、基本的に以下のような関数を用意することで、ストリームから簡単にバイナリ値を読み込むことができる。

template<typename T>
T read_binary_as(std::istream& is)
{
    T val;
    is.read(reinterpret_cast<char*>(std::addressof(val)),
            sizeof(T));
    return val;
}

const auto i = read_binary_as<std::int32_t>(ifs);

書き出しの時は同様にbasic_ostream::write(const char_type*, streamsize);が使える。

std::ofstream ofs("example.bin", std::ios::out | std::ios::binary);
const std::int32_t i = 42;
ofs.write(reinterpret_cast<const char*>(std::addressof(i)),
          sizeof(std::int32_t))

ラッパーを書くとすれば

template<typename T>
std::ostream& write_as_binary(std::ostream& os, const T& v)
{
    is.write(reinterpret_cast<const char*>(std::addressof(val)),
             sizeof(T));
    return val;
}

という感じだろうか。

続きを読む

変数への参照と「変数そのもの」

Pythonは最近非常に流行っているので、静的型付け言語が好きな私も無視できないどころか使って便利だと感じることもある。 特に大抵のライブラリがPython APIを提供しているので、ライブラリを組み合わせて何かを組み上げる時は向いているだろう。

そんな感じの認識なので、私はPythonでまとまったコードを書いたことはなく、せいぜいで数百行、関数数個にユーザー定義オブジェクトもあって数個、で事足りるような程度の小物しか書いてこなかった。 なので、私のPythonの知識は入門を終えたところ、という感じだ。そこにC++等他言語の知識・経験を持ってきて必要に応じてやっていっている。

ところで、先ほど以下のようなツイートを見た。

それを見て試して(実際にツイートのようになった)、そのあまりの非直感的な挙動に少し驚いたあと、以前同じくTwitter経由で発見した以下の質問を思い出した。

ja.stackoverflow.com

このそれぞれの予想外の動作は、似た理由に根ざしているのではと思ったのだ。つまり、オブジェクトが基本的に参照で取り回されるという特徴である(後者にはスコープの問題も絡むが)。 何が起きているか説明するために、上記リンク先からコードをいくつか引用しよう。

続きを読む