一日一吉

遊びをせんとや生れけむ、戯れせんとや生まれけん

std::begin, std::endで固定長配列のイテレータを取得する

C++学習メモ。

C++11で導入されたstd::beginstd::endを利用すると、固定長配列に対するイテレータを取得できる。

static void std_begin_t()
{
    {
        int ar[] = { 1, 9, 4 };

        for (auto it = std::begin(ar), end = std::end(ar); it != end; it++)
        {
            std::cout << *it << " ";
        }
        std::cout << std::endl;
        // 1 9 4
    }

    {
        int ar[5];
        ar[0] = 3;
        ar[1] = 5;
        ar[2] = 7;

        for (auto it = std::begin(ar), end = std::end(ar); it != end; it++)
        {
            std::cout << *it << " ";
        }
        std::cout << std::endl;
        // 3 5 7 -858993460 -858993460
    }
}

2つ目の例では、要素数5の配列を宣言し、3つ目の要素までしか値を初期化していないので、残り2つの値の出力が未初期化値となっている。

こうして取得した配列に対するイテレータは、std::vectorなどのイテレータと同じように、std::for_eachstd::sortの引数として渡すことができる。

static void ary_sort_t()
{
    int ar[5];
    ar[0] = 2;
    ar[1] = 1;
    ar[2] = 4;
    ar[3] = 5;
    ar[4] = 3;
    auto first = std::begin(ar), last = std::end(ar);

    std::sort(first, last);

    std::for_each(std::begin(ar), std::end(ar), [](int e){
        std::cout << e << " ";
    });
    std::cout << std::endl;
    // 1 2 3 4 5
}

templateクラスを書き慣れるための練習帳

C++のテンプレートクラスに慣れるために練習として書いてみた。僕が慣れるために書いた、完全なるチラシの裏の記録である。

template <typename T>
class Point
{
    T _x;
    T _y;
public:
    Point(T x, T y) : _x(x), _y(y) {}
    T x() { return _x; }
    T y() { return _y; }
    std::string to_string() {
        std::stringstream s;
        s << "(" << _x << ", " << _y << ")";
        return s.str();
    }
};

xyと2つの値を取り点を表すPointクラスである。2つの点をあらわす型をintとしたりdoubleとしたりできるよう、テンプレート化した。
使用例は以下。

static void use_Point_t()
{
    auto p_i1 = Point<int>(1, 2);
    auto p_i2 = Point<int>(1.3, 2.3);
    std::cout << "p_i1: " << p_i1.to_string() << std::endl;
    std::cout << "p_i2: " << p_i2.to_string() << std::endl;
    std::cout << "sizeof(p_i1.x()): " << sizeof(p_i1.x()) << std::endl;
    std::cout << "sizeof(p_i1): " << sizeof(p_i1) << std::endl;

    std::cout << std::endl;

    auto p_d1 = Point<double>(1, 2);
    auto p_d2 = Point<double>(1.3, 2.3);
    std::cout << "p_d1: " << p_d1.to_string() << std::endl;
    std::cout << "p_d2: " << p_d2.to_string() << std::endl;
    std::cout << "sizeof(p_d2.x()): " << sizeof(p_d2.x()) << std::endl;
    std::cout << "sizeof(p_d1): " << sizeof(p_d1) << std::endl;
}

出力は以下のようになる。

p_i1: (1, 2)
p_i2: (1, 2)
sizeof(p_i1.x()): 4
sizeof(p_i1): 8

p_d1: (1, 2)
p_d2: (1.3, 2.3)
sizeof(p_d2.x()): 8
sizeof(p_d1): 16

p_i2の初期化時に浮動小数点値をコンストラクタに渡しているが、int型に変換されて格納されるので、出力時にはp_i1と同じ値(1, 2)となる。
p_d2では座標値をdouble型で格納するので、(1.3, 2.3)と出力される。

また、p_i1ではx座標値がint型なのでサイズが4バイト、yと合わせてp_i1インスタンスのサイズは8バイトである。 一方、p_d1では座標値がdouble型なのでxだけで8バイト、yと合わせると16バイトとなる。

