Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1702 | - | 1 | // -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- |
2 | // |
||
3 | // This is free software; you can redistribute it and/or modify it under |
||
4 | // the terms of the GNU Lesser General Public License as published by the |
||
5 | // Free Software Foundation; either version 2.1 of the License, or (at |
||
6 | // your option) any later version. |
||
7 | // |
||
8 | |||
9 | /// @file AP_Meta_class.h |
||
10 | /// @brief An abstract base class from which other classes can inherit. |
||
11 | /// |
||
12 | /// This abstract base class declares and implements functions that are |
||
13 | /// useful to code that wants to know things about a class, or to operate |
||
14 | /// on the class without knowing precisely what it is. |
||
15 | /// |
||
16 | /// All classes that inherit from this class can be assumed to have these |
||
17 | /// basic functions. |
||
18 | /// |
||
19 | |||
20 | #ifndef AP_META_CLASS_H |
||
21 | #define AP_META_CLASS_H |
||
22 | |||
23 | #include <stddef.h> // for size_t |
||
24 | #include <inttypes.h> |
||
25 | |||
26 | #include <avr/io.h> // for RAMEND |
||
27 | /// |
||
28 | /// Basic meta-class from which other AP_* classes can derive. |
||
29 | /// |
||
30 | /// Functions that form the public API to the metaclass are prefixed meta_. |
||
31 | /// |
||
32 | /// Note that classes inheriting from AP_Meta_class *must* have a default |
||
33 | /// constructor and destructor in order for meta_cast to work. |
||
34 | /// |
||
35 | class AP_Meta_class |
||
36 | { |
||
37 | public: |
||
38 | /// Default constructor does nothing. |
||
39 | AP_Meta_class(void); |
||
40 | |||
41 | /// Default destructor is virtual, to ensure that all subclasses' |
||
42 | /// destructors are virtual. This guarantees that all destructors |
||
43 | /// in the inheritance chain are called at destruction time. |
||
44 | /// |
||
45 | virtual ~AP_Meta_class(); |
||
46 | |||
47 | /// Typedef for the ID unique to all instances of a class. |
||
48 | /// |
||
49 | /// See ::meta_type_id for a discussion of class type IDs. |
||
50 | /// |
||
51 | typedef uint16_t Type_id; |
||
52 | |||
53 | /// Obtain a value unique to all instances of a specific subclass. |
||
54 | /// |
||
55 | /// The value can be used to determine whether two class pointers |
||
56 | /// refer to the same exact class type. The value can also be cached |
||
57 | /// and then used to detect objects of a given type at a later point. |
||
58 | /// |
||
59 | /// This is similar to the basic functionality of the C++ typeid |
||
60 | /// keyword, but does not depend on std::type_info or any compiler- |
||
61 | /// generated RTTI. |
||
62 | /// |
||
63 | /// The value is derived from the vtable address, so it is guaranteed |
||
64 | /// to be unique but cannot be known until the program has been compiled |
||
65 | /// and linked. Thus, the only way to know the type ID of a given |
||
66 | /// type is to construct an object at runtime. To cache the type ID |
||
67 | /// of a class Foo, see the templated version below: |
||
68 | /// |
||
69 | /// @return A type-unique value for this. |
||
70 | /// |
||
71 | Type_id meta_type_id(void) const { |
||
72 | return *(Type_id *)this; |
||
73 | } |
||
74 | |||
75 | /// Obtain a value unique to all instances of a named subclass. |
||
76 | /// |
||
77 | /// This is similar to ::meta_type_id, but is a template taking a class name. |
||
78 | /// Use this function to cache the Type_id for a class when you don't need |
||
79 | /// or cannot afford the constructor cost associated with meta_cast. |
||
80 | /// |
||
81 | /// @tparam T A subclass of AP_Meta_class. |
||
82 | /// @return The Type_id value for T. |
||
83 | /// |
||
84 | template<typename T> |
||
85 | static Type_id meta_type_id(void) { |
||
86 | T tmp; |
||
87 | return tmp.meta_type_id(); |
||
88 | } |
||
89 | |||
90 | /// External handle for an instance of an AP_Meta_class subclass, contains |
||
91 | /// enough information to construct and validate a pointer to the instance |
||
92 | /// when passed back from an untrusted source. |
||
93 | /// |
||
94 | /// Handles are useful when passing a reference to an object to a client outside |
||
95 | /// the system, as they can be validated by the system when the client hands |
||
96 | /// them back. |
||
97 | /// |
||
98 | typedef uint32_t Meta_handle; |
||
99 | |||
100 | /// Return a value that can be used as an external pointer to an instance |
||
101 | /// of a subclass. |
||
102 | /// |
||
103 | /// The value can be passed to an untrusted agent, and validated on its return. |
||
104 | /// |
||
105 | /// The value contains the 16-bit type ID of the actual class and |
||
106 | /// a pointer to the class instance. |
||
107 | /// |
||
108 | /// @return An opaque handle |
||
109 | /// |
||
110 | Meta_handle meta_get_handle(void) const { |
||
111 | return ((Meta_handle)meta_type_id() << 16) | (uintptr_t)this; |
||
112 | } |
||
113 | |||
114 | /// Validates an AP_Meta_class handle. |
||
115 | /// |
||
116 | /// The value of the handle is not required to be valid; in particular the |
||
117 | /// pointer encoded in the handle is validated before being dereferenced. |
||
118 | /// |
||
119 | /// The handle is considered good if the pointer is valid and the object |
||
120 | /// it points to has a type ID that matches the ID in the handle. |
||
121 | /// |
||
122 | /// @param handle A possible AP_Meta_class handle |
||
123 | /// @return The instance pointer if the handle is good, |
||
124 | /// or NULL if it is bad. |
||
125 | /// |
||
126 | static AP_Meta_class *meta_validate_handle(Meta_handle handle) { |
||
127 | AP_Meta_class *candidate = (AP_Meta_class *)(handle & 0xffff); // extract object pointer |
||
128 | uint16_t id = handle >> 16; // and claimed type |
||
129 | |||
130 | // Sanity-check the pointer to ensure it lies within the device RAM, so that |
||
131 | // a bad handle won't cause ::meta_type_id to read outside of SRAM. |
||
132 | // Assume that RAM (or addressable storage of some sort, at least) starts at zero. |
||
133 | // |
||
134 | // Note that this implies that we cannot deal with objects in ROM or EEPROM, |
||
135 | // but the constructor wouldn't be able to populate a vtable pointer there anyway... |
||
136 | // |
||
137 | if ((uintptr_t)candidate >= (RAMEND - 2)) { // -2 to account for the type_id |
||
138 | return NULL; |
||
139 | } |
||
140 | |||
141 | // Compare the typeid of the object that candidate points to with the typeid |
||
142 | // from the handle. Note that it's safe to call meta_type_id() off the untrusted |
||
143 | // candidate pointer because meta_type_id is non-virtual (and will in fact be |
||
144 | // inlined here). |
||
145 | // |
||
146 | if (candidate->meta_type_id() == id) { |
||
147 | return candidate; |
||
148 | } |
||
149 | |||
150 | return NULL; |
||
151 | } |
||
152 | |||
153 | /// Tests whether two objects are of precisely the same class. |
||
154 | /// |
||
155 | /// Note that in the case where p2 inherits from p1, or vice-versa, this will return |
||
156 | /// false as we cannot detect these inheritance relationships at runtime. |
||
157 | /// |
||
158 | /// In the caller's context, p1 and p2 may be pointers to any type, but we require |
||
159 | /// that they be passed as pointers to AP_Meta_class in order to make it clear that |
||
160 | /// they should be pointers to classes derived from AP_Meta_class. |
||
161 | /// |
||
162 | /// No attempt is made to validate whether p1 and p2 are actually derived from |
||
163 | /// AP_Meta_class. If p1 and p2 are equal, or if they point to non-class objects with |
||
164 | /// similar contents, or to non-AP_Meta_class derived classes with no virtual functions |
||
165 | /// this function may return true. |
||
166 | /// |
||
167 | /// @param p1 The first object to be compared. |
||
168 | /// @param p2 The second object to be compared. |
||
169 | /// @return True if the two objects are of the same class, false |
||
170 | /// if they are not. |
||
171 | /// |
||
172 | static bool meta_type_equivalent(AP_Meta_class *p1, AP_Meta_class *p2) { |
||
173 | return p1->meta_type_id() == p2->meta_type_id(); |
||
174 | } |
||
175 | |||
176 | /// Cast a pointer to an expected class type. |
||
177 | /// |
||
178 | /// This function is used when a pointer is expected to be a pointer to a |
||
179 | /// subclass of AP_Meta_class, but the caller is not certain. It will return the pointer |
||
180 | /// if it is, or NULL if it is not a pointer to the expected class. |
||
181 | /// |
||
182 | /// This should be used with caution, as T's default constructor and |
||
183 | /// destructor will be run, possibly introducing undesired side-effects. |
||
184 | /// |
||
185 | /// @todo Consider whether we should make it difficult to have a default constructor |
||
186 | /// with appreciable side-effects. |
||
187 | /// |
||
188 | /// @param p An AP_Meta_class subclass that may be of type T. |
||
189 | /// @tparam T The name of a type to which p is to be cast. |
||
190 | /// @return NULL if p is not of precisely type T, otherwise p cast to T. |
||
191 | /// |
||
192 | template<typename T> |
||
193 | static T *meta_cast(AP_Meta_class *p) { |
||
194 | T tmp; |
||
195 | if (meta_type_equivalent(p, &tmp)) { |
||
196 | return (T *)p; |
||
197 | } |
||
198 | return NULL; |
||
199 | } |
||
200 | |||
201 | /// Cast this to an expected class type. |
||
202 | /// |
||
203 | /// This is equivalent to meta_cast<T>(this) |
||
204 | /// |
||
205 | /// @tparam T The name of a type to which this is to be cast. |
||
206 | /// @return NULL if this is not of precisely type T, otherwise this cast to T. |
||
207 | /// |
||
208 | template<typename T> |
||
209 | T *meta_cast(void) { |
||
210 | T tmp; |
||
211 | if (meta_type_equivalent(this, &tmp)) { |
||
212 | return (T*)this; |
||
213 | } |
||
214 | return NULL; |
||
215 | } |
||
216 | |||
217 | /// Serialize the class. |
||
218 | /// |
||
219 | /// Serialization stores the state of the class in an external buffer in such a |
||
220 | /// fashion that it can later be restored by unserialization. |
||
221 | /// |
||
222 | /// AP_Meta_class subclasses should only implement these functions if saving and |
||
223 | /// restoring their state makes sense. |
||
224 | /// |
||
225 | /// Serialization provides a mechanism for exporting the state of the class to an |
||
226 | /// external consumer, either for external introspection or for subsequent restoration. |
||
227 | /// |
||
228 | /// Classes that wrap variables should define the format of their serialiaed data |
||
229 | /// so that external consumers can reliably interpret it. |
||
230 | /// |
||
231 | /// @param buf Buffer into which serialised data should be placed. |
||
232 | /// @param bufSize The size of the buffer provided. |
||
233 | /// @return The size of the serialised data, even if that data would |
||
234 | /// have overflowed the buffer. If the return value is zero, |
||
235 | /// the class does not support serialization. |
||
236 | /// |
||
237 | virtual size_t serialize(void *buf, size_t bufSize) const; |
||
238 | |||
239 | /// Unserialize the class. |
||
240 | /// |
||
241 | /// Unserializing a class from a buffer into which the class previously serialized |
||
242 | /// itself restores the instance to an identical state, where "identical" is left |
||
243 | /// up to the class itself to define. |
||
244 | /// |
||
245 | /// Classes that wrap variables should define the format of their serialized data so |
||
246 | /// that external providers can reliably encode it. |
||
247 | /// |
||
248 | /// @param buf Buffer containing serialized data. |
||
249 | /// @param bufSize The size of the buffer. |
||
250 | /// @return The number of bytes from the buffer that would be consumed |
||
251 | /// unserializing the data. If the value is less than or equal |
||
252 | /// to bufSize, unserialization was successful. If the return |
||
253 | /// value is zero the class does not support unserialisation or |
||
254 | /// the data in the buffer is invalid. |
||
255 | /// |
||
256 | virtual size_t unserialize(void *buf, size_t bufSize); |
||
257 | }; |
||
258 | |||
259 | #endif // AP_Meta_class_H |