OSDN Git Service

memberwise_swap を追加
[gintenlib/gintenlib.git] / gintenlib / bool_comparable.hpp
1 #ifndef GINTENLIB_INCLUDED_BOOL_COMPARABLE_HPP_
2 #define GINTENLIB_INCLUDED_BOOL_COMPARABLE_HPP_
3
4 /*
5   bool_comparable : safe_boolイディオム提供
6   
7   宣言:
8     template<typename Derived>
9     class bool_comparable
10     {
11      public:
12       // safe_bool 本体
13       operator unspecified_bool_type() const;
14       // 派生したクラスの側で operator! が定義されていない場合、
15       // 派生したクラスの boolean_test() メンバ関数があればその否定を返す。
16       // operator!() も boolean_test() もなければ、エラー
17       bool operator!() const;
18     
19       // bool との比較。 int のときは反応しないようになってます
20       friend bool operator==( const bool_comparable& lhs, bool rhs );
21       friend bool operator==( bool lhs, const bool_comparable& rhs );
22       friend bool operator!=( const bool_comparable& lhs, bool rhs );
23       friend bool operator!=( bool lhs, const bool_comparable& rhs );
24     
25      protected:
26       bool_comparable() {}
27       ~bool_comparable() {}
28     
29     };  // class bool_comparable<Derived>
30     
31         
32     // 他、呼び出されるとコンパイルエラーになる operator== と operator!= が宣言されています
33
34   機能:
35     boost::operators みたいな感じに、簡単に safe_bool イディオムを実現します。
36     使用法は、bool_comparable のテンプレート引数に製作するクラスの名前を入れて public 継承。
37     そして製作するクラスに、
38       bool operator!() const;
39     関数か、あるいは
40       bool boolean_test() const;
41     関数のどちらかを定義すればOKです(両方ある場合は operator!() が優先されます)。
42   
43   使用法 : 
44     // 例として boost::optional 的な何かを作りましょう
45     #include <boost/scoped_ptr.hpp>
46     
47     template<typename T>
48     class my_optional
49       : public gintenlib::bool_comparable< my_optional<T> > // このように使う
50     {
51       // 本体部分はなんら気にせず書いてよい
52      public:
53       typedef  T  value_type;
54       
55       typedef       T&       reference;
56       typedef const T& const_reference;
57       
58       my_optional() : p() {}
59       my_optional( const T& x ) : p( new T(x) ) {}
60       
61       reference        operator*()       { return *p; }
62       const_reference  operator*() const { return *p; }
63       
64       // operator!() を確実に実装することを忘れなければ。
65       bool operator!() const
66       {
67         return !p;
68       }
69       // これにより、bool比較も出来るようになる
70       
71       // ちなみに operator!() じゃなくて
72       // bool boolean_test() const { return p.get(); }
73       // これでもいい(その場合、operator! は自動定義)
74       
75       // そのほか、コピーコンストラクタとか代入演算子とかは面倒だから省略
76       
77      private:
78       // 面倒なポインタ管理とかしたくないので scoped_ptr を使う
79       boost::scoped_ptr<T> p;
80       
81     };
82     
83     int main()
84     {
85       my_optional<int> a, b(1), c(0);
86       
87       // if の条件部として
88       if( a )
89       {
90         assert( !"a is empty." );
91       }
92       
93       // && や || と組み合わせて
94       assert( b && c );
95       
96       // ゼロとの比較
97       assert( a == 0 && b != 0 && c != 0 );
98       // assert( b == 1 );  // 1 とは比較できない
99       
100       // bool 値との比較
101       assert( a == false && b == true && c == true );
102       // static_cast
103       assert( static_cast<bool>(b) == static_cast<bool>(c) );
104       // assert( a != b ); // コレはダメ(もし b と c を比較したら?)
105
106   注意事項:
107     ・boost::enable_if を使っています。古いコンパイラでは動きません。
108     ・それが嫌なら GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF マクロを定義すればOKです。
109     ・ただし、その場合、上の例での「b == 1」的な表現がコンパイル通ってしまいます。
110     
111     ・「 a == b 」のような、派生先のクラス同士での比較は、デフォルトではコンパイルエラーになります。
112     ・ですが、当然、派生先で operator== を定義しておけば、きちんと比較できるようになります。
113     ・ただし、派生先のクラスが「 0 から暗黙変換可能」であり「operator==が定義されている」場合は、
114       「 x == 0 」のような比較が、ゼロとの比較に限りですが、出来なくなってしまいます:
115         struct convertible_from_int
116           : gintenlib::bool_comparable<convertible_from_int>,
117             private boost::equality_comparable<convertible_from_int>
118         {
119           int value;
120           convertible_from_int( int x = 0 ) : value(x) {} // int から暗黙変換できると・・・
121           
122           bool operator!() const { return value == 0; }
123           
124           // operator== による比較が出来る場合
125           friend bool operator==( const convertible_from_int& lhs, const convertible_from_int& rhs )
126           {
127             return lhs.value == rhs.value;
128           }
129          
130         };  
131         
132         int main()
133         {
134           convertible_from_int x, y = 2;
135           assert( !x ); // こういうのは普通に出来るが
136           assert( y );
137           assert( x == false );
138           assert( y == true );
139           
140           // assert( x == 0 ); // コレが実行できない(曖昧になってしまう)
141           // assert( y != 0 ); // 当然これもダメ
142           
143           assert( y == 2 ); // でもコレはOK
144           assert( x != 1 ); // コレもOK
145         }
146        
147     ・その場合は int との比較を別個定義してやればOKです:
148         struct convertible_from_int
149           : gintenlib::bool_comparable<convertible_from_int>,
150             private boost::equality_comparable<convertible_from_int>,
151             private boost::equality_comparable<convertible_from_int, int>
152         {
153           int value;
154           convertible_from_int( int x = 0 ) : value(x) {} // int から暗黙変換できる場合でも
155           
156           bool boolean_test() const { return value; } // boolean_test を使ってみる。
157           
158           friend bool operator==( const piyo& lhs, const piyo& rhs )
159           {
160             return lhs.value == rhs.value;
161           }
162           // int との比較を明示的に定義さえすれば
163           friend bool operator==( const convertible_from_int& lhs, int rhs )
164           {
165             return lhs.value == rhs;
166           }
167          
168         };
169         
170         int main()
171         {
172           convertible_from_int x, y(2)
173           assert( x == 0 ); // OK
174           assert( y != 0 );
175         }
176     
177 */
178
179 #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
180   #include "enable_if.hpp"
181   #include <boost/type_traits/is_same.hpp>
182 #endif
183
184 #include "cast.hpp" // for gintenlib::cast
185
186 namespace gintenlib
187 {
188   // ヘルパ構造体
189   namespace detail_
190   {
191     struct bool_comparable_helper
192     {
193       // operator bool_type() の戻り値として使われる値
194       void bool_value() const {}
195       
196      private:
197       // コンパイルエラーを起こすための private 関数
198       bool this_type_does_not_support_comparisons_();
199     
200     };
201   }
202   
203  namespace bool_comparable_ // ADL 回避
204  {
205   // 本体
206   template<typename Derived>
207   class bool_comparable
208   {
209     // safe-bool に使う型(メンバ関数へのポインタ)
210     typedef void (bool_comparable::*bool_type)() const;
211     
212     // 派生したクラスを得る関数
213     const Derived& derived() const { return *static_cast<const Derived*>(this); }
214           Derived& derived()       { return *static_cast<      Derived*>(this); }
215     
216     // 他のテンプレートでも derived は使いたいぜ
217     template<typename U> friend class bool_comparable;
218     
219     // ヘルパ構造体名を適当に短縮
220     typedef ::gintenlib::detail_::bool_comparable_helper helper;
221     
222    public:
223     // 本題
224     operator bool_type() const
225     {
226       return bool_test_() ? bool_value_() : 0;
227       // bool_test_, bool_value_ は後で定義する
228     }
229   
230     // bool との比較
231     
232   #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
233     
234     // enable_if で bool と比較する時だけ有効にすることで、x == 1 のような比較を無力化する
235     // todo: g++ 以外のコンパイラでの動作チェック
236     
237     // 実装
238     template<typename U>
239     friend typename enable_if<boost::is_same<U, bool>, bool>::type
240       operator==( const Derived& lhs, U rhs )
241     {
242       return cast<bool>(lhs) == rhs;
243     }
244     template<typename U>
245     friend typename enable_if<boost::is_same<U, bool>, bool>::type
246       operator==( U lhs, const Derived& rhs )
247     {
248       return lhs == cast<bool>(rhs);
249     }
250     template<typename U>
251     friend typename enable_if<boost::is_same<U, bool>, bool>::type
252       operator!=( const Derived& lhs, U rhs )
253     {
254       return cast<bool>(lhs) != rhs;
255     }
256     template<typename U>
257     friend typename enable_if<boost::is_same<U, bool>, bool>::type
258       operator!=( U lhs, const Derived& rhs )
259     {
260       return lhs != cast<bool>(rhs);
261     }
262     
263   #else   // #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
264     
265     // 一応 enable_if を使わない版も用意
266     // この場合、 x == 1 のような表現が通ってしまうが仕方ないね
267     friend bool operator==( const Derived& lhs, bool rhs )
268     {
269       return cast<bool>(lhs) == rhs;
270     }
271     friend bool operator==( bool lhs, const Derived& rhs )
272     {
273       return lhs == cast<bool>(rhs);
274     }
275     friend bool operator!=( const Derived& lhs, bool rhs )
276     {
277       return cast<bool>(lhs) != rhs;
278     }
279     friend bool operator!=( bool lhs, const Derived& rhs )
280     {
281       return lhs != cast<bool>(rhs);
282     }
283     
284   #endif  // #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
285   
286    private:
287     // 実装補助
288     // bool_type として返す値
289     static bool_type bool_value_()
290     {
291       // 関数を無駄に作らせない工夫。どうせ bool_type の値は使わないし
292       return reinterpret_cast<bool_type>( &helper::bool_value );
293     }
294     
295     // テスト関数
296     // まず Derived::operator!() を見に行く
297     bool bool_test_() const
298     {
299       return !( !derived() );
300     }
301     
302    public:
303     // Derived 側で operator!() が定義されて無いなら
304     // Derived::boolean_test() 関数がないか探し、無ければコンパイルエラー
305     bool operator!() const
306     {
307       return !( derived().boolean_test() );
308     }
309     // friend 関数版。存在する意味は特に無い。
310     friend bool boolean_test( const bool_comparable& x )
311     {
312       return x.bool_test_();
313     }
314     
315     // 比較はエラーになる( Derived 側で特別に定義すればOK)
316     
317     // 等値比較
318     friend bool operator==( const bool_comparable& lhs, const bool_comparable& rhs )
319     {
320       return helper().this_type_does_not_support_comparisons_();
321     }
322     // 不等値比較
323     friend bool operator!=( const bool_comparable& lhs, const bool_comparable& rhs )
324     {
325       return helper().this_type_does_not_support_comparisons_();
326     }
327   
328    protected:
329     // 派生クラス以外からの構築禁止
330      bool_comparable() {}
331     ~bool_comparable() {}
332     
333   };  // class bool_comparable<Derived>
334   
335  }  // namespace bool_comparable_
336  
337   // gintenlib 名前空間に引き入れる
338   using namespace bool_comparable_;
339
340 } // namespace gintenlib
341
342 #endif  // #ifndef GINTENLIB_INCLUDED_BOOL_COMPARABLE_HPP_