1 #pragma once
2 
3 #include <bit>
4 #include <cstdint>
5 #include <type_traits>
6 
7 namespace lg2
8 {
9 namespace details
10 {
11 
12 /** Type to hold a set of logging flags. */
13 template <typename... Fs>
14 struct log_flag
15 {
16     /** Combined bit-set value of the held flags. */
17     static constexpr auto value = (0 | ... | Fs::value);
18 };
19 
20 /** Constant for the "zero" flag. */
21 static constexpr auto log_flag_seq_start =
22     std::integral_constant<uint64_t, 0>{};
23 
24 /** Concept to determine if a type is one of the defined flag types. */
25 template <typename T>
26 concept log_flags = requires { T::i_am_a_lg2_flag_type; };
27 
28 /** Operator to combine log_flag sets together. */
29 template <log_flags... As, log_flags... Bs>
30 constexpr auto operator|(const log_flag<As...>, const log_flag<Bs...>)
31 {
32     return details::log_flag<As..., Bs...>{};
33 }
34 
35 /** Static check to determine if a prohibited flag is found in a flag set. */
36 template <log_flags... Fs, log_flags F>
37 constexpr void prohibit(log_flag<Fs...>, log_flag<F>)
38 {
39     static_assert(!(... || std::is_same_v<Fs, F>),
40                   "Prohibited flag found for value type.");
41 }
42 
43 /** Static check to determine if any conflicting flags are found in a flag set.
44  */
45 template <log_flags... As, log_flags... Bs>
46 constexpr void one_from_set(log_flag<As...> a, log_flag<Bs...> b)
47 {
48     static_assert(std::popcount(a.value & b.value) < 2,
49                   "Conflicting flags found for value type.");
50 }
51 
52 } // namespace details
53 
54 // Macro used to define all of the logging flags as a sequence of bitfields.
55 //  - Creates a struct-type where the `value` is 1 bit higher than the previous
56 //    so that it can be combined together with other flags using `log_flag`.
57 //  - Creates a static instance of the flag in the `lg2` namespace.
58 #define PHOSPHOR_LOG2_DECLARE_FLAG(flagname, prev)                             \
59     namespace details                                                          \
60     {                                                                          \
61     struct flag_##flagname                                                     \
62     {                                                                          \
63         static constexpr uint64_t value =                                      \
64             prev.value == log_flag_seq_start.value ? 1 : (prev.value << 1);    \
65                                                                                \
66         static constexpr bool i_am_a_lg2_flag_type = true;                     \
67     };                                                                         \
68     }                                                                          \
69     static constexpr auto flagname =                                           \
70         details::log_flag<details::flag_##flagname>()
71 
72 // Set of supported logging flags.
73 //      Please keep these sorted!
74 PHOSPHOR_LOG2_DECLARE_FLAG(bin, log_flag_seq_start);
75 PHOSPHOR_LOG2_DECLARE_FLAG(dec, bin);
76 PHOSPHOR_LOG2_DECLARE_FLAG(field8, dec);
77 PHOSPHOR_LOG2_DECLARE_FLAG(field16, field8);
78 PHOSPHOR_LOG2_DECLARE_FLAG(field32, field16);
79 PHOSPHOR_LOG2_DECLARE_FLAG(field64, field32);
80 PHOSPHOR_LOG2_DECLARE_FLAG(floating, field64);
81 PHOSPHOR_LOG2_DECLARE_FLAG(hex, floating);
82 PHOSPHOR_LOG2_DECLARE_FLAG(signed_val, hex);
83 PHOSPHOR_LOG2_DECLARE_FLAG(str, signed_val);
84 PHOSPHOR_LOG2_DECLARE_FLAG(unsigned_val, str);
85 
86 #undef PHOSPHOR_LOG2_DECLARE_FLAG
87 
88 /** Handy scope-level `using` to get the format flags. */
89 #define PHOSPHOR_LOG2_USING_FLAGS                                              \
90     using lg2::bin;                                                            \
91     using lg2::dec;                                                            \
92     using lg2::field8;                                                         \
93     using lg2::field16;                                                        \
94     using lg2::field32;                                                        \
95     using lg2::field64;                                                        \
96     using lg2::floating;                                                       \
97     using lg2::hex;                                                            \
98     using lg2::signed_val;                                                     \
99     using lg2::str;                                                            \
100     using lg2::unsigned_val
101 
102 } // namespace lg2
103