OSDN Git Service

sdasadSD
[btop/system-metrics.git] / cxx / sm_mapify.hxx
1 // Copyright (c) 2020 Tomasz Konojacki
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
6 //  * Redistributions of source code must retain the above copyright notice,
7 //    this list of conditions and the following disclaimer.
8 //
9 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
10 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
11 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
13 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
14 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
15 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
16 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
17 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
18 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
19 // POSSIBILITY OF SUCH DAMAGE.
20 //
21 // SPDX-License-Identifier: BSD-1-Clause
22
23 #pragma once
24
25 #include <string_view>
26 #include <type_traits>
27
28 // Sadly, the preprocessor doesn't support loops, so we have to emulate them
29 // with those *disgusting* hacks.
30
31 #define EVAL5(...) __VA_ARGS__
32 #define EVAL4(...) EVAL5 (EVAL5 (EVAL5 (__VA_ARGS__)))
33 #define EVAL3(...) EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))
34 #define EVAL2(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__)))
35 #define EVAL(...)  EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__)))
36
37 #define _SM_COMMA ,
38
39 #define _SM_EMPTY()
40 #define _SM_DISCARD(...)
41
42 #define _SM_SECOND2(a,b,...) b
43 #define _SM_SECOND(...) _SM_SECOND2(__VA_ARGS__)
44
45 // ~ doesn't have any special meaning, it's just a placeholder so _SM_DISCARD
46 // is returned as a second value.
47 #define _SM_DISCARD_IF_END2() ~, _SM_DISCARD
48
49 // If arg is set to (), _SM_DISCARD_IF_END2() will be called, otherwise the
50 // macro will return literal "_SM_DISCARD_IF_END2 arg". Note that there's no
51 // comma, so it's a single value.
52 #define _SM_DISCARD_IF_END(arg) _SM_DISCARD_IF_END2 arg
53
54 #define _SM_DEFER_IF_NOT_END(f, arg, ...) _SM_SECOND(_SM_DISCARD_IF_END(arg), f)  _SM_EMPTY()
55
56 #define _SM_RECURSE2(f, arg, ...) _SM_COMMA f(arg) _SM_DEFER_IF_NOT_END(_SM_RECURSE, __VA_ARGS__)(f, __VA_ARGS__)
57 #define _SM_RECURSE(f, ...)  _SM_DEFER_IF_NOT_END(_SM_RECURSE2, __VA_ARGS__)(f, __VA_ARGS__)
58
59 #define _SM_FOREACH2(f, arg, ...) EVAL(_SM_DEFER_IF_NOT_END(f, arg)(arg) _SM_RECURSE(f, __VA_ARGS__))
60
61 // _SM_FOREACH(FUNC, arg1, arg2, ...) will call FUNC(argN) for each argument,
62 // the outputs will be separated with commas (_SC_COMMA).
63 // () is a magic value that signals the end of the list, see _SM_DISCARD_IF_END().
64 #define _SM_FOREACH(...)  _SM_FOREACH2(__VA_ARGS__, ())
65
66 #define _SM_ADD_FIELD(field_name) \
67     { #field_name , &std::remove_pointer<decltype(this)>::type::field_name }
68
69 // SM_MAPIFY_STRUCT(type, field1, ...) is a macro that adds std::map-like at()
70 // and contains() methods to a struct for specified fields. All mapified fields
71 // have to be of the same type and they must not be bit fields.
72 //
73 // Example usage:
74 //
75 //   struct foobar {
76 //     bool flag1;
77 //     bool flag2;
78 //     bool flag3;
79 //     SM_MAPIFY_STRUCT(
80 //       bool, flag1, flag2, flag3
81 //     )
82 //   };
83 //   struct foobar bar;
84 //   bar.at("flag1") = true; // bar.flag1 is set to 'true'
85 //   bar.contains("flag1"); // true
86 //   bar.contains("foo"); // false
87 //   bar.set_all(false); // now all fields are set to 'false'
88
89 #define SM_MAPIFY_STRUCT(ftype, ...)                                          \
90     private:                                                                  \
91         const auto &_sm_fields_map() {                                        \
92             using ptype = ftype std::remove_pointer<decltype(this)>::type::*; \
93             /* All fmap keys are string literals. String literals are */      \
94             /* guaranteed to be never freed, which means that using   */      \
95             /* std::string_view as a key is perfectly safe.           */      \
96             static const std::unordered_map<std::string_view, ptype> fmap = { \
97                 _SM_FOREACH(_SM_ADD_FIELD, __VA_ARGS__)                       \
98             };                                                                \
99             return fmap;                                                      \
100         }                                                                     \
101     public:                                                                   \
102         ftype &at(const std::string_view &field) {                            \
103             return this->*_sm_fields_map().at(field);                         \
104         }                                                                     \
105         bool contains(const std::string_view &field) {                        \
106             return static_cast<bool>(this->_sm_fields_map().count(field));    \
107         }                                                                     \
108         void set_all(ftype val) {                                             \
109             for (const auto &[k, v]: this->_sm_fields_map()) {                \
110                 this->*v = val;                                               \
111             }                                                                 \
112         }