C++17 の std::optional に無効値を設定する

C++17 では Boost.Optional が新しく標準ライブラリ入りしました。 Boost.Optional で無効値を指定する場合は boost::none を使っていたんですが、標準ライブラリの std::optional では std::nullopt を使用して無効値を指定します。

[ソースコード]

#include <optional>
#include <iostream>

template<typename T>
void
print(std::optional<T> const& op){
    if( op ){
        std::cout << "OK:" << * op << std::endl;
    }
    else{
        std::cout << "NG" << std::endl;
    }
}

int
main(){
    std::optional<int> value;
    print(value);

    value = 42;
    print(value);

    value = std::nullopt;
    print(value);

    return 0;;
}

[出力]

NG
OK:42
NG

nullptrnullopt が似ていてややこしい。。。

void でキャストすることで "unused" の警告を回避する

元々は C言語?で使われているようなハックらしいのですが、知らなかったので覚書。

さて、例えば次のように関数の引数を assert チェックしてる関数があるとします。

void
func(int value){
    assert(value == 42);
}

この場合、デバッグビルドでは問題ないんですがリリースビルドでは『assert で参照してる value が使用されていない』とコンパイラが判断して、

warning: unused parameter 'value' [-Wunused-parameter]

という風な警告を出す可能性があります。
このような場合、次のように void でキャストして回避することが可能です。

void
func(int value){
    (void)value;
    assert(value == 42);
}

たまに (void)xxx; しているコードを見かける事があるんですが、こういう理由だったんですね…。 ちなみに C++17 では [[maybe_unused]] を使用して回避することが出来ます。

void
// コンパイラに対して "使用されない可能性がある変数" として明示化する
func([[maybe_unused]] int value){
    assert(value == 42);
}

[参照]

Ruby で任意の異なるクラスのオブジェクトを #uniq するときの注意

元ネタ

と、いうことでちょっとやってみたら見事にハマったので覚書

元のコード

class Hoge
    def initialize(num)
        @num = num
    end
    def ==(other)
        if @num == other
            true
        else
            false
        end
    end
end

p [Hoge.new(1), Hoge.new(2), 1, 2, 5 ,6].uniq
# => [#<Hoge:0x000000024871f8 @num=1>, #<Hoge:0x000000024871d0 @num=2>, 1, 2, 5, 6]

元のコードはユーザ定義クラスと Integer が入り混じった配列をいい感じに #uniq したいという内容でした。

#uniq の条件

#uniq は要素の Object#eql? を使用して重複を判定します。 また、 #eql? を定義した場合は Object#hash も再定義する必要があります。 と、いうことでこれに沿って修正してみました。

class Hoge
    def initialize(num)
        @num = num
    end

    def hash
        @num.hash
    end

    def eql?(other)
        if @num == other
            true
        else
            false
        end
    end
end

p [Hoge.new(1), Hoge.new(2), 1, 2, 5 ,6].uniq
# => [#<Hoge:0x000000024871f8 @num=1>, #<Hoge:0x000000024871d0 @num=2>, 1, 2, 5, 6]

しかし、これでもまだ意図した動作になりません。

#hash#eql? は双方向で true になる必要がある

調べていたらわかったんですが

Hoge.new(1).eql? 1 # => true

だけではなくて

1.eql? Hoge.new(1).eql? # => true

