1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2 
3 #ifndef OBJECT_H
4 #define OBJECT_H
5 
6 #include "base/i2-base.hpp"
7 #include "base/debug.hpp"
8 #include <boost/smart_ptr/intrusive_ptr.hpp>
9 #include <atomic>
10 #include <cstddef>
11 #include <cstdint>
12 #include <mutex>
13 #include <thread>
14 #include <vector>
15 
16 using boost::intrusive_ptr;
17 using boost::dynamic_pointer_cast;
18 using boost::static_pointer_cast;
19 
20 namespace icinga
21 {
22 
23 class Value;
24 class Object;
25 class Type;
26 class String;
27 struct DebugInfo;
28 class ValidationUtils;
29 
30 extern Value Empty;
31 
32 #define DECLARE_PTR_TYPEDEFS(klass) \
33 	typedef intrusive_ptr<klass> Ptr
34 
35 #define IMPL_TYPE_LOOKUP_SUPER() 					\
36 
37 #define IMPL_TYPE_LOOKUP() 							\
38 	static intrusive_ptr<Type> TypeInstance;				\
39 	virtual intrusive_ptr<Type> GetReflectionType() const override		\
40 	{									\
41 		return TypeInstance;						\
42 	}
43 
44 #define DECLARE_OBJECT(klass) \
45 	DECLARE_PTR_TYPEDEFS(klass); \
46 	IMPL_TYPE_LOOKUP();
47 
48 #define REQUIRE_NOT_NULL(ptr) RequireNotNullInternal(ptr, #ptr)
49 
50 void RequireNotNullInternal(const intrusive_ptr<Object>& object, const char *description);
51 
52 void DefaultObjectFactoryCheckArgs(const std::vector<Value>& args);
53 
54 template<typename T>
DefaultObjectFactory(const std::vector<Value> & args)55 intrusive_ptr<Object> DefaultObjectFactory(const std::vector<Value>& args)
56 {
57 	DefaultObjectFactoryCheckArgs(args);
58 
59 	return new T();
60 }
61 
62 template<typename T>
DefaultObjectFactoryVA(const std::vector<Value> & args)63 intrusive_ptr<Object> DefaultObjectFactoryVA(const std::vector<Value>& args)
64 {
65 	return new T(args);
66 }
67 
68 typedef intrusive_ptr<Object> (*ObjectFactory)(const std::vector<Value>&);
69 
70 template<typename T, bool VA>
71 struct TypeHelper
72 {
73 };
74 
75 template<typename T>
76 struct TypeHelper<T, false>
77 {
GetFactoryicinga::TypeHelper78 	static ObjectFactory GetFactory()
79 	{
80 		return DefaultObjectFactory<T>;
81 	}
82 };
83 
84 template<typename T>
85 struct TypeHelper<T, true>
86 {
GetFactoryicinga::TypeHelper87 	static ObjectFactory GetFactory()
88 	{
89 		return DefaultObjectFactoryVA<T>;
90 	}
91 };
92 
93 template<typename T>
94 struct Lazy
95 {
96 	using Accessor = std::function<T ()>;
97 
Lazyicinga::Lazy98 	explicit Lazy(T value)
99 		: m_Cached(true), m_Value(value)
100  	{ }
101 
Lazyicinga::Lazy102 	explicit Lazy(Accessor accessor)
103 		: m_Accessor(accessor)
104 	{ }
105 
106 	template<typename U>
Lazyicinga::Lazy107 	explicit Lazy(const Lazy<U>& other)
108 	{
109 		if (other.m_Cached) {
110 			m_Accessor = Accessor();
111 			m_Value = static_cast<T>(other.m_Value);
112 			m_Cached = true;
113 		} else {
114 			auto accessor = other.m_Accessor;
115 			m_Accessor = [accessor]() { return static_cast<T>(accessor()); };
116 			m_Cached = false;
117 		}
118 	}
119 
120 	template<typename U>
operator Lazy<U>icinga::Lazy121 	operator Lazy<U>() const
122 	{
123 		if (m_Cached)
124 			return Lazy<U>(static_cast<U>(m_Value));
125 		else {
126 			Accessor accessor = m_Accessor;
127 			return Lazy<U>(static_cast<typename Lazy<U>::Accessor>([accessor]() { return static_cast<U>(accessor()); }));
128 		}
129 	}
130 
operator ()icinga::Lazy131 	const T& operator()() const
132 	{
133 		if (!m_Cached) {
134 			m_Value = m_Accessor();
135 			m_Cached = true;
136 		}
137 
138 		return m_Value;
139 	}
140 
141 private:
142 	Accessor m_Accessor;
143 	mutable bool m_Cached{false};
144 	mutable T m_Value;
145 
146 	template<typename U>
147 	friend struct Lazy;
148 };
149 
150 /**
151  * Base class for all heap-allocated objects. At least one of its methods
152  * has to be virtual for RTTI to work.
153  *
154  * @ingroup base
155  */
156 class Object
157 {
158 public:
159 	DECLARE_PTR_TYPEDEFS(Object);
160 
161 	Object();
162 	virtual ~Object();
163 
164 	virtual String ToString() const;
165 
166 	virtual intrusive_ptr<Type> GetReflectionType() const;
167 
168 	virtual void Validate(int types, const ValidationUtils& utils);
169 
170 	virtual void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty);
171 	virtual Value GetField(int id) const;
172 	virtual Value GetFieldByName(const String& field, bool sandboxed, const DebugInfo& debugInfo) const;
173 	virtual void SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo);
174 	virtual bool HasOwnField(const String& field) const;
175 	virtual bool GetOwnField(const String& field, Value *result) const;
176 	virtual void ValidateField(int id, const Lazy<Value>& lvalue, const ValidationUtils& utils);
177 	virtual void NotifyField(int id, const Value& cookie = Empty);
178 	virtual Object::Ptr NavigateField(int id) const;
179 
180 #ifdef I2_DEBUG
181 	bool OwnsLock() const;
182 #endif /* I2_DEBUG */
183 
184 	static Object::Ptr GetPrototype();
185 
186 	virtual Object::Ptr Clone() const;
187 
188 	static intrusive_ptr<Type> TypeInstance;
189 
190 private:
191 	Object(const Object& other) = delete;
192 	Object& operator=(const Object& rhs) = delete;
193 
194 	std::atomic<uint_fast64_t> m_References;
195 	mutable std::recursive_mutex m_Mutex;
196 
197 #ifdef I2_DEBUG
198 	mutable std::atomic<std::thread::id> m_LockOwner;
199 	mutable size_t m_LockCount = 0;
200 #endif /* I2_DEBUG */
201 
202 	friend struct ObjectLock;
203 
204 	friend void intrusive_ptr_add_ref(Object *object);
205 	friend void intrusive_ptr_release(Object *object);
206 };
207 
208 Value GetPrototypeField(const Value& context, const String& field, bool not_found_error, const DebugInfo& debugInfo);
209 
210 void TypeAddObject(Object *object);
211 void TypeRemoveObject(Object *object);
212 
213 void intrusive_ptr_add_ref(Object *object);
214 void intrusive_ptr_release(Object *object);
215 
216 template<typename T>
217 class ObjectImpl
218 {
219 };
220 
221 }
222 
223 #endif /* OBJECT_H */
224 
225 #include "base/type.hpp"
226