GPL v2のライセンス文書が作られたのは1991年

 プログラマーが仕事でプログラミングするときに、OSSのライブラリをよく使う。

 そのときに気をつけるのがライセンスで、GPLとかLGPLとかAGPLとか、「GPL」がつくものは気をつけよう、というような認識でいる。

 

 で、そもそも、そのGPLってライセンス文書の中に何書いてあるのってのが気になって、少しずつ読み始めた。

 具体的には、PostGISの最新版2.5.1のソースコードに添付されているLICENSE.TXTというファイルと、COPYINGというファイルだ。

 

 1つ目のLICENSE.TXTというファイルは、PostGISがどんなライセンスに従い配布されているかが書かれている。GPL v2またはそれ以降としている。また、PostGISが利用しているライブラリやソースコードで、GPLでないものについて列挙している。

 

 そして、2つ目のCOPYINGというファイルが、GPL v2の原文である。これを読み始めて驚いたことが2つ。

 

 1つ目は、この原文が作られたのが1991年だということ。まだインターネットが普及しておらず(日本では一部の新しいもの好きがパソコン通信をしていた頃と思う)、OSSという概念も広く世の中に知られていない頃だ。僕は小学生で、ファミコンで夢中に遊んでいたけれど、そのゲームの「プログラマ」とか「ゲームクリエイタ」といった作り手のことなんかまったく考えていなかった。

 そんな頃に、すでに「フリーソフトウェア」に関して、確固たるポリシーを持って、ライセンス文書を用意してソフトウェアを開発している人々がいたのだ。そう考えるだけでも、なんだか歴史的な重みを感じる。

 

 そして、2つ目が、このライセンス文書のPreamble(まえがき、前文、といえばいいのだろうか)の項目に、「ソフトウェアを自由に共有したり変えたりする権利をユーザに保証するためにこのライセンスがある」というようなことが書かれている。

 なんというか、ライセンスの文書って、ひたすら「これはしていい、あれはだめ」というような条件面のことを書いているものだと思ったけど、そもそもなんでこのライセンスを考えたのか、フリーソフトウェアがどうあるべきか、という「思想面」にまで踏み込んで書かれていたことに驚きを感じたのだ。

 

 まだまだ読みかけだけど、引き続きこの文書は読んでいきたい所存。

センスは知識からはじまるらしいのでつべこべ言わずにとりあえず知識を蓄積してみる

  水野学さんの『センスは知識からはじまる』を読んだ。

センスは知識からはじまる

センスは知識からはじまる

 

 

 水野さんの名前は知らなかったのだけれど、彼の仕事についてはよく知っていた(もう少し正確に言うと、多くの日本人によって知られているものだった)。docomoの「iD」のデザインやブランディング、「くまモン」のキャラクターデザインを手がけているそうだ。

 そんな、日本を代表するクリエイティブディレクターである水野さん。センスあふれる仕事をされているが、自身の「センス論」を展開している。

 まず「センス」ってそもそもなに? p.18より引用すると

「センスのよさ」とは、数値化できない事象のよし悪しを判断し、最適化する能力である。

である。

 

 そして、原則的に、センスとは、才能ではなく知識からはじまるという。

 膨大な知識を蓄積し、それらを駆使してロジカルに導き出される最適化の過程こそが、センスのよさが発揮される過程だそうである。

 

 その例として、FLANDERS LINENというプレオーガニックコットンブランディングの仕事の話が出ている。

 その話の中では、ベルギー周辺をはじめとしたヨーロッパ地方の歴史に関する知識、フォントに関する知識(歴史的な部分も含む)。「フランダースリネン」というネーミングを考える過程では、このコットンから作られるリネンの主な顧客となるであろうと想定されるターゲット層が持っているであろう知識、見たであろうテレビ番組...。

 こうした、膨大な知識が(この仕事を進める過程で調査、収集された知識もあるけれど)、最終的なブランド名やロゴというアウトプットにつながっている。

 

 こうした話を目の当たりにすると、「自分にはセンスがないから」とか、「あの人はセンスがいいから」と言い訳して片付けるわけにはいかなくなる。自分にセンスがないと感じるならば、それは才能ではなく、知識が不足しているのである。したがって、とやかく言わずに知識を吸収しまくれ、ということになる。

 そんなわけで、まずは「自分に関係ない」「興味ない」とものごとをスルーするのでなく、何事でもいちいち引っかかって咀嚼してみるところから始めたい。そうすることで、知識が自ずと増えていき、それがやがて「センス」へと結晶化されていくと期待する。

【C++】同じ生ポインタを二度shared_ptrに渡してはいけない

はじめに

 C++shared_ptrは、クラスのインスタンスC#Javaのクラスのインスタンスと同じように扱えるので便利である。
 その一方で、C++では生のポインタを取り扱うこともある。
 shared_ptrと生のポインタの混在環境で僕がしでかしたミスで、原因追求にしばらく時間を要したことがあったので、ここにまとめておきたい。
 はじめに教訓めいたものを挙げておくと、タイトルの通り「同じ生ポインタを二度shared_ptrに渡してはいけない」ということである。

