ひがきの日記

2016-02-07

C++ で Ruby の inject みたいなやつを作ってみる

仕事で C++ を使うことになったので、リハビリを兼て Ruby の inject みたいな (lisp の apply みたいな) 関数を書いてみた。

#include <iostream>
#include <vector>
#include <functional>

using namespace std;

#define ARRSIZ(a) (sizeof(a)/sizeof((a)[0]))
#define ARREND(a) ((a)+ARRSIZ(a))

template <class T, class R, class F>
R inject(T begin, T end, R result, F func) {
    for (; begin != end; ++begin)
	result = func(result, *begin);
    return result;
}

template <class T>
struct PLUS {
    T operator()(const T& a, const T& b) const {
        return a + b;
    }
};

int main() {
    int src[] = {1, 2, 3, 4, 5};
    vector<int> v;
    vector<double> w;

    for (int* i = src; i != ARREND(src); ++i) {
	v.push_back(*i);
	w.push_back(*i * 1.1);
    }

    cout << inject(src, ARREND(src),   0,   plus<int>())    << endl;
    cout << inject(v.begin(), v.end(), 0,   plus<int>())    << endl;
    cout << inject(w.begin(), w.end(), 0.0, plus<double>()) << endl;
    cout << inject(v.begin(), v.end(), 0,   PLUS<int>())    << endl;
    cout << inject(w.begin(), w.end(), 0.0, PLUS<double>()) << endl;

    return 0;
}

inject の受け取る、型 F がなんかよく分からん。

template 使うと、関数オブジェクト関数内でちょいちょいっと書けないのかね。

    struct _plus_ {
	int operator()(const int& a, const int& b) const {
	    return a + b;
	}
    };
    cout << inject(v.begin(), v.end(), 0, _plus_()) << endl;

上のように main() の中で struct を書くと、

warning: template argument uses local type '_plus_'

こんな感じで怒られる。めんどくさい。

今時ならラムダ式を使うといいんだろうか?

そもそも inject に相当する関数はどこかに定義されてるんじゃないのか?

2009-07-04

STL の iterator って静的型のダックタイピングなの?

iterator ってポインタのように使えるだけかと思ったら、ポインタiterator のように使うこともできるのか。

STL の sort とか find って、何であんな まどろっこしい引数なのかと思ったら、そういうことですか。

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>

#define ARRSIZ(arr) (sizeof(arr)/sizeof(arr[0]))


template <class T>
void dump(T first, T last)
{
    for (char* sep = ""; first != last; ++first, sep = " ")
	std::cout << sep << *first;
    std::cout << std::endl;
}


using namespace std;

int main()
{
    int a[] = { 6, 7, 2, 4, 9, 8, 3, };
    vector<int> v;
    set<int> s;
    for (size_t i = 0; i < ARRSIZ(a); ++i) {
        v.push_back(a[i]);
        s.insert(a[i]);
    }

    dump(a, a + ARRSIZ(a));
    dump(v.begin(), v.end());
    dump(s.begin(), s.end());

    cout << "sort" << endl;
    sort(a, a + ARRSIZ(a));
    sort(v.begin(), v.end());
//  sort(s.begin(), s.end());   // sort する必要ないし

    dump(a, a + ARRSIZ(a));
    dump(v.begin(), v.end());
    dump(s.begin(), s.end());

    return 0;
}

C 言語の配列 (a) でも、STLvector (v) でも、set (s) でも、iterator を使って同じようにアクセスできる。

だから algorithm ヘッダが vector (など) とは別に存在するんだ。なるほどな。

2009-06-18

オブジェクトの順序

オレオレクラスのオブジェクトSTL のコンテナに詰めたい。

vector になら詰められるけど、set に詰めようと思ったら、オブジェクトの順序を決めないといけない。

operator< さえあればオブジェクトの順序は決まるみたい。

class Foo
{
        ...
public:
	bool operator<(const Foo& rhs) const { return id < rhs.id; }
    ...

これで set に詰めても怒られない。

#include <set>
    ...
    set<Foo> foo;
    foo.insert(Foo(1));
    foo.insert(Foo(2));
    ...

operator< は、カプセル化を崩さないなら、メンバ関数にしない方がいいらしい。

class Foo
{
        ...
public:
	int id() const;  // 判断材料が公開されているなら ...
    ...
};

bool operator<(const Foo& lhs, const Foo& rhs)
{
	return lhs.id() < rhs.id();
}

こうすると < の右辺でも左辺でも暗黙の型変換をしてくれる。*1

<=, >=, > は < から自動生成するらしい。

順序とは関係ないけど、!= は == から自動生成するらしい。

#include <utility>
    ...
using namespace std::rel_ops;
    ...
    Foo f1(1);
    Foo f2(2);
    if (f1 >= f2)
        ...

*1:コンストラクタが explicit でなければ

2009-06-13

オレオレクラスを ostream に出力する

C++ プログラミング入門 によると、print() というメンバ関数を作って、operator<< から呼び出すらしい。

class Foo
{
    ...
public:
    void print(ostream* out) const { *out << value; }
    ...
};

ostream& operator<<(ostream& out, const Foo& foo);

なんで print なのか?
なんで ostream& じゃなくて ostream* なのか?

operator<< の実体は、print() を呼び出すだけの簡単なお仕事です。

ostream& operator<<(ostream& out, const Foo& foo)
{
    foo.print(&out);
    return out;
}

これで cout << foo << endl; とかできるようになる。


クラスの雛形

class Foo
{
    // constructor, destructor and ...
public:
    Foo(int val = 0): value(val) {}
    ~Foo() {}
    Foo(const Foo& src);
    Foo& operator=(const Foo& src);
    // accessor
    int getValue() const { return value; }
    void setValue(int val) { value = val; }

    // member function

    // data member
private:
    int value;
};

入門書を見ると必ず

  1. メンバ関数
  2. データメンバ

の順に宣言されるけど、逆じゃダメなのかな?

2009-02-09

VisualStudio のデバッグビルドではまった

あるライブラリを使おうとヘッダファイルを include したら、意味不明なエラーが出た。

(×) 1 error C2059: 構文エラー : '文字列'
(×) 2 error C2091: 関数関数を返せません。
(×) 3 error C2802: 静的なメンバ 'operator new' に仮引数リストがありません。

1 行追加しただけ (include しただけ) なのに何故?

問題のエラー箇所を見ると、

    void* operator new(size_t size);

関数返してないし!
静的なメンバじゃないし!

1 時間くらい悩んでやっと分かった。

#include "stdafx.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#include <foo.h>  // 問題のヘッダファイル

new が DEBUG_NEW に変わってるやん _| ̄|○

#include "stdafx.h"

#include <foo.h>  // ここへ移動させれば OK

#ifdef _DEBUG
#define new DEBUG_NEW
#endif