Prevent for writing unset union
[archive/platform/core/system/libConfig.git] / src / config / fields-union.hpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Mateusz Malicki (m.malicki2@samsung.com)
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License
17  */
18
19 /**
20  * @file
21  * @author  Mateusz Malicki (m.malicki2@samsung.com)
22  * @brief   Macros for declare configuration fields
23  */
24
25 #ifndef CONFIG_FIELDS_UNION_HPP
26 #define CONFIG_FIELDS_UNION_HPP
27
28 #include "config/fields.hpp"
29 #include "config/exception.hpp"
30
31 #include <utility>
32 #include <boost/any.hpp>
33 #include <cassert>
34
35 /**
36  * Use this macro to declare and register config fields
37  *
38  * Example:
39  *  struct Foo {
40  *      std::string bar;
41  *
42  *      CONFIG_REGISTER
43  *      (
44  *          bar,
45  *      )
46  *  };
47  *
48  *  struct Config
49  *  {
50  *      CONFIG_DECLARE_UNION
51  *      (
52  *          Foo,
53  *          int
54  *      )
55  *  };
56  *
57  *  Example of valid configuration:
58  *   1. {
59  *        "type": "Foo",
60  *        "value": { "bar": "some string" }
61  *      }
62  *   2. {
63  *        "type": "int",
64  *        "value": 1
65  *      }
66  *
67  *
68  *  Usage:
69  *   Config config;
70  *   // ...
71  *   if (config.isSet()) {
72  *      if (config.is<Foo>()) {
73  *          Foo& foo = config.as<Foo>();
74  *          // ...
75  *      }
76  *      if (config.is<int>())) {
77  *          int field = config.as<int>();
78  *          // ...
79  *      }
80  *   } else {
81  *     // Config is not set
82  *     Foo foo({"some string"});
83  *     config.set(foo);
84  *     config.set(std::move(foo));         //< copy sic!
85  *     config.set(Foo({"some string"}));
86  *   }
87  */
88
89 class DisbaleMoveAnyWrapper : public boost::any
90 {
91     public:
92         DisbaleMoveAnyWrapper() {}
93         DisbaleMoveAnyWrapper(const DisbaleMoveAnyWrapper& any)
94             : boost::any(static_cast<const boost::any&>(any)) {};
95         DisbaleMoveAnyWrapper& operator=(DisbaleMoveAnyWrapper&& any) = delete;
96         DisbaleMoveAnyWrapper& operator=(const DisbaleMoveAnyWrapper& any) {
97             static_cast<boost::any&>(*this) = static_cast<const boost::any&>(any);
98             return *this;
99         }
100 };
101
102 #define CONFIG_DECLARE_UNION(...)                                                               \
103 private:                                                                                        \
104     DisbaleMoveAnyWrapper mConfigDeclareField;                                                  \
105                                                                                                 \
106     template<typename Visitor>                                                                  \
107     void visitOption(Visitor& v, const std::string& name) {                                     \
108         GENERATE_CODE(GENERATE_UNION_VISIT__, __VA_ARGS__)                                      \
109         throw config::ConfigException("Union type error. Unsupported type");                    \
110     }                                                                                           \
111     template<typename Visitor>                                                                  \
112     void visitOption(Visitor& v, const std::string& name) const {                               \
113         GENERATE_CODE(GENERATE_UNION_VISIT_CONST__, __VA_ARGS__)                                \
114         throw config::ConfigException("Union type error. Unsupported type");                    \
115     }                                                                                           \
116     std::string getOptionName() const {                                                         \
117         GENERATE_CODE(GENERATE_UNION_NAME__, __VA_ARGS__)                                       \
118         return std::string();                                                                   \
119     }                                                                                           \
120     boost::any& getHolder() {                                                                   \
121         return static_cast<boost::any&>(mConfigDeclareField);                                   \
122     }                                                                                           \
123     const boost::any& getHolder() const {                                                       \
124         return static_cast<const boost::any&>(mConfigDeclareField);                             \
125     }                                                                                           \
126 public:                                                                                         \
127                                                                                                 \
128     template<typename Visitor>                                                                  \
129     void accept(Visitor v) {                                                                    \
130         std::string name;                                                                       \
131         v.visit("type", name);                                                                  \
132         visitOption(v, name);                                                                   \
133     }                                                                                           \
134                                                                                                 \
135     template<typename Visitor>                                                                  \
136     void accept(Visitor v) const {                                                              \
137         const std::string name = getOptionName();                                               \
138         assert(!name.empty() && "Type is not set");                                             \
139         v.visit("type", name);                                                                  \
140         visitOption(v, name);                                                                   \
141     }                                                                                           \
142                                                                                                 \
143     template<typename Type>                                                                     \
144     bool is() const {                                                                           \
145         return boost::any_cast<Type>(&getHolder()) != NULL;                                     \
146     }                                                                                           \
147     template<typename Type>                                                                     \
148     typename std::enable_if<!std::is_const<Type>::value, Type>::type& as() {                    \
149         assert(!getHolder().empty());                                                           \
150         return boost::any_cast<Type&>(getHolder());                                             \
151     }                                                                                           \
152     template<typename Type>                                                                     \
153     const Type& as() const {                                                                    \
154         assert(!getHolder().empty());                                                           \
155         return boost::any_cast<const Type&>(getHolder());                                       \
156     }                                                                                           \
157     bool isSet() {                                                                              \
158         return !getOptionName().empty();                                                        \
159     }                                                                                           \
160     template<typename Type>                                                                     \
161     Type& set(const Type& src) {                                                                \
162         getHolder() = std::forward<const Type>(src);                                            \
163         assert(!getOptionName().empty() && "Unsupported type");                                 \
164         return as<Type>();                                                                      \
165     }                                                                                           \
166
167 #define GENERATE_CODE(MACRO, ...)                                                               \
168     BOOST_PP_LIST_FOR_EACH(MACRO, _, BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__))
169
170 #define GENERATE_UNION_VISIT__(r, _, TYPE_)                                                     \
171     if (#TYPE_ == name) {                                                                       \
172         v.visit("value", set(std::move(TYPE_())));                                              \
173         return;                                                                                 \
174     }
175
176 #define GENERATE_UNION_VISIT_CONST__(r, _, TYPE_)                                               \
177     if (#TYPE_ == name) {                                                                       \
178         v.visit("value", as<const TYPE_>());                                                    \
179         return;                                                                                 \
180     }
181
182 #define GENERATE_UNION_NAME__(r, _, TYPE_)                                                      \
183     if (is<TYPE_>()) {                                                                          \
184         return #TYPE_;                                                                          \
185     }
186
187 #endif /* CONFIG_FIELDS_UNION_HPP */