OSDN Git Service

4c797291726288e6ea50fa9380f96036466636f7
[hengbandforosx/hengbandosx.git] / src / util / flag-group.h
1 #pragma once
2
3 #include <bitset>
4 #include <concepts>
5 #include <iterator>
6 #include <optional>
7 #include <type_traits>
8
9 template <typename T>
10 class EnumRange;
11
12 namespace flag_group {
13
14 /**
15  * @brief 型がFlagGroupクラスで使用するフラグを指すイテレータであることを表すコンセプト
16  *
17  * Iter の型が以下の要件を満たすことを表す
18  *
19  * - 入力イテレータである
20  * - そのイテレータが指す要素の型が FlagType である
21  */
22 template <typename Iter, typename FlagType>
23 concept FlagIter = std::input_iterator<Iter> && std::same_as<std::iter_value_t<Iter>, FlagType>;
24
25 }
26
27 namespace flag_group::detail {
28
29 template <typename Func, size_t BITSET_SIZE>
30 void read_bitset(std::bitset<BITSET_SIZE> &bs, Func rd_byte_func, size_t count)
31 {
32     for (auto i = 0U; i < count; i++) {
33         const std::bitset<8> rd_byte_bs(rd_byte_func());
34
35         for (auto j = 0U; j < 8; j++) {
36             const size_t pos = i * 8 + j;
37             if (pos >= bs.size()) {
38                 break;
39             }
40
41             bs[pos] = rd_byte_bs[j];
42         }
43     }
44 }
45
46 template <typename Func, size_t BITSET_SIZE>
47 void write_bitset(const std::bitset<BITSET_SIZE> &bs, Func wr_byte_func, size_t count)
48 {
49     for (auto i = 0U; i < count; i++) {
50         std::bitset<8> wr_byte_bs;
51
52         for (auto j = 0U; j < 8; j++) {
53             const size_t pos = i * 8 + j;
54             if (pos >= bs.size()) {
55                 break;
56             }
57
58             wr_byte_bs[j] = bs[pos];
59         }
60
61         wr_byte_func(wr_byte_bs.to_ulong() & 0xff);
62     }
63 }
64
65 }
66
67 /**
68  * @brief フラグ集合を扱う、FlagGroupクラス
69  *
70  * @tparam FlagType 扱うフラグ集合を定義したenum型 もしくは enum class型
71  * @tparam FlagTypeに列挙される値の最大値+1
72  */
73 template <typename FlagType, FlagType MAX>
74 class FlagGroup {
75 private:
76     /** フラグ集合のフラグ数 */
77     static constexpr auto FLAG_TYPE_MAX = static_cast<size_t>(MAX);
78
79 public:
80     using flag_type = FlagType;
81
82     /**
83      * @brief フラグ集合に含まれるフラグの種類数を返す
84      *
85      * @return フラグ集合に含まれるフラグの種類数
86      */
87     [[nodiscard]] constexpr size_t size() const noexcept
88     {
89         return FLAG_TYPE_MAX;
90     };
91
92     /**
93      * @brief FlagGroupクラスのデフォルトコンストラクタ
94      *
95      * すべてのフラグがOFFの状態のFlagGroupクラスのインスタンスを生成する
96      */
97     FlagGroup() = default;
98
99     /**
100      * @brief FlagGroupクラスのコンストラクタ
101      *
102      * initializer_listで指定したフラグがON、それ以外はOFFの状態の
103      * FlagGroupクラスのインスタンスを生成する
104      *
105      * @param il ONの状態で生成するフラグを指定した initializer_list
106      */
107     FlagGroup(std::initializer_list<FlagType> il)
108         : FlagGroup(il.begin(), il.end())
109     {
110     }
111
112     /**
113      * @brief FlagGroupクラスのコンストラクタ
114      *
115      * EnumRangeクラスで指定した範囲のフラグがON、それ以外はOFFの状態の
116      * FlagGroupクラスのインスタンスを生成する
117      *
118      * @param range 範囲を示すEnumRangeクラスのオブジェクト
119      */
120     FlagGroup(const EnumRange<FlagType> &range)
121         : FlagGroup(range.begin(), range.end())
122     {
123     }
124
125     /**
126      * @brief FlagGroupクラスのコンストラクタ
127      *
128      * 入力イテレータで指定した範囲のリストに含まれるフラグがON、
129      * それ以外のフラグがOFFの状態のFlagGroupクラスのインスタンスを生成する
130      *
131      * @tparam InputIter 入力イテレータの型
132      * @param first 範囲の開始位置を示す入力イテレータ
133      * @param last 範囲の終了位置を示す入力イテレータ
134      */
135     template <flag_group::FlagIter<FlagType> InputIter>
136     FlagGroup(InputIter first, InputIter last)
137     {
138         for (; first != last; ++first) {
139             set(*first);
140         }
141     }
142
143     /**
144      * @brief フラグ集合に含まれるフラグをすべてOFFにする
145      *
146      * @return *thisを返す
147      */
148     FlagGroup<FlagType, MAX> &clear() noexcept
149     {
150         bs_.reset();
151         return *this;
152     }
153
154     /**
155      * @brief 指定したフラグを指定した値にセットする
156      *
157      * @param flag 値をセットするフラグを指定する
158      * @param val セットする値。trueならフラグをON、falseならフラグをOFFにする。
159      *            引数を省略した場合はフラグをONにする。
160      * @return *thisを返す
161      */
162     FlagGroup<FlagType, MAX> &set(FlagType flag, bool val = true)
163     {
164         bs_.set(static_cast<size_t>(flag), val);
165         return *this;
166     }
167
168     /**
169      * @brief 入力イテレータで指定した範囲のリストに含まれるフラグをONにする
170      *
171      * @tparam InputIter 入力イテレータの型
172      * @param first 範囲の開始位置を示す入力イテレータ
173      * @param last 範囲の終了位置を示す入力イテレータ
174      * @return *thisを返す
175      */
176     template <flag_group::FlagIter<FlagType> InputIter>
177     FlagGroup<FlagType, MAX> &set(InputIter first, InputIter last)
178     {
179         return set(FlagGroup(first, last));
180     }
181
182     /**
183      * @brief 指定したFlagGroupのインスンタンスのONになっているフラグをONにする
184      *
185      * @param rhs ONにするフラグがONになっているFlagGroupのインスタンス
186      * @return *thisを返す
187      */
188     FlagGroup<FlagType, MAX> &set(const FlagGroup<FlagType, MAX> &rhs)
189     {
190         bs_ |= rhs.bs_;
191         return *this;
192     }
193
194     /**
195      * @brief 指定したフラグをOFFにする
196      *
197      * @param flag OFFにするフラグを指定する
198      * @return *thisを返す
199      */
200     FlagGroup<FlagType, MAX> &reset(FlagType flag)
201     {
202         bs_.reset(static_cast<size_t>(flag));
203         return *this;
204     }
205
206     /**
207      * @brief 入力イテレータで指定した範囲のリストに含まれるフラグをOFFにする
208      *
209      * @tparam InputIter 入力イテレータの型
210      * @param first 範囲の開始位置を示す入力イテレータ
211      * @param last 範囲の終了位置を示す入力イテレータ
212      * @return *thisを返す
213      */
214     template <flag_group::FlagIter<FlagType> InputIter>
215     FlagGroup<FlagType, MAX> &reset(InputIter first, InputIter last)
216     {
217         return reset(FlagGroup(first, last));
218     }
219
220     /**
221      * @brief 指定したFlagGroupのインスンタンスのONになっているフラグをOFFにする
222      *
223      * @param rhs OFFにするフラグがONになっているFlagGroupのインスタンス
224      * @return *thisを返す
225      */
226     FlagGroup<FlagType, MAX> &reset(const FlagGroup<FlagType, MAX> &rhs)
227     {
228         bs_ &= ~rhs.bs_;
229         return *this;
230     }
231
232     /**
233      * @brief 指定したフラグがONかOFFか調べる
234      *
235      * @param f 調べるフラグを指定する
236      * @return 指定したフラグがONならtrue、OFFならfalse
237      */
238     [[nodiscard]] bool has(FlagType f) const
239     {
240         if (f == MAX) {
241             // どのフラグにも該当しないFlagTypeの型としてMAXを指定する事があるため、MAXが指定された時はfalseを返すようにする
242             return false;
243         }
244         return bs_.test(static_cast<size_t>(f));
245     }
246
247     /**
248      * @brief 指定したフラグがOFFかONか調べる
249      *
250      * @param f 調べるフラグを指定する
251      * @return 指定したフラグがOFFならtrue、ONならfalse
252      */
253     [[nodiscard]] bool has_not(FlagType f) const
254     {
255         return !has(f);
256     }
257
258     /**
259      * @brief フラグ集合のいずれかのフラグがONかどうかを調べる
260      *
261      * @return フラグ集合のいずれかのフラグがONならtrue
262      *         フラグ集合のすべてのフラグがOFFならfalse
263      */
264     [[nodiscard]] bool any() const noexcept
265     {
266         return bs_.any();
267     }
268
269     /**
270      * @brief フラグ集合のすべてのフラグがOFFかどうかを調べる
271      *
272      * @return フラグ集合のすべてのフラグがOFFならtrue
273      *         フラグ集合のいずれかのフラグがONならfalse
274      */
275     [[nodiscard]] bool none() const noexcept
276     {
277         return bs_.none();
278     }
279
280     /**
281      * @brief 入力イテレータで指定した範囲のリストに含まれるフラグがすべてONかどうかを調べる
282      *
283      * @tparam InputIter 入力イテレータの型
284      * @param first 範囲の開始位置を示す入力イテレータ
285      * @param last 範囲の終了位置を示す入力イテレータ
286      * @return すべてのフラグがONであればtrue、そうでなければfalse
287      */
288     template <flag_group::FlagIter<FlagType> InputIter>
289     [[nodiscard]] bool has_all_of(InputIter first, InputIter last) const
290     {
291         return has_all_of(FlagGroup(first, last));
292     }
293
294     /**
295      * @brief 引数で指定したFlagGroupのインスンタンスのONになっているフラグがすべてONかどうかを調べる
296      *
297      * @param rhs FlagGroupのインスタンス
298      * @return すべてのフラグがONであればtrue、そうでなければfalse
299      */
300     [[nodiscard]] bool has_all_of(const FlagGroup<FlagType, MAX> &rhs) const
301     {
302         return (bs_ & rhs.bs_) == rhs.bs_;
303     }
304
305     /**
306      * @brief 入力イテレータで指定した範囲のリストに含まれるフラグのいずれかがONかどうかを調べる
307      *
308      * @tparam InputIter 入力イテレータの型
309      * @param first 範囲の開始位置を示す入力イテレータ
310      * @param last 範囲の終了位置を示す入力イテレータ
311      * @return いずれかのフラグがONであればtrue、そうでなければfalse
312      */
313     template <flag_group::FlagIter<FlagType> InputIter>
314     [[nodiscard]] bool has_any_of(InputIter first, InputIter last) const
315     {
316         return has_any_of(FlagGroup(first, last));
317     }
318
319     /**
320      * @brief 引数で指定したFlagGroupのインスンタンスのONになっているフラグのいずれかがONかどうかを調べる
321      *
322      * @param rhs FlagGroupのインスタンス
323      * @return いずれかのフラグがONであればtrue、そうでなければfalse
324      */
325     [[nodiscard]] bool has_any_of(const FlagGroup<FlagType, MAX> &rhs) const
326     {
327         return (bs_ & rhs.bs_).any();
328     }
329
330     /**
331      * @brief 入力イテレータで指定した範囲のリストに含まれるフラグがすべてOFFかどうかを調べる
332      *
333      * @tparam InputIter 入力イテレータの型
334      * @param first 範囲の開始位置を示す入力イテレータ
335      * @param last 範囲の終了位置を示す入力イテレータ
336      * @return すべてのフラグがOFFであればtrue、そうでなければfalse
337      */
338     template <flag_group::FlagIter<FlagType> InputIter>
339     [[nodiscard]] bool has_none_of(InputIter first, InputIter last) const
340     {
341         return !has_any_of(first, last);
342     }
343
344     /**
345      * @brief 引数で指定したFlagGroupのインスンタンスのONになっているフラグがすべてOFFかどうかを調べる
346      *
347      * @param rhs FlagGroupのインスタンス
348      * @return すべてのフラグがOFFであればtrue、そうでなければfalse
349      */
350     [[nodiscard]] bool has_none_of(const FlagGroup<FlagType, MAX> &rhs) const
351     {
352         return !has_any_of(rhs);
353     }
354
355     /**
356      * @brief フラグ集合のONになっているフラグの数を返す
357      *
358      * @return ONになっているフラグの数
359      */
360     [[nodiscard]] size_t count() const noexcept
361     {
362         return bs_.count();
363     }
364
365     /**
366      * @brief フラグ集合のONになっているフラグのうち最初のフラグを返す
367      *
368      * @return フラグ集合のONになっているフラグのうち最初のフラグ。但し一つもONになっているフラグがなければ std::nullopt
369      */
370     [[nodiscard]] std::optional<FlagType> first() const noexcept
371     {
372         for (size_t i = 0; i < bs_.size(); i++) {
373             if (bs_.test(i)) {
374                 return static_cast<FlagType>(i);
375             }
376         }
377
378         return std::nullopt;
379     }
380
381     /**
382      * @brief フラグ集合の状態を0と1で表した文字列を返す
383      *
384      * フラグ集合の上位番号から順に、フラグがONなら1、OFFなら0で表した文字列を返す。
385      * 例: 5つのフラグ集合で、0:ON、1:OFF、2:OFF、3:ON、4:OFFの場合、"01001"
386      *
387      * @return フラグ集合の状態を表した文字列
388      */
389     [[nodiscard]] std::string str() const
390     {
391         return bs_.to_string();
392     }
393
394     /*!
395      * @brief フラグ集合の状態を表したビット列を unsigned long 型にして返す
396      *
397      * @return フラグ集合の状態を表したビット列
398      */
399     [[nodiscard]] unsigned long to_ulong() const
400     {
401         return bs_.to_ulong();
402     }
403
404     /*!
405      * @brief フラグ集合の状態を表したビット列を unsigned long long 型にして返す
406      *
407      * @return フラグ集合の状態を表したビット列
408      */
409     [[nodiscard]] unsigned long long to_ullong() const
410     {
411         return bs_.to_ullong();
412     }
413
414     /**
415      * @brief フラグ集合 *this と rhs が等値かどうかを調べる
416      *
417      * @param rhs 比較するフラグ集合
418      * @return すべてのフラグの状態が等しければtrue、そうでなければfalse
419      */
420     [[nodiscard]] bool operator==(const FlagGroup<FlagType, MAX> &rhs) const noexcept
421     {
422         return bs_ == rhs.bs_;
423     }
424
425     /**
426      * @brief フラグ集合 *this と rhs が非等値かどうかを調べる
427      *
428      * @param rhs 比較するフラグ集合
429      * @return いずれかのフラグの状態が等しくなければtrue、そうでなければfalse
430      */
431     [[nodiscard]] bool operator!=(const FlagGroup<FlagType, MAX> &rhs) const noexcept
432     {
433         return bs_ != rhs.bs_;
434     }
435
436     /**
437      * @brief フラグ集合 *this と rhs の論理積(AND)の複合演算を行う
438      *
439      * *this に対して、*this と rhs で共通してONのフラグをONのままにし、それ以外のフラグをOFFにする
440      *
441      * @param rhs 複合演算を行うフラグ集合
442      * @return *thisを返す
443      */
444     FlagGroup<FlagType, MAX> &operator&=(const FlagGroup<FlagType, MAX> &rhs) noexcept
445     {
446         bs_ &= rhs.bs_;
447         return *this;
448     }
449
450     /**
451      * @brief フラグ集合 *this と rhs の論理和(OR)の複合演算を行う
452      *
453      * *this に対して、*this と rhs でどちらか一方でもONのフラグをONにし、それ以外のフラグをOFFにする
454      *
455      * @param rhs 複合演算を行うフラグ集合
456      * @return *thisを返す
457      */
458     FlagGroup<FlagType, MAX> &operator|=(const FlagGroup<FlagType, MAX> &rhs) noexcept
459     {
460         bs_ |= rhs.bs_;
461         return *this;
462     }
463
464     /**
465      * @brief フラグ集合のONになっているフラグを出力イテレータに書き込む
466      *
467      * @tparam OutputIter フラグを書き込む出力イテレータの型
468      * @param flag_group 対象のフラグ集合
469      * @param start フラグを書き込む出力イテレータ
470      */
471     template <typename OutputIter>
472     static void get_flags(const FlagGroup<FlagType, MAX> &flag_group, OutputIter start)
473     {
474         for (size_t i = 0; i < flag_group.size(); i++) {
475             if (flag_group.bs_.test(i)) {
476                 *start++ = static_cast<FlagType>(i);
477             }
478         }
479     }
480
481     /**
482      * @brief セーブファイルからフラグ集合を読み出す
483      *
484      * @param fg 読み出したフラグ集合を格納するFlagGroupインスタンスの参照
485      * @param rd_byte_func セーブファイルから1バイトデータを読み出す関数(rd_byte)へのポインタ
486      */
487     template <typename Func>
488     friend void rd_FlagGroup(FlagGroup<FlagType, MAX> &fg, Func rd_byte_func)
489     {
490         auto tmp_l = rd_byte_func();
491         auto tmp_h = rd_byte_func();
492         const auto fg_size = static_cast<uint16_t>((tmp_h << 8) | tmp_l);
493
494         flag_group::detail::read_bitset(fg.bs_, rd_byte_func, fg_size);
495     }
496
497     /**
498      * @brief セーブファイルにフラグ集合を書き込む
499      *
500      * @param fg 書き込むフラグ集合を保持したFlagGroupインスタンスの参照
501      * @param wr_byte_func セーブファイルに1バイトデータを書き込む関数(wr_byte)へのポインタ
502      */
503     template <typename Func>
504     friend void wr_FlagGroup(const FlagGroup<FlagType, MAX> &fg, Func wr_byte_func)
505     {
506         const auto fg_size = static_cast<uint16_t>((fg.bs_.size() + 7) / 8);
507         wr_byte_func(fg_size & 0xff);
508         wr_byte_func((fg_size >> 8) & 0xff);
509
510         flag_group::detail::write_bitset(fg.bs_, wr_byte_func, fg_size);
511     }
512
513     /**
514      * @brief セーブファイルから固定バイト長でフラグ集合を読み出す
515      *
516      * @param fg 読み出したフラグ集合を格納するFlagGroupインスタンスの参照
517      * @param rd_byte_func セーブファイルから1バイトデータを読み出す関数(rd_byte)へのポインタ
518      * @param count 読み出すバイト数
519      */
520     template <typename Func>
521     friend void rd_FlagGroup_bytes(FlagGroup<FlagType, MAX> &fg, Func rd_byte_func, size_t count)
522     {
523         flag_group::detail::read_bitset(fg.bs_, rd_byte_func, count);
524     }
525
526     /**
527      * @brief セーブファイルに固定バイト長でフラグ集合を書き込む
528      *
529      * @param fg 書き込むフラグ集合を保持したFlagGroupインスタンスの参照
530      * @param wr_byte_func セーブファイルに1バイトデータを書き込む関数(wr_byte)へのポインタ
531      * @param count 書き込むバイト数
532      */
533     template <typename Func>
534     friend void wr_FlagGroup_bytes(const FlagGroup<FlagType, MAX> &fg, Func wr_byte_func, size_t count)
535     {
536         flag_group::detail::write_bitset(fg.bs_, wr_byte_func, count);
537     }
538
539     /**
540      * @brief 文字列からフラグへのマップを指定した検索キーで検索し、
541      *        見つかった場合はフラグ集合に該当するフラグをセットする
542      *
543      * @tparam Map std::map<string_view, FlagType> もしくは std::unordered_map<string_view, FlagType>
544      * @param fg フラグをセットするフラグ集合
545      * @param dict 文字列からフラグへのマップ
546      * @param what マップの検索キー
547      * @return 検索キーでフラグが見つかり、フラグをセットした場合 true
548      *         見つからなかった場合 false
549      */
550     template <typename Map>
551     static bool grab_one_flag(FlagGroup<FlagType, MAX> &fg, const Map &dict, std::string_view what)
552     {
553         auto it = dict.find(what);
554         if (it == dict.end()) {
555             return false;
556         }
557
558         fg.set(it->second);
559         return true;
560     }
561
562 private:
563     /**
564      * @brief 指定したフラグ位置へのアクセスを提供するプロキシクラス
565      */
566     class reference {
567     public:
568         reference(std::bitset<FLAG_TYPE_MAX> &flags, size_t pos)
569             : bs_(flags)
570             , pos_(pos)
571         {
572         }
573
574         reference &operator=(const reference &rhs)
575         {
576             bs_[pos_] = static_cast<bool>(rhs);
577             return *this;
578         }
579
580         reference &operator=(bool val)
581         {
582             bs_[pos_] = val;
583             return *this;
584         }
585
586         [[nodiscard]] operator bool() const
587         {
588             return bs_[pos_];
589         }
590
591     private:
592         std::bitset<FLAG_TYPE_MAX> &bs_;
593         size_t pos_;
594     };
595
596 public:
597     /**
598      * @brief 指定したフラグへのアクセスを提供するプロキシオブジェクトを取得する
599      *
600      * @param f プロキシオブジェクトを取得するフラグ
601      * @return 指定したフラグへのアクセスを提供するプロキシオブジェクト
602      */
603     [[nodiscard]] reference operator[](FlagType f)
604     {
605         return reference(bs_, static_cast<size_t>(f));
606     }
607
608 private:
609     /** フラグ集合を保持するstd::bitsetのインスタンス */
610     std::bitset<FLAG_TYPE_MAX> bs_;
611 };
612
613 /**
614  * @brief enum clsas 型に対してFlagGroupクラスを使用するエイリアステンプレート
615  *
616  * FlagGroupクラスのテンプレート引数に、使用する型として FlagType、列挙値の最大値として FlagType::MAX を渡す。
617  *
618  * @tparam FlagType FlagGroupクラスを使用する enum class 型
619  */
620 template <typename FlagType>
621 using EnumClassFlagGroup = FlagGroup<FlagType, FlagType::MAX>;
622
623 /**
624  * @brief フラグ集合 lhs と rhs に対して論理積(AND)を取ったフラグ集合を生成する
625  *
626  * lhs と rhs で共通してONのフラグがON、それ以外のフラグがOFFのフラグ集合を生成する
627  *
628  * @tparam FlagType 扱うフラグ集合を定義したenum class型
629  * @param lhs フラグ集合1
630  * @param rhs フラグ集合2
631  * @return lhs と rhs の論理積を取ったフラグ集合
632  */
633 template <typename FlagType, FlagType MAX>
634 [[nodiscard]] FlagGroup<FlagType, MAX> operator&(const FlagGroup<FlagType, MAX> &lhs, const FlagGroup<FlagType, MAX> &rhs) noexcept
635 {
636     return FlagGroup<FlagType, MAX>(lhs) &= rhs;
637 }
638
639 /**
640  * @brief フラグ集合 lhs と rhs に対して論理和(OR)を取ったフラグ集合を生成する
641  *
642  * lhs と rhs でどちらか一方でもONのフラグがON、それ以外のフラグがOFFのフラグ集合を生成する
643  *
644  * @tparam FlagType 扱うフラグ集合を定義したenum class型
645  * @param lhs フラグ集合1
646  * @param rhs フラグ集合2
647  * @return lhs と rhs の論理積を取ったフラグ集合
648  */
649 template <typename FlagType, FlagType MAX>
650 [[nodiscard]] FlagGroup<FlagType, MAX> operator|(const FlagGroup<FlagType, MAX> &lhs, const FlagGroup<FlagType, MAX> &rhs) noexcept
651 {
652     return FlagGroup<FlagType, MAX>(lhs) |= rhs;
653 }