現象

 以下のようなコードを実行したとき、どんな出力が得られるだろうか?

class C
{
public:
    C() : v(10) { std::cout << "C.constructor" << std::endl; }
    ~C() { std::cout << "C.destructor" << std::endl; }
    int v;
};

std::shared_ptr<C> get_c()
{
    C* c = new C;
    auto shared_c1 = std::shared_ptr<C>(c);
    auto shared_c2 = std::shared_ptr<C>(c);

    return shared_c2;
}

int main()
{
    std::shared_ptr<C> shared_c = get_c();

    std::cout << shared_c->v << std::endl;

    return 0;
}

 僕は以下のような出力を期待した。

C.constructor
10
C.destructor

 実際には、以下のような出力が得られた(VS2017の場合)。

C.constructor
C.destructor
-572662307
C.destructor

 Cのデストラクタが二度走る。さらに、main()関数内でshared_c->vのようにCインスタンスのメンバ変数の値を取得した際には、期待される10ではない、わけのわからない値が返されている。

原因

 原因はget_c()関数に内にある。以下にget_c()を再掲する。

std::shared_ptr<C> get_c()
{
    C* c = new C;
    auto shared_c1 = std::shared_ptr<C>(c);
    auto shared_c2 = std::shared_ptr<C>(c);

    return shared_c2;
}

 まず、new演算子Cインスタンスを生成する。
 次に、それをshared_ptrで管理させるために、auto shared_c1 = std::shared_ptr<C>(c)とする。ここまでは良い。
 問題は、同じインスタンスへの参照をshared_ptr型でもう一つほしいな、と思ってshared_c2変数を宣言、初期化するときに、再び生のポインタcからshared_ptrを作ってしまうことである。

 これにより、shared_c1shared_c2の参照カウンタはバラバラであり、それぞれ1となる。return shared_c2get_c()のスコープから抜ける時点で、shared_c1の側の参照カウンタが1減じられ、0となる。この時点で、shared_c1が管理するポインタ(すなわち、Cインスタンス)は他から参照されていないとみなされ、破棄される(Cのデストラクタが走る)。

 このため、main()関数側で、get_c()から受け取ったshared_ptrは、既に有効なCインスタンスを指していない。そのため、メンバ変数vの値を取得しようとすると、無効な値が返されるのである。

 さらに、main()関数のスコープから抜ける時点で、変数shared_cの参照カウンタが1減じられ、0となる。ここで再びCのデストラクタが呼び出されてしまう。したがって、出力結果の所で "C.destructor"が二度表示されていたのである。

解決

 同じ生ポインタを2度shared_ptrに渡してはいけない。したがって、get_c()は以下のように書かれるべきである。

std::shared_ptr<C> get_c()
{
    C* c = new C;
    auto shared_c1 = std::shared_ptr<C>(c);
    auto shared_c2 = shared_c1;

    return shared_c2;
}

 もっと言えば、自分でnewする限りにおいては、生のポインタを参照する変数を宣言しないほうが安全である。

std::shared_ptr<C> get_c()
{
    auto shared_c1 = std::shared_ptr<C>(new C);
    auto shared_c2 = shared_c1;

    return shared_c2;
}

 こうすれば、期待通りの出力が得られる。

C.constructor
10
C.destructor

補足

 上記の例は極端に単純化した例である。実際にget_c()のように、同じインスタンスへのshared_ptrshared_c1shared_c2と並べて宣言することなどないだろう。
 僕が出会った事例にもう少し近づけて書くと、こんな感じになる。

class C
{
public:
    C() : v(10) {
        all_c.push_back(this);
        std::cout << "C.constructor" << std::endl; 
    }
    ~C() { std::cout << "C.destructor" << std::endl; }
    int v;

    static const std::vector<C*>& get_all_c() { return all_c; }

private:
    static std::vector<C*> all_c;
};

std::vector<C*> C::all_c;

std::shared_ptr<C> get_c()
{
    auto shared_c1 = std::shared_ptr<C>(new C);
    return shared_c1;
}

 このように、生成されたCインスタンスすべてを追えるような簡易的なstatic変数として、all_cが定義されていた。
(この実装自体の良し悪しはいまはおいておく。とにかく、このようなコードがあった)
 で、僕は、過去に作ったCインスタンスを取得する便利関数を作ろうと思って、何気なく以下のような関数を作った。

