OSDN Git Service

Merge pull request #716 from sikabane-works/release/3.0.0Alpha16
[hengbandforosx/hengbandosx.git] / src / util / flag-group.h
1 #pragma once
2
3 #include <bitset>
4
5 /**
6  * @brief フラグ集合を扱う、FlagGroupクラス
7  *
8  * @tparam FlagType 扱うフラグ集合を定義したenum class型
9  */
10 template <typename FlagType>
11 class FlagGroup {
12 private:
13     /** フラグ集合のフラグ数 */
14     static constexpr auto FLAG_TYPE_MAX = static_cast<size_t>(FlagType::MAX);
15
16 public:
17     /**
18      * @brief フラグ集合に含まれるフラグの種類数を返す
19      *
20      * @return constexpr size_t フラグ集合に含まれるフラグの種類数
21      */
22     constexpr size_t size() const noexcept
23     {
24         return FLAG_TYPE_MAX;
25     };
26
27     /**
28      * @brief FlagGroupクラスのデフォルトコンストラクタ
29      *
30      * すべてのフラグがOFFの状態のFlagGroupクラスのインスタンスを生成する
31      */
32     FlagGroup<FlagType>() = default;
33
34     /**
35      * @brief FlagGroupクラスのコンストラクタ
36      *
37      * initializer_listで指定したフラグがON、それ以外はOFFの状態の
38      * FlagGroupクラスのインスタンスを生成する
39      *
40      * @param il ONの状態で生成するフラグを指定した initializer_list
41      */
42     FlagGroup<FlagType>(std::initializer_list<FlagType> il)
43     {
44         set(il);
45     }
46
47     /**
48      * @brief FlagGroupクラスのコンストラクタ
49      *
50      * 入力イテレータで指定した範囲のリストに含まれるフラグがON、
51      * それ以外のフラグがOFFの状態のFlagGroupクラスのインスタンスを生成する
52      *
53      * @tparam InputIter 入力イテレータの型
54      * @param first 範囲の開始位置を示す入力イテレータ
55      * @param last 範囲の終了位置を示す入力イテレータ
56      */
57     template <typename InputIter>
58     FlagGroup<FlagType>(InputIter first, InputIter last)
59     {
60         set(first, last);
61     }
62
63     /**
64      * @brief フラグ集合に含まれるフラグをすべてOFFにする
65      *
66      * @return FlagGroup<FlagType>& *thisを返す
67      */
68     FlagGroup<FlagType> &clear() noexcept
69     {
70         bs_.reset();
71         return *this;
72     }
73
74     /**
75      * @brief 指定したフラグを指定した値にセットする
76      *
77      * @param flag 値をセットするフラグを指定する
78      * @param val セットする値。trueならフラグをON、falseならフラグをOFFにする。
79      *            引数を省略した場合はフラグをONにする。
80      * @return FlagGroup<FlagType>& *thisを返す
81      */
82     FlagGroup<FlagType> &set(FlagType flag, bool val = true)
83     {
84         bs_.set(static_cast<size_t>(flag), val);
85         return *this;
86     }
87
88     /**
89      * @brief 入力イテレータで指定した範囲のリストに含まれるフラグをONにする
90      *
91      * @tparam InputIter 入力イテレータの型
92      * @param first 範囲の開始位置を示す入力イテレータ
93      * @param last 範囲の終了位置を示す入力イテレータ
94      * @return FlagGroup<FlagType>& *thisを返す
95      */
96     template <typename InputIter>
97     FlagGroup<FlagType> &set(InputIter first, InputIter last)
98     {
99         static_assert(std::is_same<typename std::iterator_traits<InputIter>::value_type, FlagType>::value, "Iterator value type is invalid");
100
101         for (; first != last; ++first) {
102             set(*first);
103         }
104
105         return *this;
106     }
107
108     /**
109      * @brief 指定したFlagGroupのインスンタンスのONになっているフラグをONにする
110      *
111      * @param rhs ONにするフラグがONになっているFlagGroupのインスタンス
112      * @return FlagGroup<FlagType>& *thisを返す
113      */
114     FlagGroup<FlagType> &set(const FlagGroup<FlagType> &rhs)
115     {
116         bs_ |= rhs.bs_;
117         return *this;
118     }
119
120     /**
121      * @brief 指定したinitializer_listに含まれるフラグをONにする
122      *
123      * @param list ONにするフラグを列挙したinitializer_list
124      * @return FlagGroup<FlagType>& *thisを返す
125      */
126     FlagGroup<FlagType> &set(std::initializer_list<FlagType> list)
127     {
128         return set(std::begin(list), std::end(list));
129     }
130
131     /**
132      * @brief 指定したフラグをOFFにする
133      *
134      * @param flag OFFにするフラグを指定する
135      * @return FlagGroup<FlagType>& *thisを返す
136      */
137     FlagGroup<FlagType> &reset(FlagType flag)
138     {
139         bs_.reset(static_cast<size_t>(flag));
140         return *this;
141     }
142
143     /**
144      * @brief 入力イテレータで指定した範囲のリストに含まれるフラグをOFFにする
145      *
146      * @tparam InputIter 入力イテレータの型
147      * @param first 範囲の開始位置を示す入力イテレータ
148      * @param last 範囲の終了位置を示す入力イテレータ
149      * @return FlagGroup<FlagType>& *thisを返す
150      */
151     template <typename InputIter>
152     FlagGroup<FlagType> &reset(InputIter first, InputIter last)
153     {
154         static_assert(std::is_same<typename std::iterator_traits<InputIter>::value_type, FlagType>::value, "Iterator value type is invalid");
155
156         for (; first != last; ++first) {
157             reset(*first);
158         }
159
160         return *this;
161     }
162
163     /**
164      * @brief 指定したFlagGroupのインスンタンスのONになっているフラグをOFFにする
165      *
166      * @param rhs OFFにするフラグがONになっているFlagGroupのインスタンス
167      * @return FlagGroup<FlagType>& *thisを返す
168      */
169     FlagGroup<FlagType> &reset(const FlagGroup<FlagType> &rhs)
170     {
171         bs_ &= ~rhs.bs_;
172         return *this;
173     }
174
175     /**
176      * @brief 指定したinitializer_listに含まれるフラグをOFFにする
177      *
178      * @param list OFFにするフラグを列挙したinitializer_list
179      * @return FlagGroup<FlagType>& *thisを返す
180      */
181     FlagGroup<FlagType> &reset(std::initializer_list<FlagType> list)
182     {
183         return reset(std::begin(list), std::end(list));
184     }
185
186     /**
187      * @brief 指定したフラグがONかOFFか調べる
188      *
189      * @param f 調べるフラグを指定する
190      * @return bool 指定したフラグがONならtrue、OFFならfalse
191      */
192     bool has(FlagType f) const
193     {
194         return bs_.test(static_cast<size_t>(f));
195     }
196
197     /**
198      * @brief 指定したフラグがOFFかONか調べる
199      *
200      * @param f 調べるフラグを指定する
201      * @return bool 指定したフラグがOFFならtrue、ONならfalse
202      */
203     bool has_not(FlagType f) const
204     {
205         return !has(f);
206     }
207
208     /**
209      * @brief フラグ集合のいずれかのフラグがONかどうかを調べる
210      *
211      * @return bool フラグ集合のいずれかのフラグがONならtrue
212      *              フラグ集合のすべてのフラグがOFFならfalse
213      */
214     bool any() const noexcept
215     {
216         return bs_.any();
217     }
218
219     /**
220      * @brief フラグ集合のすべてのフラグがOFFかどうかを調べる
221      *
222      * @return bool フラグ集合のすべてのフラグがOFFならtrue
223      *              フラグ集合のいずれかのフラグがONならfalse
224      */
225     bool none() const noexcept
226     {
227         return bs_.none();
228     }
229
230     /**
231      * @brief 入力イテレータで指定した範囲のリストに含まれるフラグがすべてONかどうかを調べる
232      *
233      * @tparam InputIter 入力イテレータの型
234      * @param first 範囲の開始位置を示す入力イテレータ
235      * @param last 範囲の終了位置を示す入力イテレータ
236      * @return bool すべてのフラグがONであればtrue、そうでなければfalse
237      */
238     template <typename InputIter>
239     bool has_all_of(InputIter first, InputIter last) const
240     {
241         static_assert(std::is_same<typename std::iterator_traits<InputIter>::value_type, FlagType>::value, "Iterator value type is invalid");
242
243         for (; first != last; ++first) {
244             if (has_not(*first)) {
245                 return false;
246             }
247         }
248
249         return true;
250     }
251
252     /**
253      * @brief 指定したinitializer_listに含まれるフラグがすべてONかどうかを調べる
254      *
255      * @param list 調べるフラグを列挙したinitializer_list
256      * @return bool すべてのフラグがONであればtrue、そうでなければfalse
257      */
258     bool has_all_of(std::initializer_list<FlagType> list) const
259     {
260         return has_all_of(std::begin(list), std::end(list));
261     }
262
263     /**
264      * @brief 引数で指定したFlagGroupのインスンタンスのONになっているフラグがすべてONかどうかを調べる
265      *
266      * @param rhs FlagGroupのインスタンス
267      * @return bool すべてのフラグがONであればtrue、そうでなければfalse
268      */
269     bool has_all_of(const FlagGroup<FlagType> &rhs) const
270     {
271         return (bs_ & rhs.bs_) == rhs.bs_;
272     }
273
274     /**
275      * @brief 入力イテレータで指定した範囲のリストに含まれるフラグのいずれかがONかどうかを調べる
276      *
277      * @tparam InputIter 入力イテレータの型
278      * @param first 範囲の開始位置を示す入力イテレータ
279      * @param last 範囲の終了位置を示す入力イテレータ
280      * @return bool いずれかのフラグがONであればtrue、そうでなければfalse
281      */
282     template <typename InputIter>
283     bool has_any_of(InputIter first, InputIter last) const
284     {
285         static_assert(std::is_same<typename std::iterator_traits<InputIter>::value_type, FlagType>::value, "Iterator value type is invalid");
286
287         for (; first != last; ++first) {
288             if (has(*first)) {
289                 return true;
290             }
291         }
292
293         return false;
294     }
295
296     /**
297      * @brief 指定したinitializer_listに含まれるフラグのいずれかがONかどうかを調べる
298      *
299      * @param list 調べるフラグを列挙したinitializer_list
300      * @return bool いずれかのフラグがONであればtrue、そうでなければfalse
301      */
302     bool has_any_of(std::initializer_list<FlagType> list) const
303     {
304         return has_any_of(std::begin(list), std::end(list));
305     }
306
307     /**
308      * @brief 引数で指定したFlagGroupのインスンタンスのONになっているフラグのいずれかがONかどうかを調べる
309      *
310      * @param rhs FlagGroupのインスタンス
311      * @return bool いずれかのフラグがONであればtrue、そうでなければfalse
312      */
313     bool has_any_of(const FlagGroup<FlagType> &rhs) const
314     {
315         return (bs_ & rhs.bs_) != 0;
316     }
317
318     /**
319      * @brief 入力イテレータで指定した範囲のリストに含まれるフラグがすべてOFFかどうかを調べる
320      *
321      * @tparam InputIter 入力イテレータの型
322      * @param first 範囲の開始位置を示す入力イテレータ
323      * @param last 範囲の終了位置を示す入力イテレータ
324      * @return bool すべてのフラグがOFFであればtrue、そうでなければfalse
325      */
326     template <typename InputIter>
327     bool has_none_of(InputIter first, InputIter last) const
328     {
329         static_assert(std::is_same<typename std::iterator_traits<InputIter>::value_type, FlagType>::value, "Iterator value type is invalid");
330
331         return !has_any_of(first, last);
332     }
333
334     /**
335      * @brief 指定したinitializer_listに含まれるフラグがすべてOFFかどうかを調べる
336      *
337      * @param list 調べるフラグを列挙したinitializer_list
338      * @return bool すべてのフラグがOFFであればtrue、そうでなければfalse
339      */
340     bool has_none_of(std::initializer_list<FlagType> list) const
341     {
342         return !has_any_of(list);
343     }
344
345     /**
346      * @brief 引数で指定したFlagGroupのインスンタンスのONになっているフラグがすべてOFFかどうかを調べる
347      *
348      * @param rhs FlagGroupのインスタンス
349      * @return bool すべてのフラグがOFFであればtrue、そうでなければfalse
350      */
351     bool has_none_of(const FlagGroup<FlagType> &rhs) const
352     {
353         return !has_any_of(rhs);
354     }
355
356     /**
357      * @brief フラグ集合のONになっているフラグの数を返す
358      *
359      * @return size_t ONになっているフラグの数
360      */
361     size_t count() const noexcept
362     {
363         return bs_.count();
364     }
365
366     /**
367      * @brief フラグ集合の状態を0と1で表した文字列を返す
368      *
369      * フラグ集合の上位番号から順に、フラグがONなら1、OFFなら0で表した文字列を返す。
370      * 例: 5つのフラグ集合で、0:ON、1:OFF、2:OFF、3:ON、4:OFFの場合、"01001"
371      *
372      * @return std::string フラグ集合の状態を表した文字列
373      */
374     std::string str() const
375     {
376         return bs_.to_string();
377     }
378
379     /**
380      * @brief セーブファイルからフラグ集合を読み出す
381      *
382      * @param fg 読み出したフラグ集合を格納するFlagGroupインスタンスの参照
383      * @param rd_byte_func セーブファイルから1バイトデータを読み出す関数(rd_byte)へのポインタ
384      */
385     template <typename Func>
386     friend void rd_FlagGroup(FlagGroup<FlagType> &fg, Func rd_byte_func)
387     {
388         uint8_t tmp_l, tmp_h;
389         rd_byte_func(&tmp_l);
390         rd_byte_func(&tmp_h);
391         const auto fg_size = static_cast<uint16_t>((tmp_h << 8) | tmp_l);
392
393         for (int i = 0; i < fg_size; i++) {
394             uint8_t flag_byte;
395             rd_byte_func(&flag_byte);
396             std::bitset<8> flag_bits(flag_byte);
397             for (int j = 0; j < 8; j++) {
398                 const size_t pos = i * 8 + j;
399                 if (pos < fg.bs_.size()) {
400                     fg.bs_[pos] = flag_bits[j];
401                 }
402             }
403         }
404     }
405
406     /**
407      * @brief セーブファイルにフラグ集合を書き込む
408      *
409      * @param fg 書き込むフラグ集合を保持したFlagGroupインスタンスの参照
410      * @param wr_byte_func セーブファイルに1バイトデータを書き込む関数(wr_byte)へのポインタ
411      */
412     template <typename Func>
413     friend void wr_FlagGroup(const FlagGroup<FlagType> &fg, Func wr_byte_func)
414     {
415         const auto fg_size = static_cast<uint16_t>((fg.bs_.size() + 7) / 8);
416         wr_byte_func(fg_size & 0xff);
417         wr_byte_func((fg_size >> 8) & 0xff);
418
419         for (int i = 0; i < fg_size; i++) {
420             std::bitset<8> flag_bits;
421             for (int j = 0; j < 8; j++) {
422                 const size_t pos = i * 8 + j;
423                 if (pos < fg.bs_.size()) {
424                     flag_bits[j] = fg.bs_[pos];
425                 }
426             }
427             wr_byte_func(flag_bits.to_ulong() & 0xff);
428         }
429     }
430
431     /**
432      * @brief 文字列からフラグへのマップを指定した検索キーで検索し、
433      *        見つかった場合はフラグ集合に該当するフラグをセットする
434      *
435      * @tparam Map std::map<string_view, FlagType> もしくは std::unordered_map<string_view, FlagType>
436      * @param fg フラグをセットするフラグ集合
437      * @param dict 文字列からフラグへのマップ
438      * @param what マップの検索キー
439      * @return bool 検索キーでフラグが見つかり、フラグをセットした場合 true
440      *              見つからなかった場合 false
441      */
442     template <typename Map>
443     static bool grab_one_flag(FlagGroup<FlagType> &fg, const Map &dict, std::string_view what)
444     {
445         auto it = dict.find(what);
446         if (it == dict.end())
447             return false;
448
449         fg.set(it->second);
450         return true;
451     }
452
453 private:
454     /**
455      * @brief 指定したフラグ位置へのアクセスを提供するプロキシクラス
456      */
457     class reference {
458     public:
459         reference(std::bitset<FlagGroup::FLAG_TYPE_MAX> &flags, size_t pos)
460             : bs_(flags)
461             , pos_(pos)
462         {
463         }
464
465         reference &operator=(const reference &rhs)
466         {
467             bs_[pos_] = static_cast<bool>(rhs);
468             return *this;
469         }
470
471         reference &operator=(bool val)
472         {
473             bs_[pos_] = val;
474             return *this;
475         }
476
477         operator bool() const
478         {
479             return bs_[pos_];
480         }
481
482     private:
483         std::bitset<FlagGroup::FLAG_TYPE_MAX> &bs_;
484         size_t pos_;
485     };
486
487 public:
488     /**
489      * @brief 指定したフラグへのアクセスを提供するプロキシオブジェクトを取得する
490      *
491      * @param f プロキシオブジェクトを取得するフラグ
492      * @return reference 指定したフラグへのアクセスを提供するプロキシオブジェクト
493      */
494     reference operator[](FlagType f)
495     {
496         return reference(bs_, static_cast<size_t>(f));
497     }
498
499 private:
500     /** フラグ集合を保持するstd::bitsetのインスタンス */
501     std::bitset<FLAG_TYPE_MAX> bs_;
502 };