Fix: Crashing when writing unset union type
[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
34 /**
35  * Use this macro to declare and register config fields
36  *
37  * Example:
38  *  struct Foo {
39  *      std::string bar;
40  *
41  *      CONFIG_REGISTER
42  *      (
43  *          bar,
44  *      )
45  *  };
46  *
47  *  struct Config
48  *  {
49  *      CONFIG_DECLARE_UNION
50  *      (
51  *          Foo,
52  *          int
53  *      )
54  *  };
55  *
56  *  Example of valid configuration:
57  *   1. {
58  *        "type": "Foo",
59  *        "value": { "bar": "some string" }
60  *      }
61  *   2. {
62  *        "type": "int",
63  *        "value": 1
64  *      }
65  *
66  *
67  *  Usage:
68  *   Config config;
69  *   // ...
70  *   if (config.isSet()) {
71  *      if (config.is<Foo>()) {
72  *          Foo& foo = config.as<Foo>();
73  *          // ...
74  *      }
75  *      if (config.is<int>())) {
76  *          int field = config.as<int>();
77  *          // ...
78  *      }
79  *   } else {
80  *     // Config is not set
81  *     Foo foo({"some string"});
82  *     config.set(foo);
83  *     config.set(std::move(foo));         //< copy sic!
84  *     config.set(Foo({"some string"}));
85  *   }
86  */
87
88 class DisbaleMoveAnyWrapper : public boost::any
89 {
90     public:
91         DisbaleMoveAnyWrapper() {}
92         DisbaleMoveAnyWrapper(const DisbaleMoveAnyWrapper& any)
93             : boost::any(static_cast<const boost::any&>(any)) {};
94         DisbaleMoveAnyWrapper& operator=(DisbaleMoveAnyWrapper&& any) = delete;
95         DisbaleMoveAnyWrapper& operator=(const DisbaleMoveAnyWrapper& any) {
96             static_cast<boost::any&>(*this) = static_cast<const boost::any&>(any);
97             return *this;
98         }
99 };
100
101 #define CONFIG_DECLARE_UNION(...)                                                               \
102 private:                                                                                        \
103     DisbaleMoveAnyWrapper mConfigDeclareField;                                                  \
104                                                                                                 \
105     template<typename Visitor>                                                                  \
106     void visitOption(Visitor& v, const std::string& name) {                                     \
107         GENERATE_CODE(GENERATE_UNION_VISIT__, __VA_ARGS__)                                      \
108         throw config::ConfigException("Union type error. Unsupported type");                    \
109     }                                                                                           \
110     template<typename Visitor>                                                                  \
111     void visitOption(Visitor& v, const std::string& name) const {                               \
112         GENERATE_CODE(GENERATE_UNION_VISIT_CONST__, __VA_ARGS__)                                \
113         throw config::ConfigException("Union type error. Unsupported type");                    \
114     }                                                                                           \
115     std::string getOptionName() const {                                                         \
116         GENERATE_CODE(GENERATE_UNION_NAME__, __VA_ARGS__)                                       \
117         return std::string();                                                                   \
118     }                                                                                           \
119     boost::any& getHolder() {                                                                   \
120         return static_cast<boost::any&>(mConfigDeclareField);                                   \
121     }                                                                                           \
122     const boost::any& getHolder() const {                                                       \
123         return static_cast<const boost::any&>(mConfigDeclareField);                             \
124     }                                                                                           \
125 public:                                                                                         \
126                                                                                                 \
127     template<typename Visitor>                                                                  \
128     void accept(Visitor v) {                                                                    \
129         std::string name;                                                                       \
130         v.visit("type", name);                                                                  \
131         visitOption(v, name);                                                                   \
132     }                                                                                           \
133                                                                                                 \
134     template<typename Visitor>                                                                  \
135     void accept(Visitor v) const {                                                              \
136         const std::string name = getOptionName();                                               \
137         if (name.empty()) {                                                                     \
138            throw config::ConfigException("Type is not set");                                    \
139         }                                                                                       \
140         v.visit("type", name);                                                                  \
141         visitOption(v, name);                                                                   \
142     }                                                                                           \
143                                                                                                 \
144     template<typename Type>                                                                     \
145     bool is() const {                                                                           \
146         return boost::any_cast<Type>(&getHolder()) != NULL;                                     \
147     }                                                                                           \
148     template<typename Type>                                                                     \
149     typename std::enable_if<!std::is_const<Type>::value, Type>::type& as() {                    \
150         if (getHolder().empty()) {                                                              \
151             throw config::ConfigException("Type is not set");                                   \
152         }                                                                                       \
153         return boost::any_cast<Type&>(getHolder());                                             \
154     }                                                                                           \
155     template<typename Type>                                                                     \
156     const Type& as() const {                                                                    \
157         if (getHolder().empty()) {                                                              \
158             throw config::ConfigException("Type is not set");                                   \
159         }                                                                                       \
160         return boost::any_cast<const Type&>(getHolder());                                       \
161     }                                                                                           \
162     bool isSet() {                                                                              \
163         return !getOptionName().empty();                                                        \
164     }                                                                                           \
165     template<typename Type>                                                                     \
166     Type& set(const Type& src) {                                                                \
167         getHolder() = std::forward<const Type>(src);                                            \
168         if (getOptionName().empty()) {                                                          \
169            throw config::ConfigException("Unsupported type");                                   \
170         }                                                                                       \
171         return as<Type>();                                                                      \
172     }                                                                                           \
173
174 #define GENERATE_CODE(MACRO, ...)                                                               \
175     BOOST_PP_LIST_FOR_EACH(MACRO, _, BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__))
176
177 #define GENERATE_UNION_VISIT__(r, _, TYPE_)                                                     \
178     if (#TYPE_ == name) {                                                                       \
179         v.visit("value", set(std::move(TYPE_())));                                              \
180         return;                                                                                 \
181     }
182
183 #define GENERATE_UNION_VISIT_CONST__(r, _, TYPE_)                                               \
184     if (#TYPE_ == name) {                                                                       \
185         v.visit("value", as<const TYPE_>());                                                    \
186         return;                                                                                 \
187     }
188
189 #define GENERATE_UNION_NAME__(r, _, TYPE_)                                                      \
190     if (is<TYPE_>()) {                                                                          \
191         return #TYPE_;                                                                          \
192     }
193
194 #endif /* CONFIG_FIELDS_UNION_HPP */