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
nullptr
と nullopt
が似ていてややこしい。。。
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 するときの注意
元ネタ
Rubyむずい…うむむむむむむむうむ
— 異常者 (@h1manoa) 2017年6月8日
[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ https://t.co/buooxLKDHW
と、いうことでちょっとやってみたら見事にハマったので覚書
元のコード
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
これで無事に when
に Method
を渡して評価する事が出来ました。
関係ないですが 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++ では『マクロを使うべきではないコード』もたくさんありますが『マクロでしか実装出来ないコード』もまた存在します。
マクロは用法・用量を守って正しくお使いください。