Ruby でゴーストメソッド

Ruby でゴーストメソッドを書いてみた。

class X
    def initialize value
        @value = value
    end

    def method_missing name, *args
        @value.send name, *args
    end
end

n = X.new 42
p n + 3
p n - 4

ary = X.new [1, 2, 3]
p ary.include? 2

ゴーストメソッドとは『存在しないメソッドを存在するかのように振る舞うメソッド』のことをさす。
Ruby では method_missing を使用して定義する事ができる。
Rubyメタプログラミングでは割とポピュラーな技法になる。

RSpec の have_attributes マッチャ

RSpechave_attributes マッチャは『オブジェクトの属性(プロパティ)を検証する』マッチャになります。 例えば、

it { expect(10.to_s).to eq("10") }
it { expect(10.positive?).to eq(true) }

というようなテストが

# メソッド名と戻り値の Hash を渡す
it { expect(10).to have_attributes(to_s: "10", positive?: true) }

という風に書くことが出来ます。

[subject と組み合わせる]

subjectis_expected を使用して、subject 対象のプロパティのテストを行ないたい場合、

subject { 10 }
it { expect(subject.to_s).to eq("10") }
# こういう風に書くことは出来ない
# it { is_expected.to_s.to eq("10") }

のように is_expected を使用することは出来ません。
このような場合に have_attributes マッチャを利用することが出来ます。

it { is_expected.to have_attributes(to_s: "10") }

べんり。

[参照]

https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/have-attributes-matcher

C++ の range-based for 内で iterator を進めたい

通常 range-based for を使用する場合は iterator ではなくて実体を参照するのでユーザから iterator の操作を行うことは出来ません。 ので、range-based for 内でも間接的に iterator を操作できるようなラッパーを書いてみました。

[コード]

#include <vector>
#include <string>
#include <iostream>

template<typename T>
struct iteratorable{
    using iterator_type = typename T::iterator;

    struct iterator_wrapper{
        iterator_wrapper(iterator_type itr) : itr(itr) {}
        iterator_type& operator * (){
            return itr;
        }
        iterator_type& operator ++ (){
            return ++itr;
        }

        bool operator != (const iterator_wrapper& o){
            return itr < o.itr;
        }
        iterator_type itr;
    };

    iterator_wrapper
    begin(){
        return { list.begin() };
    }

    iterator_wrapper
    end(){
        return { list.end() };
    }

    T& list;
};

template<typename T>
iteratorable<T>
as_iterator(T& t){
    return { t };
}

int
main(){
    std::vector<std::string> table = { "homu", "mami", "mado", "saya", "an" };

    for(auto&& it : as_iterator(table)){
        std::cout << "name :" << *it << std::endl;
        it++;
    }
    return 0;
}

[出力]

name :homu
name :mado
name :an

まあそもそも range-based for は『iterator を操作しないで安全なループを行う』って役割もあると思うのでこういうアプローチをしたいなら直接 for 文 + iterator でループしたほうがよい気はする。

C++ の range-based for でループカウンタを使用する

Twitter でみかけたので書いてみた。
一番簡単なのは boost::adaptors:indexed 使うのが楽っぽい。

[コード]

#include <vector>
#include <iostream>
#include <string>
#include <boost/range/adaptor/indexed.hpp>

int
main(){
    std::vector<std::string> table = { "homu", "mami", "mado"};

    for(auto&& it : table | boost::adaptors::indexed()){
        std::cout << it.index() << ":" << it.value() << std::endl;
    }
    return 0;
}
/* output:
0:homu
1:mami
2:mado
*/

標準ライブラリに Range ライブラリはよ

clang-check で -std=c++14 を使う

C++14 に対して clang-check を使おうと思ったんですが

$ clang-check -std=c++14 main.cpp --
clang-check: Unknown command line argument '-std=c++14'.  Try: 'clang-check -help'
clang-check: Did you mean '-h=c++14'?

みたいに単純に -std=c++14 をつけたらエラーになりました。

[-extra-arg を使用する]

clang のコンパイラオプションを使用したい場合は -extra-arg を返せばいいみたいです。

$ clang-check -std=c++14 main.cpp --

Ruby の &. と #try の違い

Ruby 2.3 で Safe Navigation Operator(&.)という新しい演算子が追加されました。 これは obj&.to_i という風にメソッド呼び出しのように使い、レシーバが nil の場合に nil を返します。

10&.to_s  # => "10"
nil&.to_s # nil

このように nil チェックすることなく安全にメソッドを呼び出すことが出来ます。

#try

RailsActiveSupport にも #try という似たようなメソッドが定義されています。 これはレシーバがメソッドが呼びだせる場合にそのメソッドを呼び出すというような処理になります。 また、レシーバが nil の場合は必ず nil を返します。

10.try(:to_s)   # => "10"
nil.try(:to_s)  # => nil

&.#try の違い

両方とも『レシーバが nil ではない場合にメソッドを呼び出す』という目的なんですが、具体的な挙動は少し違っています。 &.が『nilでない場合にメソッドを呼び出す』のに対して、#try は『メソッドを呼び出すことができるときにメソッドを呼び出す』というような挙動になります。 どういうことかというと

# 10 には #hoge メソッドは定義されていない(呼び出せない)ので nil を返す
10.try(:hoge)   # => nil

# レシーバは nil ではないので #hoge メソッドを呼びだそうとするが
# #hoge メソッドは定義されてないのでエラー
10&.hoge # => Error: undefined method `hoge' for 10:Fixnum (NoMethodError)

というような挙動になります。
これは #tryrespond_to? を使用して『メソッドを呼びだせるかどうか』をチェックしているためです。
このような違いがあるため &.#try の代替として使用する場合は注意して使用する必要があります。

共通の挙動

#try&. もレシーバが nil の場合は nil しか返しません。

nil&.nil?      # => nil
nil.try(:nil?) # => nil