OSDN Git Service

e975be1d6229290752f9317b6dec12ad555bfa7d
[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   struct bool_comparable_helper
190   {
191     // operator bool_type() の戻り値として使われる値
192     void bool_value() const {}
193     
194    private:
195     // コンパイルエラーを起こすための private 関数
196     bool this_type_does_not_support_comparisons_();
197   
198   };
199   
200   // 本体
201   template<typename Derived>
202   class bool_comparable
203   {
204     // safe-bool に使う型(メンバ関数へのポインタ)
205     typedef void (bool_comparable::*bool_type)() const;
206     
207     // 派生したクラスを得る関数
208     const Derived& derived() const { return *static_cast<const Derived*>(this); }
209           Derived& derived()       { return *static_cast<      Derived*>(this); }
210     
211     // 他のテンプレートでも derived は使いたいぜ
212     template<typename U> friend class bool_comparable;
213     
214    public:
215     // 本題
216     operator bool_type() const
217     {
218       return bool_test_() ? bool_value_() : 0;
219       // bool_test_, bool_value_ は後で定義する
220     }
221   
222     // bool との比較
223     
224   #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
225     
226     // enable_if で bool と比較する時だけ有効にすることで、x == 1 のような比較を無力化する
227     // todo: g++ 以外のコンパイラでの動作チェック
228     
229     // 実装
230     template<typename U>
231     friend typename enable_if<boost::is_same<U, bool>, bool>::type
232       operator==( const Derived& lhs, U rhs )
233     {
234       return cast<bool>(lhs) == rhs;
235     }
236     template<typename U>
237     friend typename enable_if<boost::is_same<U, bool>, bool>::type
238       operator==( U lhs, const Derived& rhs )
239     {
240       return lhs == cast<bool>(rhs);
241     }
242     template<typename U>
243     friend typename enable_if<boost::is_same<U, bool>, bool>::type
244       operator!=( const Derived& lhs, U rhs )
245     {
246       return cast<bool>(lhs) != rhs;
247     }
248     template<typename U>
249     friend typename enable_if<boost::is_same<U, bool>, bool>::type
250       operator!=( U lhs, const Derived& rhs )
251     {
252       return lhs != cast<bool>(rhs);
253     }
254     
255   #else   // #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
256     
257     // 一応 enable_if を使わない版も用意
258     // この場合、 x == 1 のような表現が通ってしまうが仕方ないね
259     friend bool operator==( const Derived& lhs, bool rhs )
260     {
261       return cast<bool>(lhs) == rhs;
262     }
263     friend bool operator==( bool lhs, const Derived& rhs )
264     {
265       return lhs == cast<bool>(rhs);
266     }
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     
276   #endif  // #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
277   
278    private:
279     // 実装補助
280     // bool_type として返す値
281     static bool_type bool_value_()
282     {
283       // 関数を無駄に作らせない工夫。どうせ bool_type の値は使わないし
284       return reinterpret_cast<bool_type>( &bool_comparable_helper::bool_value );
285     }
286     
287     // テスト関数
288     // まず Derived::operator!() を見に行く
289     bool bool_test_() const
290     {
291       return !( !derived() );
292     }
293     
294    public:
295     // Derived 側で operator!() が定義されて無いなら
296     // Derived::boolean_test() 関数がないか探し、無ければコンパイルエラー
297     bool operator!() const
298     {
299       return !( derived().boolean_test() );
300     }
301     // friend 関数版。存在する意味は特に無い。
302     friend bool boolean_test( const bool_comparable& x )
303     {
304       return x.bool_test_();
305     }
306     
307     // 比較はエラーになる( Derived 側で特別に定義すればOK)
308     
309     // 等値比較
310     friend bool operator==( const bool_comparable& lhs, const bool_comparable& rhs )
311     {
312       return bool_comparable_helper().this_type_does_not_support_comparisons_();
313     }
314     // 不等値比較
315     friend bool operator!=( const bool_comparable& lhs, const bool_comparable& rhs )
316     {
317       return bool_comparable_helper().this_type_does_not_support_comparisons_();
318     }
319   
320    protected:
321     // 派生クラス以外からの構築禁止
322      bool_comparable() {}
323     ~bool_comparable() {}
324     
325   };  // class bool_comparable<Derived>
326
327 } // namespace gintenlib
328
329 #endif  // #ifndef GINTENLIB_INCLUDED_BOOL_COMPARABLE_HPP_