Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / platform / README.md
1 # CHIP Device Layer Design Notes
2
3 This document contains overviews, notes and other informative material related
4 to the internal design of the CHIP Device Layer (`src/platform`). It is intended
5 as a place to host documentation on topics that are of value to implementers,
6 but for reasons of size or scope don't naturally fit within comments in the
7 code.
8
9 This is intended to be a living document, with an informal structure, that
10 evolves along with the code. Developers are encouraged to add things that they
11 think would be useful to their fellow engineers.
12
13 This document contains the following sections:
14
15 -   [Device Layer Adaptation Patterns](#Device-Layer-Adaptation-Patterns)
16
17 ---
18
19 ### Device Layer Adaptation Patterns
20
21 The Device Layer uses various design patterns to make it easier to adapt the
22 code to different platforms and operating contexts.
23
24 The CHIP Device Layer is intended to work across a variety of platforms and
25 operating contexts. These environments may differ by system type, OS, network
26 stack and/or threading model. One of the goals of the Device Layer is to make it
27 easy to adapt the CHIP application stack to new environments. This is especially
28 desirable in cases where the new platform is substantially similar to an
29 existing adaptation.
30
31 As part of its design, the CHIP Device Layer enables a pattern of code reuse
32 that strives to reduce the need for preprocessor conditionals (e.g. #ifdef).
33 While not eliminating #ifdefs entirely, the design allows major variances in
34 behavior to be expressed as distinct code bases (typically separate C++ classes)
35 which are then brought together via composition to enable a particular
36 adaptation.
37
38 To promote application portability, the CHIP Device Layer employs a pattern of
39 static polymorphism to insulate its application-visible API from the underlying
40 platform-specific implementation. A similar interface pattern is used within the
41 Device Layer itself to provide compartmentalization between components.
42
43 As much as possible, the above goals are achieved via the use of zero-cost
44 abstraction patterns (zero-cost in terms of code size and execution overhead).
45 Effort has been made to make the patterns easy to work with, without a lot of
46 conceptual burden or fiddly syntax.
47
48 This following sections describe some of the patterns used to achieve these
49 goals.
50
51 1. [Interface and Implementation Classes](#Interface-and-Implementation-Classes)<br>
52 2. [Method Forwarding](#Method-Forwarding)<br>
53 3. [Target Platform Selection](#Target-Platform-Selection)<br>
54 4. [Generic Implementation Classes](#Generic-Implementation-Classes)<br>
55 5. [Overriding Generic Behaviors](#Overriding-Generic-Behaviors)<br>
56 6. [Multiple Inheritance and Subclassing of Generic Implementations](#Multiple-Inheritance-and-Subclassing-of-Generic-Implementations)<br>
57 7. [Static Virtualization of Generic Implementation Behavior](#Static-Virtualization-of-Generic-Implementation-Behavior)<br>
58 8. [.cpp Files and Explicit Template Instantiation](#-ipp-files-and-explicit-template-instantiation)<br>
59
60 ---
61
62 ### Interface and Implementation Classes
63
64 The CHIP Device Layer uses a dual-class pattern to separate the abstract
65 features of a component object (generally its externally visible methods) from
66 the concrete implementation of those features on a particular platform.
67 Following this pattern, each major component in the Device Layer is embodied in
68 (at least) two C++ classes: an abstract interface class and an implementation
69 class.
70
71 The outwardly visible _**abstract interface class**_ defines a set of common
72 methods (and potentially other members) that are universally available to the
73 component user, but independent of the underlying implementation. Interface
74 classes contains no functionality themselves, but instead forward all method
75 calls to an associated implementation class using a zero-cost abstraction
76 technique. Interface classes serve to formalize the functional interface of a
77 component, as well as provide a place on which to host implementation-neutral
78 API documentation.
79
80 An _**implementation class**_ provides a concrete, platform-specific
81 implementation of the logical functionality exposed by an interface class. This
82 functionality may be provided directly by the class itself (i.e. within its
83 methods), or via delegation to one or more helper classes.
84
85 Pairs of abstract interface classes and implementation classes exists for each
86 major application-visible component of the Device Layer. Additionally, similar
87 pairs of classes are defined within the Device Layer to aid in isolation between
88 components.
89
90 Abstract interface classes are named after the functionality they provide–e.g.
91 ConfigurationManager, ConnectivityManager, etc. Implementation classes take the
92 name of their interface class with the suffix `Impl` attached. In all cases,
93 implementation class are required to inherit publicly from their interface
94 classes.
95
96 ```cpp
97 class ConfigurationManagerImpl;
98
99 /** Interface class for ConfigurationManager component
100  */
101 class ConfigurationManager
102 {
103     using ImplClass = ConfigurationManagerImpl;
104
105 public:
106     CHIP_ERROR GetDeviceId(uint64_t & deviceId);
107     static CHIP_ERROR Init();
108     ...
109 };
110
111 /** Concrete implementation of ConfigurationManager component for a specific platform
112  */
113 class ConfigurationManagerImpl final
114     : public ConfigurationManager
115 {
116     ...
117 };
118 ```
119
120 ### Method Forwarding
121
122 Interface classes forward method calls to their implementation classes by means
123 of short inline functions called **_forwarding methods_**. These methods forward
124 calls from the application by downcasting the object’s `this` pointer and
125 invoking similarly named methods on the implementation class. This pattern is
126 similar to the C++
127 [Curiously Recurring Template Pattern](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
128 with the exception that the relationship between the base class and the subclass
129 is fixed, rather than being expressed as a template parameter. A type alias
130 named `ImplClass` is used within the interface to make the forwarding method
131 definitions more concise.
132
133 ```cpp
134 inline CHIP_ERROR ConfigurationManager::GetDeviceId(uint64_t & deviceId)
135 {
136     /* forward method call... */
137     return static_cast<ImplClass*>(this)->_GetDeviceId(deviceId);
138 }
139 ```
140
141 One convenient feature of this pattern is it allows for the forwarding of static
142 methods as well as instance methods. E.g.:
143
144 ```cpp
145 inline CHIP_ERROR ConfigurationManager::Init()
146 {
147     return ImplClass::_Init();
148 }
149 ```
150
151 The methods on the implementation class that are the target of forwarding
152 methods are known as **_implementation methods_**. For every forwarding method
153 there must be a corresponding implementation method.
154
155 A leading underscore (\_) is used to distinguish implementation methods from
156 their forwarding methods. This arrangement helps to emphasize the distinction
157 between the two, and ensures the generation of a compilation error should an
158 implementer neglect to provide an implementation method.
159
160 Implementation methods are not meant to be called directly. To block this type
161 of use, implementation classes declare their implementation methods as private,
162 and then use a friend declaration to give the interface class the (sole) right
163 to invoke these methods as part of forwarding.
164
165 ```cpp
166 class ConfigurationManagerImpl;
167
168 /** Interface class for ConfigurationManager component
169  */
170 class ConfigurationManager
171 {
172     using ImplClass = ConfigurationManagerImpl;
173
174 public:
175     CHIP_ERROR GetDeviceId(uint64_t & deviceId);
176     static CHIP_ERROR Init();
177     ...
178 };
179
180 /** Concrete implementation of ConfigurationManager component for specific platform
181  */
182 class ConfigurationManagerImpl final : public ConfigurationManager
183 {
184     /* Let the forwarding methods on ConfigurationManager call implementation
185        methods on this class. */
186     friend ConfigurationManager;
187
188 private:
189     CHIP_ERROR _GetDeviceId(uint64_t & deviceId);
190     static CHIP_ERROR _Init();
191     ...
192 };
193
194 inline CHIP_ERROR ConfigurationManager::GetDeviceId(uint64_t & deviceId)
195 {
196     /* Forward calls to corresponding implementation method... */
197     return static_cast<ImplClass*>(this)->_GetDeviceId(deviceId);
198 }
199
200 inline CHIP_ERROR ConfigurationManager::Init()
201 {
202     /* Forward calls to corresponding static implementation method... */
203     return ImplClass::_Init();
204 }
205 ```
206
207 ### Target Platform Selection
208
209 An implementation class provides a concrete implementation of a Device Layer
210 component for use on a particular platform. Multiple implementation classes may
211 exist within the Device Layer source tree for the same component. Each of these
212 classes has the same name, but their code is unique to the associated platform.
213 The choice of which implementation class is included at compile time is done by
214 means of a computed #include directive with the following form:
215
216 ```cpp
217 /* contents of ConfigurationManager.h */
218
219 ...
220
221 #define CONFIGURATIONMANAGERIMPL_HEADER \
222         <platform/CHIP_DEVICE_LAYER_TARGET/ConfigurationManagerImpl.h>
223 #include CONFIGURATIONMANAGERIMPL_HEADER
224
225 ...
226 ```
227
228 This directive appears within the header file that defines the component’s
229 interface class. The C++ pre-processor automatically expands the #include line
230 to select the appropriate implementation header based on the chosen platform. In
231 this way, source files which include the component’s interface header file
232 naturally get the correct implementation header as well.
233
234 Implementation header files for each supported platform are arranged within
235 subdirectories that are named after their target platform (e.g. `ESP32`). All
236 such files have the same file name (e.g. `ConfigurationManagerImpl.h`), and each
237 file contains a definition for like-named class (`ConfigurationManagerImpl`).
238
239 Source files specific to a platform are placed in subdirectories immediately
240 beneath the root Device Layer source directory (e.g.
241 `src/adaptations/device-layer/ESP32`). Like platform-specific header
242 directories, these are subdirectories are named after the target platform.
243
244 The choice of target platform for the Device Layer is specified at project
245 configuration time, using the configure script option
246 `--device-layer=<target-platform>`. Passing the --device-layer option results in
247 the definition of a pair of pre-processor symbols where the name of the target
248 platform is incorporated into the definitions. E.g.:
249
250 ```cpp
251 #define CHIP_DEVICE_LAYER_TARGET ESP32
252 #define CHIP_DEVICE_LAYER_TARGET_ESP32 1
253 ```
254
255 The --device-layer configuration option also selects the appropriate set of
256 platform-specific source files to be included in the generated library file.
257 This is accomplished via logic in the Device Layer’s Makefile.am.
258
259 ### Generic Implementation Classes
260
261 Often it is possible to share implementation code across a range of platforms.
262 In some cases the code in question is substantially the same for all targets,
263 with only minor customizations required in each case. In other cases, the
264 commonality of the implementation extends across a subset of platforms that
265 share a particular architectural feature, such as a common OS (Linux, FreeRTOS)
266 or network stack (sockets, LwIP).
267
268 To accommodate this, the CHIP Device Layer encourages a pattern of factoring
269 common functionality into **_generic implementation base classes_**. These base
270 classes are then used to compose (via inheritance) the concrete implementation
271 classes that underlie a component.
272
273 Generic implementation base classes are realized as C++ class templates which
274 follow the C++
275 [Curiously Recurring Template Pattern](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).
276 Implementation classes wishing to incorporate common behavior inherit from an
277 instantiation of the template, passing the implementation class itself as the
278 template’s parameter.
279
280 ```cpp
281 /** Generic base class for use in implementing ConfigurationManager components
282  */
283 template<class ImplClass>
284 class GenericConfigurationManagerImpl
285 {
286     ...
287 };
288
289 /** Concrete implementation of ConfigurationManager component for specific platform
290  */
291 class ConfigurationManagerImpl final
292     : public ConfigurationManager,
293       public GenericConfigurationManagerImpl<ConfigurationManagerImpl> /* <-- Implementation provided by
294                                                                               generic base class. */
295 {
296     ...
297 };
298 ```
299
300 In many cases, the generic implementation base class itself will directy provide
301 some or all of the implementation methods needed to satisfy the component’s
302 interface. The rules of C++ method resolution are such that calls to a
303 forwarding method on the interface class are mapped directly to the base class
304 method. In this situation, the derived implementation class needn't declare a
305 version of the target method at all, and method calls are forwarded statically,
306 at compile time, without overhead.
307
308 ```cpp
309 /** Interface class for ConfigurationManager component
310  */
311 class ConfigurationManager
312 {
313     using ImplClass = ConfigurationManagerImpl;
314
315 public:
316     CHIP_ERROR GetDeviceId(uint64_t & deviceId);
317     static CHIP_ERROR Init();
318     ...
319 };
320
321 /** Generic base class for use in implementing ConfigurationManager components
322  */
323 template<class ImplClass>
324 class GenericConfigurationManagerImpl
325 {
326 protected:
327     CHIP_ERROR _GetDeviceId(uint64_t & deviceId); /* <-- Invoked when GetDeviceId() called. */
328     ...
329 };
330
331 /** Concrete implementation of ConfigurationManager component for specific platform
332  */
333 class ConfigurationManagerImpl final
334     : public ConfigurationManager,
335       public GenericConfigurationManagerImpl<ConfigurationManagerImpl>
336 {
337     ...
338 };
339 ```
340
341 ### Overriding Generic Behaviors
342
343 Where desirable, concrete implementation classes are free to override the
344 implementation methods provided by a generic base class. This is accomplished by
345 defining a platform-specific version of the method on the implementation class.
346 The rules of C++ result in the method on the implementation class being called
347 in preference to the generic method.
348
349 The new method may entirely replace the behavior of the generic method, or it
350 may augment its behavior by calling the generic method in the midst of its own
351 implementation.
352
353 ```cpp
354 CHIP_ERROR ConfigurationManagerImpl::_GetDeviceId(uint64_t & deviceId)
355 {
356     using GenericImpl = GenericConfigurationManagerImpl<ConfigurationManagerImpl>;
357
358     /* Call the generic implementation to get the device id. */
359     uint64_t deviceId = GenericImpl::_GetDeviceId(deviceId);
360
361     /* Special case the situation where the device id is not known. */
362     if (deviceId == kNodeIdNotSpecified) {
363         deviceId = PLATFORM_DEFAULT_DEVICE_ID;
364     }
365
366     return deviceId;
367 }
368 ```
369
370 ### Multiple Inheritance and Subclassing of Generic Implementations
371
372 Concrete implementation classes are free to inherit from multiple generic base
373 classes. This pattern is particularly useful when the overall functionality of a
374 component can be naturally split into independent slices (e.g. methods that
375 support WiFi and methods that support Thread). Each such slice can then be
376 implemented by a distinct base class which ends up being composed together with
377 other base classes in the final implementation.
378
379 ```cpp
380 /** Concrete implementation of ConfigurationManager component for specific platform
381  */
382 class ConfigurationManagerImpl final
383     : public ConfigurationManager,
384       public GenericWiFiConfigurationManagerImpl<ConfigurationManagerImpl>, /* <-- WiFi features */
385       public GenericThreadConfigurationManagerImpl<ConfigurationManagerImpl> /* <-- Thread features */
386 {
387     ...
388 };
389 ```
390
391 A generic implementation base class may also inherit from other generic base
392 classes. This is useful for “specializing” a generic implementation for a
393 certain sub-range of use cases (e.g. for a particular OS type).
394
395 ```cpp
396 /** Generic base class for use in implementing PlatformManager features
397  *  on all platforms.
398  */
399 template<class ImplClass>
400 class GenericPlatformManagerImpl
401 {
402     ...
403 };
404
405 /** Generic base class for use in implementing PlatformManager features
406  *  on FreeRTOS platforms.
407  */
408 template<class ImplClass>
409 class GenericPlatformManagerImpl_FreeRTOS
410     : public GenericPlatformManagerImpl<ImplClass>
411 {
412     ...
413 };
414 ```
415
416 ### Static Virtualization of Generic Implementation Behavior
417
418 When creating generic implementation base classes, developers are encouraged to
419 use a pattern of static virtualization to delegate operations to the concrete
420 implementation class in cases where the operation may or must be implemented in
421 a platform-specific way.
422
423 For example, consider a generic implementation of the ConfigurationManager
424 component where value accessor methods such as `GetDeviceId()` operate by
425 retrieving values from an underlying key-value store. The particulars of how the
426 key-value store is implemented will likely vary by platform. To allow for this,
427 the generic implementation class is structured to delegate the operation of
428 retrieving the value for a key to a method on the concrete implementation class.
429
430 Following the Curiously Recurring Template Pattern, delegation is accomplished
431 by casting the `this` pointer to the implementation class and calling a method
432 with the appropriate signature. An inline helper function named `Impl()` helps
433 to make the code concise.
434
435 ```cpp
436 template<class ImplClass>
437 class GenericConfigurationManagerImpl
438 {
439 protected:
440     CHIP_ERROR _GetDeviceId(uint64_t & deviceId);
441     ...
442 private:
443     ImplClass * Impl() { return static_cast<ImplClass*>(this); }
444 };
445
446 class ConfigurationManagerImpl final
447     : public ConfigurationManager,
448       public GenericConfigurationManagerImpl<ConfigurationManagerImpl>
449 {
450     friend GenericConfigurationManagerImpl<ConfigurationManagerImpl>;
451 private:
452     CHIP_ERROR ReadConfigValue(const char * key, uint64_t & value);
453 };
454
455 template<class ImplClass>
456 CHIP_ERROR GenericConfigurationManagerImpl<ImplClass>::_GetDeviceId(uint64_t & deviceId)
457 {
458     /* delegate to the implementation class to read the 'device-id' config value */
459     return Impl()->ReadConfigValue(“device-id”, deviceId);
460 }
461
462 CHIP_ERROR ConfigurationManagerImpl::ReadConfigValue(const char * key, uint64_t & value)
463 {
464     /* read value from platform-specific key-value store */
465     ...
466 }
467 ```
468
469 In the above example, the delegated method is conceptually ‘pure virtual’ in
470 that the concrete implementation class must supply a version of the method,
471 otherwise compilation will fail. In other situations, a similar pattern can be
472 used to allow an implementation to override a default behavior supplied by the
473 base class on an as-needed basis.
474
475 Again, delegation happens by casting the `this` pointer and calling an
476 appropriate method. In this case, however, the generic base class provides a
477 default implementation of the target method which will be used unless the
478 subclass overrides it.
479
480 ```cpp
481 template<class ImplClass>
482 class GenericPlatformManagerImpl
483 {
484 protected:
485     void _DispatchEvent(const CHIPDeviceEvent * event);
486     void DispatchEventToApplication(const CHIPDeviceEvent * event);
487     ...
488 private:
489     ImplClass * Impl() { return static_cast<ImplClass*>(this); }
490 };
491
492 template<class ImplClass>
493 void GenericPlatformManagerImpl<ImplClass>::_DispatchEvent(const CHIPDeviceEvent * event)
494 {
495     ...
496     /* Delegate work to method that can be overridden by implementation class */
497     Impl()->DispatchEventToApplication(event);
498     ...
499 }
500
501 template<class ImplClass>
502 void GenericPlatformManagerImpl<ImplClass>::DispatchEventToApplication(const CHIPDeviceEvent * event)
503 {
504     /* provide default implementation of DispatchEventToApplication() */
505     ...
506 }
507 ```
508
509 ### .cpp Files and Explicit Template Instantiation
510
511 The rules for C++ templates require that the compiler 'see' the full definition
512 of a class template at the moment of its instantiation. (Instantiation in this
513 context means the moment at which the compiler is forced to generate an actual
514 class from the recipe provided by the template). Typically this requires placing
515 the entire definition of the class template, including all its methods, into a
516 header file, which must then be included before the moment of instantiation.
517
518 To provide a separation between the definition of a class template and the
519 definitions of its members, the CHIP Device Layer places all non-inlined
520 template member definitions into a separate file. This file has the same base
521 name as the template header file, but with a `.cpp` suffix. This pattern reduces
522 clutter in the header file and makes it possible to include the non-inlined
523 member definitions only when they are needed (more on this below).
524
525 ```cpp
526 /* contents of GenericConfigurationManagerImpl.h */
527
528 template<class ImplClass>
529 class GenericConfigurationManagerImpl
530 {
531 protected:
532     CHIP_ERROR _GetDeviceId(uint64_t & deviceId);
533     ...
534 };
535 ```
536
537 ```cpp
538 /* contents of GenericConfigurationManagerImpl.cpp */
539
540 template<class ImplClass>
541 CHIP_ERROR GenericConfigurationManagerImpl<ImplClass>::_GetDeviceId(uint64_t & deviceId)
542 {
543     ...
544 }
545 ```
546
547 It is often the case that the C++ compiler is forced to instantiate a class
548 template multiple times, once for each .cpp file it compiles. This can add
549 significant overhead to the compilation process. To avoid this, the Device Layer
550 uses the C++11 technique of
551 [explicit template instantiation](https://en.cppreference.com/w/cpp/language/class_template#Explicit_instantiation)
552 to direct the compiler to instantiate the template only once. This is
553 accomplished in two steps: first, all header files that makes use of a class
554 template includes an `extern template class` declaration before the point at
555 which the template class is used. This tells the compiler to _not_ instantiate
556 the template in that context.
557
558 ```cpp
559 /* contents of ConfigurationManagerImpl.h */
560
561 #include <CHIP/DeviceLayer/internal/GenericConfigurationManagerImpl.h>
562
563 // Instruct the compiler to instantiate the GenericConfigurationManagerImpl<ConfigurationManagerImpl>
564 // class only when explicitly asked to do so.
565 extern template class GenericConfigurationManagerImpl<ConfigurationManagerImpl>;
566
567 ...
568 ```
569
570 Then, within a corresponding .cpp file, the template's .cpp file is included and
571 a `template class` definition is used to force an explicit instantiation of the
572 template.
573
574 ```cpp
575 /* contents of ConfigurationManagerImpl.cpp */
576
577 #include <CHIP/DeviceLayer/internal/GenericConfigurationManagerImpl.cpp>
578
579 // Fully instantiate the GenericConfigurationManagerImpl<ConfigurationManagerImpl> class.
580 template class GenericConfigurationManagerImpl<ConfigurationManagerImpl>;
581
582 ...
583 ```
584
585 The result is that the template's non-inlined members are only parsed and
586 instantiated once, during compilation of the referencing .cpp file, avoiding
587 redundant processing in other contexts.