のように双方向で true になる必要があるみたいです(当然 #hash に関しても同様

完成

と、言うことで Integer#eql? を拡張することで無事に意図する動作になりました。

class Hoge
    def initialize(num)
        @num = num
    end

    def hash
        @num.hash
    end

    def eql?(other)
        if @num == other
            true
        else
            false
        end
    end
end

class Integer
    def eql?(other)
        return super(other) unless Hoge === other
        other.eql? self
    end
end

_1 = Hoge.new(1)

# この条件をすべて満たす必要がある
p _1.hash == 1.hash
p _1.eql? 1
p 1.hash == _1.hash
p 1.eql? _1

p [Hoge.new(1), Hoge.new(2), 1, 2, 5 ,6].uniq
# => [#<Hoge:0x000000024871f8 @num=1>, #<Hoge:0x000000024871d0 @num=2>, 5, 6]

流石に #uniq のためだけに Integer をクラス拡張するのはツラいですね…。

まとめ

  • #uniq#eql? を参照して重複のチェックを行う
  • #eql? を書き換えた場合は #hash も書き換える必要がある
  • #uniq は双方向でチェックするので要素すべてのオブジェクトに対して #eql? を考慮する必要がある

RSpec で pending しても before :context が呼ばれてしまう

結果だけ先に書いてしまいますが『pending しているスコープで before :context を定義すると before のブロックが呼ばれます』。
どういうことかというと次のように before :context 内で例外が発生した場合、テストが失敗してしまいます。

describe X do
    # 以下のブロックを pending にする
    xcontext 'hoge' do
        # pending しているのにもかかわらず it 時に before のブロックが呼ばれてしまう
        before :context do
            raise "error"
        end
        it { expect(0).to eq 0 }
    end
end

これは pending してるのにもかかわらず before:context を指定した場合、it 時にブロック内の処理が呼ばれてしまうためです。
また、この問題(というべきかはわかりませんが)は :context を渡した時のみ発生し、引数を渡さなかったり :example を渡した場合には再現しません。
ちなみに :all:contextエイリアスなので :all を渡しても同様の問題が発生します。

どういう時に困るのか

例えば、次のように before 内で実装する予定のメソッドを呼び出していると例外が発生するのでテストが失敗してしまいます。

describe X do
    # X.hoge を後で実装する
    xcontext '.hoge' do
        before :context do
            # この段階では .hoge は未実装
            @result = X.hoge
        end
        it { expect(@result).to eq "hoge" }
        it { expect(@result).to be_kind_of String }
    end
end

こういう場合は before :context を使わなかったり、before 以外(subject など)を使用して回避する必要があります。

[関連]

https://github.com/rspec/rspec-core/issues/107

[追記]

書き忘れてましたが RSpec のバージョンは 3.6.0 です。

Ruby の Method#=== が定義されていないのでつらい問題

Method#=== が定義されていなくてつらい問題です。

Proc#===

Proc オブジェクトでは #call で評価することが出来ますが、そのエイリアスとして #=== が存在します。

upcase = proc { |a| a.upcase }
upcase.call "homu"
# => "HOMU"
upcase === "mami"
# => "MAMI"

これは次のように when で使用出来るようにするためです。

def sign n
    case n
    when proc { |n| 0 < n }
        1
    when proc { |n| 0 > n }
        -1
    else
        0
    end
end

sign -4  # => -1
sign 0   # => 0
sign 7   # => 1

ここで、Ruby に精通してる人なら気づくと思いますが、#method を使って proc { |n| 0 < n }0.method(:<) と書き直したくなります。

def sign n
    case n
    # #method はレシーバのメソッドをブロックオブジェクトとして返す
    when 0.method(:<)
        1
    when 0.method(:>)
        -1
    else
        0
    end
end

しかし、これは上手く動作しません。
なぜなら #method が返す Method クラスでは #=== が定義されていないからです。

Method#=== を定義する gem をつくった

と、いうことで Method#=== を使うことが出来る gem をつくりました。

$ gem install procedureable_method

で、インストールすることが出来ます。

使い方

使い方は

require "procedureable_method"
using ProcedureableMethod

という風に refinements として使用するか

require "procedureable_method/core_ext"

として直接モンキーパッチを適用されるかします。
これで Method#=== を使用する事が出来ます。

require "procedureable_method"

using ProcedureableMethod

def sign n
    case n
    when 0.method(:<)
        1
    when 0.method(:>)
        -1
    else
        0
    end
end

sign -4  # => -1
sign 0   # => 0
sign 7   # => 1

これで無事に whenMethod を渡して評価する事が出来ました。
関係ないですが when の場合はちゃんと refinements で定義したメソッドが呼ばれるんですね。
本体にも Method#=== が追加されてほしい。

Ruby で mixin したモジュールを削除する gem をつくった

Rubyメタプログラミングをやっているとどうしても『mixin したモジュールを削除したい』という欲求にかられるのでつくりました。

インストール

$ gem install unmixer

使い方

require "unmixer"

# Unmixer は refinements を使用してるので using して使用します
using Unmixer

module M1; end
module M2; end
module M3; end

class X
    include M1
    prepend M2
end

p X.ancestors
# => [M2, X, M1, Object, Kernel, BasicObject]

# include したモジュールを削除
X.instance_eval { uninclude M1 }
p X.ancestors
# => [M2, X, Object, Kernel, BasicObject]

# include したモジュール以外は削除できない
X.instance_eval { uninclude M2 }
p X.ancestors
# => [M2, X, Object, Kernel, BasicObject]


# prepend したモジュールを削除
X.instance_eval { unprepend M2 }
p X.ancestors
# => [X, Object, Kernel, BasicObject]


X.extend M3
p X.singleton_class.ancestors
# => [#<Class:X>, M3, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]

# extend したモジュールを削除
X.unextend M3
p X.singleton_class.ancestors
# => [#<Class:X>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]


# #extend にブロックを渡した場合、ブロック内でのみ mixin される
X.extend M1 do
    # mixin only in block.
    p X.singleton_class.ancestors
    # => [#<Class:X>, M1, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
    p X.singleton_class.ancestors.include? M1
    # => true
end
p X.singleton_class.ancestors
# => [#<Class:X>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
p X.singleton_class.ancestors.include? M1
# => false

元々は uninclude という gem があったのですが、こちらは比較的新しい Ruby では動作しなかったのでそれを焼き直した形になります。

現状は、Ruby 2.1 〜2.4 であれば動作すると思います。
ただ、実装が結構無理なりなので今後のサポートや意図しない動作が発生する可能性はあります。
今回初めて Ruby で C拡張のコードを書いたんですが、やはり C言語つらいですね…。
最初は C 側で全部を実装していたんですが、途中でつらくなったので C 側は最小限の処理のみ実装して結局 Ruby 側でガシガシ実装しました。
多少不安なところはあるんですが、やはり mixin を削除できる機能はかなり便利です。
言語仕様側で導入されないかなあ…。

【初心者C++er Advent Calendar 2016 21日目】C++ でプリプロセスマクロを使う場面

初心者C++er Advent Calendar 2016 21日目の記事です。
さて、巷ではマクロを使用したコードを公開すると『static const を使え』や『typedef しろ』、『関数テンプレートでおk』などと斧が飛んでくる突っ込まれることが多いです。
これは C++ では『基本的にはマクロを使うべきではなく』さらに『マクロを使わないでよいコード』に直せることが多いからです。
では逆に『マクロを使うべきコード』というのはどういうものなのでしょうか。
この記事では『マクロを使うべきコード』の実例をいくつか紹介してみたいと思います。

[インクルードガード]

ヘッダーファイルを記述する際に『重複インクルードを防止するため』にインクルードガードという技法が使用されれます。
これは #ifndef を利用して『一度だけヘッダーファイルが読み込まれる』ような仕組みです。

// hoge.h
#ifndef HOGE_H
#define HOGE_H

// ここに実装を記述する

#endif    // HOGE_H

これにより複数回 #include "hoge.h" しても一度だけ定義が読み込まれます。
ただ、最近は同等の機能である #pragma once を実装してるコンパイラもあるので、そちらを使用している人もいます。

[キーワードなどを置き換える]

さて、次のように constexpr を使ったコードがあるとします。

constexpr auto
plus(int a, int b){
    return a + b;
}

constexpr auto
minus(int a, int b){
    return a - b;
}

static_assert(plus(1, 2) == 3, "");
static_assert(minus(4, 5) == -1, "");

これは C++11 に対応してるコンパイラであれば問題なく動作します。
しかし、環境によってはどうしても C++03 に対応しなければならないこともあります。
こういう場合、constexpr というキーワードをマクロに置き換えることで、複数の環境に柔軟に対応する事が出来ます。

// C++11 以上ならば
#if __cplusplus >= 201103L
    // constexpr をマクロに置き換える
   #define CONSTEXPR constexpr
#else
    // C++11 以上に対応してないなら空のマクロを定義
   #define CONSTEXPR
#endif

// constexpr をマクロに置き換える
CONSTEXPR int
plus(int a, int b){
    return a + b;
}

CONSTEXPR int
minus(int a, int b){
    return a - b;
}

#if __cplusplus >= 201103L
static_assert(plus(1, 2) == 3, "");
static_assert(minus(4, 5) == -1, "");
#endif

このように『マクロ以外』で置き換えることが出来ないようなワークアラウンドに対応する場合に利用できます。

[コンパイルするときに挙動を変更したい場合]

例えば、次のように『デバッグコードを仕込んだコード』があるとします。

int
plus(int a, int b){
// DEBUG が定義されている時のみログを出力したい
#ifndef DEBUG
    std::cout << a << std::endl;
    std::cout << b << std::endl;
#endif
    return a + b;
}

このように『マクロを使用すること』でコンパイル時に『挙動を変更する』事が出来ます

# DEBUG マクロを定義してコンパイルする
$ g++ main.cpp -DDEBUG
$ ./a
1
2
3

gcc だとこんな感じでコンパイル時にマクロを定義する事が出来ます。

[まとめ]

と、言う感じでパッと思いついたものを上げてみました。
他にも使うべき場面はありますが、初心者向けならこのあたりでしょうか。
C++ では『マクロを使うべきではないコード』もたくさんありますが『マクロでしか実装出来ないコード』もまた存在します。
マクロは用法・用量を守って正しくお使いください。