OSDN Git Service

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