std::shared_ptr<C> get_c_by_idx(int idx)
{
    auto all_c = C::get_all_c();
    if (idx < all_c.size()) return std::shared_ptr<C>(all_c.at(idx));
    return nullptr;
}

 これを利用したシーンが以下である。

int main()
{
    std::shared_ptr<C> shared_c = get_c();

    // ... 上記コードとはすごく離れたどこか遠くで
    std::shared_ptr<C> first_c = get_c_by_idx(0);

    return 0;
}

 もうおわかりだろう。get_c_by_idx()関数内で、既に一度shared_ptrに渡した生のポインタを、再びshared_ptrに渡している。
 実際、上記のmain()関数は以下のような出力をする。インスタンスは1つしか作っていないのに、デストラクタが2度走る。

C.constructor
C.destructor
C.destructor

 この間違いに気がつくのに結構時間がかかった。だから、今後、同じような間違いをしないために、この記事を書いた。
 こうした間違いをするたびに、自分はまだまだC++のことを知らないと痛感し、C++の奥深さに畏敬の念を感じる。

さいごに

 もう一度教訓を繰り返す。「同じ生ポインタを二度shared_ptrに渡してはいけない」(生ポ二度渡し厳禁の令)。
 別の視点からいえば、デバッグしていてshared_ptrの参照カウンタが想定より小さな値を指している、おかしい、と思ったら、生ポ二度渡し厳禁の令に違反していないかどうかをチェックしてみると良いかもしれない。

【C++】ファイルから丸ごと読んだ文字列でコンパイルエラー

動作確認バージョン

 現象発生、エラー内容などはVisual Studio 2015。

現象

 C++ ファイルを全て読み込む - 備忘録

 このサイトを参考に、ファイルの内容をすべて読みこんでstd::stringに格納しようとした。

 すると、以下のようなコードでコンパイルエラーが起きた

int main()
{
    std::string filePath = "abc.txt";
    std::ifstream ifs(filePath);
    std::string s1(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
    std::string s2 = s1;    // コンパイルエラー
    

    return 0;
}

 エラー内容は以下の通り。

error C2440: '初期化中': 'std::string (__cdecl *)(std::istreambuf_iterator<char,std::char_traits<char>>,std::istreambuf_iterator<char,std::char_traits<char>> (__cdecl *)(void))' から 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>' に変換できません。
note: コンストラクターはソース型を持てません、またはコンストラクターのオーバーロードの解決があいまいです。

解決策

 原因はよくわからなかったが、ひとまずの回避策として、ファイルから読み込んで文字列化する部分を別関数にしたらコンパイルが通った。

std::string readAll(std::string filePath)
{
    std::ifstream ifs(filePath);
    return std::string(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
}

int main()
{
    std::string filePath = "abc.txt";
    std::string s1 = readAll(filePath);
    std::string s2 = s1;    // OK

    return 0;
}

 原因の追求は今後の課題として、ひとまず。

ruby学習日記19:p地獄

 irbpメソッドを使ってみて気が付いた。pメソッドは出力した文字列を返す。

irb(main):055:0> p 'a'
"a"
=> "a"

 なので、pが返した結果をさらにpに渡して...ということが可能である。

irb(main):056:0> p p 'a'
"a"
"a"
=> "a"
irb(main):057:0> p p p 'a'
"a"
"a"
"a"
=> "a"

 このように、同じ文字列が繰り返し出力される。

 ここで考えられるちょっとしたイタズラは、このpを何個積み重ねたら、rubyVMが悲鳴をあげるか、ということである。

 このようなコードをmake_p.rbに保存する。

# make_p.rb
1000.times do print 'p ' end
p 'a'

 保存したら、シェルからこのファイルを実行し、標準出力へ出力される結果をp1000.rbというファイルに保存する。

$ ruby make_p.rb > p1000.rb

 p1000.rbファイルは、ひたすらp p p ...pが並んで、末尾にp "a"がきている一行のファイルとなっているはず。

$ cat p1000.rb 
p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p "a"

 これを実行する。

$ ruby p1000.rb

 以下のように"a"がひたすら出力され、やがて実行が完了するならば、VMはこのp地獄に耐えている。

$ ruby p1000.rb
"a"
"a"
"a"
"a"
"a"
"a"
"a"
"a"
...
"a"
"a"
"a"
$

 make_p.rb1000.timesのところを10000.timesにしたら、VMが悲鳴をあげた。

# make_p.rb
10000.times do print 'p ' end
p 'a'
$ ruby make_p.rb > p10000.rb
$ ruby p10000.rb 
p10000.rb:1: memory exhausted
...p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p ...
...