2 = Project Connected Home over IP Software
3 :listing-caption: *Listing*
14 == Best Practices, Coding Conventions, and Style
21 *Status:* [red]*Approved* / [red]*Active*
25 == Typographic and Syntactic Conventions
27 The following syntactic conventions are used throughout this document:
31 is used to indicate a mandatory rule or guideline that must be adhered
32 to without exception to claim compliance with this specification.
36 is used to indicate a rule or guideline that serves as a strong
37 preference to suggested practice and is to be followed in the absence of
38 a compelling reason to do otherwise.
42 is used to indicate a rule or guideline that serves as a reference to
47 There are likely as many unique combinations of software engineering and
48 development standards, conventions, and practices as there organizations
49 that do such work. This document pulls together those that Project
50 Connected Home over IP believes best for our organization, its efforts,
51 and products that consume those efforts, with a particular emphasis on
52 embedded systems with C or C{plusplus} language development and runtime
55 This document and requirements should be considered canonical for all
56 Project Connected Home over IP shared infrastructure software, including
57 both RTOS-based and non-RTOS-based projects on both tightly- and
58 loosely-constrained system platforms.
60 The document is broadly categorized at the highest level into:
62 * Best Practices and Conventions
65 And, within conventions, further sub-categorized into those that apply
71 system platforms. Applicability to tightly-constrained systems also
72 generally applies to shared infrastructure software that is used on both
73 tightly- and loosely-constrained systems.
75 link:#id.jzphr1iiku89[Figure 1 below] attempts to illustrate both
76 qualitative and quantitative applicability of these guidelines to
77 Project Connected Home over IP software.
79 Generally, product-specific applications have the greatest flexibility
80 and latitude in applying these guidelines to their software. Whereas,
81 shared infrastructure bears the least flexibility and bears the greatest
82 adherence to these guidelines.
84 image:CODING_STYLE_GUIDE-figure1.png[Figure 1. Graphical summary of the
85 qualitative and quantitative applicability to Project CHIP software.]
90 *Figure 1.* Graphical summary of the qualitative and quantitative
91 applicability to Project CHIP software.
97 Project CHIP embedded software development adopts the minimum C and C{plusplus}
98 standards listed in Table 2.1 below.
100 [[t.4d8bfeef046f29261fc72f1a903d6d10a909957a]][[t.2]]
102 [cols=3,options="header"]
104 |Language |Minimum Standard |Aliases
106 |C|ISO9899:1999|ISO C99, C99
107 |C{plusplus}|ISO14882:2014|ISO C{plusplus}14, C{plusplus}14
110 *Table 2.1.* C and C{plusplus} language minimum standards adopted by Project CHIP
113 Product-specific software may elect to use later standards to the extent
114 their software is not broadly shared inside or outside Project CHIP.
118 Project CHIP embedded software development uses and enforces the
119 ISO9899:1999 (aka ISO C99, C99) C language standard as the minimum.
121 Wherever possible, particularly in non-product-specific,
122 shared-infrastructure software, toolchain-specific (e.g GCC/GNU)
123 extensions or the use of later standards shall be avoided or shall be
124 leveraged through toolchain-compatibility preprocessor macros.
126 ==== Motivation and Rationale
128 At the time of this writing, the C99 standard has been out for over 20
129 years. Project CHIP and both the new and contributed source code that
130 comprise it have only existed for the last seven to eight of those
133 This is beyond more than adequate time for this standard to be pervasive
134 throughout any toolchain vendor’s C compiler and saves team members from
135 worrying about ISO9899:1990 (aka ISO C90, C90) portability issues that
136 have long-since been solved by C99.
140 Project CHIP embedded software development uses the ISO14882:2014 (aka
141 ISO C{plusplus}14) language standard as a baseline for source code
142 compatibility. Conformance with other standards, for example, ISO14882:1998
143 (aka ISO C{plusplus}98), may be additionally required in cases where wider
144 portability is necessary, but in all cases, ISO C{plusplus}14 is the baseline
147 Wherever possible, particularly in non-product-specific,
148 shared-infrastructure software, toolchain-specific (e.g GCC/GNU)
149 extensions or the use of later standards shall be avoided or shall be
150 leveraged through toolchain-compatibility preprocessor macros.
152 ==== Motivation and Rationale
154 CHIP strives to use the latest C++ functionality as long as existing compilers
155 support such standards.
157 C{plusplus}14 is considered pervasive enough to be used. As compilers start
158 supporting standards such as C{plusplus}17, C{plusplus}20 and beyond,
159 CHIP may follow suit.
161 == Conventions and Best Practices
165 The following sections summarize those best practices that are
166 independent of particular nuances of either the C or C{plusplus} languages.
170 The most important convention and practice in the Project CHIP embedded
171 software is "_When in Rome..._", per the quote below.
175 If you should be in Rome, live in the Roman manner; if you should be
176 elsewhere, live as they do there.
179 ===== Motivation and Rationale
181 At this stage in the work group’s and the team’s life cycle, it is rare
182 the project or subsystem that is entirely new and built from scratch.
183 More often than not, development will involve extending, enhancing, and
184 fixing existing code in existing projects.
186 When in this situation, it is mandatory you observe how things are done
187 in this context and do the best that you can to follow the prevailing
188 conventions present. Not doing so can lead to readability and
189 maintenance problems down the line and will likely earn you the
190 disapprobation of the code’s _owner_ or other team members.
192 Your extensions or fixes to existing code should be *indistinguishable*,
193 stylistically, from the original code such that the only way to
194 ascertain ownership and responsibility is to use the source code control
195 system’s change attribution (aka _blame_) feature.
197 If you find the conventions so foreign or otherwise confusing, it may be
198 best to let whoever owns the file make the necessary changes or seek the
199 counsel of others in the group to find out what the right thing to do
200 is. Never just start changing code wholesale for personal reasons
201 without consulting others first.
203 ==== Language-independent
205 ===== Commenting Out or Disabling Code
207 Unused code shall not be disabled by commenting it out with C- or
208 C{plusplus}-style comments or with preprocessor `#if 0 ... #endif` semantics.
210 ====== Motivation and Rationale
212 Code should either be actively maintained and "in" the source base for a
213 purpose or removed entirely. Code that is disabled in this way is
214 generally sloppy and does not convey a sense of certainty and direction
217 Anyone who is interested in the history of a particular source code file
218 should use the source code control system to browse it.
220 Code that is debug- or test-only should be moved to a conditionally
221 compiled test source file or conditionalized with an appropriate
222 `WITH_DEBUG`, `WANT_DEBUG`, `WITH_TESTS`, `WANT_TESTS`, or some similar such
223 preprocessor mnemonic that can be asserted from the build system.
225 ===== Use C _stdint.h_ or C{plusplus} _cstdint_ for Plain Old Data Types
227 Standard, scalar data types defined in _stdint.h_ \(C) or _cstdint_ (C{plusplus})
228 should be used for basic signed and unsigned integer types, especially
229 when size and serialization to non-volatile storage or across a network
232 Examples of these are: `uint8_t`, `int8_t`, etc.
234 ====== Motivation and Rationale
236 These types have been effectively standardized since C99 and should be
237 available on every platform and provide more neutral portability than
238 OS-specific types such as `u8`, `UInt8`, etc. Moreover, because these are
239 pervasive, you do not need to spend any time and energy as a developer
240 and engineer creating more such types on your own—the compiler vendors
241 have already done the hard work for you.
243 Additionally, using traditional scalar types such as `char`, `int`, `short`, or
244 `long` have portability issues where data width is concerned because these
245 types are either signed- or sized-differently on different processor
246 architectures and and ABIs for those architectures. For example, a char is signed
247 on some architectures and unsigned on others and a long is 32-bits on some
248 architectures and 64-bits on others.
250 ==== Language-dependent
254 ====== Avoid `using namespace` Statements in Headers
256 By doing this, you are effectively forcing every other module that
257 includes the header to also be using the namespace. This causes
258 namespace pollution and generally defeats the purposes of namespaces.
259 Fully-qualified symbols should be used instead.
261 === Tightly-constrained Systems and Shared Infrastructure
263 Applicability to tightly-constrained systems also generally applies to
264 shared infrastructure software that is used on both tightly- and
265 loosely-constrained systems.
267 ==== Avoid Heap-based Resource Allocation
269 Heap-based resource allocation should be avoided.
271 ===== Motivation and Rationale
273 As emphasized throughout this document, the software produced by Project
274 CHIP is consumed both inside and outside Project CHIP, across a variety
275 of platforms. The capabilities of these platforms are broad, spanning
276 soft real-time, deeply-embedded systems based on based on RTOSes that
277 may cover life safety and/or physical security applications to richer,
278 softly-embedded systems based on non-RTOS platforms such as Darwin or
279 Linux. While the latter are apt to have fully-functional heaps, the
280 former explicitly may not.
282 Consequently, when planning new or extending existing Project CHIP code,
283 consider the platforms to which the code is targeted. If the platforms
284 include those deeply-embedded platforms absent functioning heaps, then
285 heap-based resource allocation is absolutely forbidden. If not,
286 consideration should be made to the cost / benefit trade-offs of
287 heap-based allocation and, if possible, it should be avoided using one
288 of the recommended techniques below.
292 In either case, recommended resource allocation alternatives are:
294 * In Place Allocation and Initialization
295 * Pool-based Allocators
296 * Platform-defined and -assigned Allocators
298 The interfaces in https://github.com/project-chip/connectedhomeip/blob/master/src/lib/support/CHIPMem.h[_src/lib/support/CHIPMem.h_] provide support for
299 the latter two alternatives.
301 ====== Use In Place Allocation and Initialization
303 Regardless of whether the source code and runtime are C or C{plusplus}, the
304 first step is creating storage for the object being allocated and
305 initialized. For simple
306 https://en.wikipedia.org/wiki/Passive_data_structure[plain-old-data
307 (POD)] data structures, this can be done by just allocating the
308 structure at an appropriate scope. Alternatively, _raw_ storage can be
309 allocated and then cast. However, great care must be taken with the
310 latter approach to ensure that natural machine alignments and language
311 strict-aliasing rules are observed. With the simple data structure
312 declaration, the compiler does this on your behalf. With the raw
313 approach, you must do this.
315 Once the storage has been allocated, then use symmetric initializers and
316 deinitializers such as those, for example, for `pthread_attr_t`. An
317 example is shown in the listing below.
319 [source,C,caption='',title='{listing-caption} *{counter:refnum}*. Using in place allocation and initialization in C or C{plusplus}.']
326 // Preprocessor Definitions
328 // Allocate the structure using "raw" storage.
330 #if defined(__cplusplus) && (__cplusplus >= 201103L)
331 #include <type_traits>
333 #define chipDEFINE_ALIGNED_VAR(name, size, align_type) \
334 typename std::aligned_storage<size, alignof(align_type)>::type name;
337 #define chipDEFINE_ALIGNED_VAR(name, size, align_type) \
338 align_type name[(((size) + (sizeof (align_type) - 1)) / sizeof (align_type))]
340 #endif // defined(__cplusplus) && (__cplusplus >= 201103L)
342 // Forward Declarations
344 extern void * foobar_entry(void *aArgument);
348 #if USE_STRUCT_STORAGE
349 // Allocate the structure directly.
350 static pthread_attr_t sThreadAttributes;
352 #elif USE_RAW_STORAGE
353 static chipDEFINE_ALIGNED_VAR(sThreadAttributes, sizeof (pthread_attr_t), uint64_t);
355 #endif // USE_STRUCT_STORAGE
362 pthread_attr_t * attrs = (pthread_attr_t *)&sThreadAttributes;
364 // Now "construct" or initialize the storage.
365 retval = pthread_attr_init(attrs);
369 retval = pthread_create(&thread, attrs, foobar_entry, NULL);
373 status = pthread_join(thread, NULL);
380 status = pthread_attr_destroy(attrs);
393 For non-scalar types and objects such as C{plusplus} classes, this gets slightly
394 trickier since C{plusplus} constructors and destructors must be accounted for
395 and invoked. Fortunately, C{plusplus} has placement new which handles this.
396 The listing below modifies the listing above using C{plusplus} placement new
397 to ensure the class is properly constructed before initialization and
398 destructed after deinitialization.
400 [source,C++,caption='',title='{listing-caption} *{counter:refnum}*. Using C{plusplus} placement new for in place allocation and initialization.']
409 // Preprocessor Definitions
411 // Allocate the structure using "raw" storage.
413 #if defined(__cplusplus) && (__cplusplus >= 201103L)
414 #include <type_traits>
416 #define chipDEFINE_ALIGNED_VAR(name, size, align_type) \
417 typename std::aligned_storage<size, alignof(align_type)>::type name;
420 #define chipDEFINE_ALIGNED_VAR(name, size, align_type) \
421 align_type name[(((size) + (sizeof (align_type) - 1)) / sizeof (align_type))]
423 #endif // defined(__cplusplus) && (__cplusplus >= 201103L)
427 class ThreadAttributes
430 ThreadAttributes(void) {};
431 ~ThreadAttributes(void) {};
433 operator pthread_attr_t *(void) { return &mAttributes; }
436 pthread_attr_t mAttributes;
439 // Forward Declarations
441 extern void * foobar_entry(void *aArgument);
445 static chipDEFINE_ALIGNED_VAR(sThreadAttributes, sizeof (ThreadAttributes), uint64_t);
452 ThreadAttributes * ta;
453 pthread_attr_t * attrs;
455 ta = new (&sThreadAttributes) ThreadAttributes;
459 attrs = static_cast<pthread_attr_t *>(*ta);
461 // Now "construct" or initialize the storage.
462 retval = pthread_attr_init(attrs);
466 retval = pthread_create(&thread, attrs, foobar_entry, NULL);
470 status = pthread_join(thread, NULL);
477 status = pthread_attr_destroy(attrs);
486 ta->~ThreadAttributes();
493 ====== Use Pool-based Allocators
495 In place allocation allows the successful allocation, initialization,
496 deinitialization, and deallocation of a single object allocated from
497 preallocated storage. However, if the desire exists for a fixed,
498 configurable pool of objects where 0 to `n` of such objects can be
499 allocated at any one time, a pool allocator for that specific object
500 type must be created.
502 As shown in the listing below, a pool allocator for a `Foo` class of
503 `CHIP_FOO_COUNT` objects is effected, assuming the existence of another
504 helper class, StaticAllocatorBitmap, which uses a bitmap to track the
505 storage of objects from a static array of storage.
507 [source,C++,caption='',title='{listing-caption} *{counter:refnum}*. Using pool-based allocators.']
512 // Preprocessor Definitions
514 // Allocate the structure using "raw" storage.
516 #if defined(__cplusplus) && (__cplusplus >= 201103L)
517 #include <type_traits>
519 #define chipDEFINE_ALIGNED_VAR(name, size, align_type) \
520 typename std::aligned_storage<size, alignof(align_type)>::type name;
523 #define chipDEFINE_ALIGNED_VAR(name, size, align_type) \
524 align_type name[(((size) + (sizeof (align_type) - 1)) / sizeof (align_type))]
526 #endif // defined(__cplusplus) && (__cplusplus >= 201103L)
534 Foo(const Foo &inFoo);
540 static chipDEFINE_ALIGNED_VAR(sFooAllocatorBuffer, sizeof (StaticAllocatorBitmap), uint32_t);
541 static StaticAllocatorBitmap *sFooAllocator;
543 static void CreateFooAllocator(void *inStorage,
544 const StaticAllocatorBitmap::size_type &inStorageSize,
545 const StaticAllocatorBitmap::size_type &inElementCount,
546 StaticAllocatorBitmap::InitializeFunction inInitialize,
547 StaticAllocatorBitmap::DestroyFunction inDestroy)
549 sFooAllocator = new (sFooAllocatorBuffer)
550 StaticAllocatorBitmap(inStorage,
557 static StaticAllocatorBitmap &GetFooAllocator(void)
559 return *sFooAllocator;
562 static void *FooInitialize(AllocatorBase &inAllocator, void *inObject)
564 memset(inObject, 0, sizeof(Foo));
569 static void FooDestroy(AllocatorBase &inAllocator, void *inObject)
576 static const size_t sFooCount = CHIP_FOO_COUNT;
577 static chipAllocatorStaticBitmapStorageDefine(sFooStorage, Foo, sFooCount, uint32_t, sizeof (void *));
580 CreateFooAllocator(sFooStorage,
581 sizeof (sFooStorage),
589 Foo * FooAllocate(void)
593 foo = static_cast<Foo *>(GetFooAllocator().allocate());
598 void FooDeallocate(Foo *inFoo)
600 GetFooAllocator().deallocate(inFoo);
604 ====== Use Platform-defined and -assigned Allocators
606 This is a variation on both in place allocation and pool-based
607 allocation in that it completely delegates resource allocation to the
608 system integrator and the platform on which the particular software
609 subsystem is running.
611 The advantage of this approach is that it allows the platform to decide
612 how resource allocation will be handled and allows the package to scale
613 independently of platform resource allocation.
615 The package may define default implementations for a few types of
616 platform allocation strategies, such as heap-based allocators and
617 pool-based allocators.
619 There are a range of granularities for achieving this type of
620 delegation, depending on the desired size of the API surface, as shown
621 in the listings below.
623 [source,C++,caption='',title='{listing-caption} *{counter:refnum}*. Using a common allocator method pattern with unique allocators per object, accessed from a unique singleton access per allocator.']
626 chipPlatformInitFooAllocator();
627 chipPlatformInitBarAllocator();
629 foo = chipPlatformGetFooAllocator().allocate();
631 chipPlatformGetFooAllocator().deallocate(foo);
633 bar = chipPlatformGetBarAllocator().allocate();
635 chipPlatformGetBarAllocator().deallocate(bar);
638 [source,C++,caption='',title='{listing-caption} *{counter:refnum}*. Using a common allocator method pattern with unique allocators per object, accessed from a common singleton access with type per allocator.']
640 chipPlatformInitAllocator(CHIP_FOO_T);
641 chipPlatformInitAllocator(CHIP_BAR_T);
643 foo = chipPlatformGetAllocator(CHIP_FOO_T).allocate();
645 chipPlatformGetAllocator(CHIP_FOO_T).deallocate(foo);
647 bar = chipPlatformGetAllocator(CHIP_BAR_T).allocate();
649 chipPlatformGetAllocator(CHIP_BAR_T).deallocate(bar);
652 [source,C,caption='',title='{listing-caption} *{counter:refnum}*. Using unique allocators per object.']
654 chipPlatformInitFooAllocator();
655 chipPlatformInitBarAllocator();
657 foo = chipPlatformFooAllocate();
659 chipPlatformFooDeallocate(foo);
661 bar = chipPlatformBarAllocate();
663 chipPlatformBarDeallocate(bar);
666 [source,C,caption='',title='{listing-caption} *{counter:refnum}*. Using a common allocator pattern with unique allocators per object, accessed from a common interface with type per allocator.']
669 chipPlatformInitAllocator(CHIP_FOO_T);
670 chipPlatformInitAllocator(CHIP_BAR_T);
672 foo = chipPlatformAllocate(CHIP_FOO_T);
674 chipPlatformDeallocate(CHIP_FOO_T, foo);
676 bar = chipPlatformAllocate(CHIP_BAR_T);
678 chipPlatformBarDeallocate(CHIP_BAR_T, bar);
683 == Recommended Reading
685 While the following references and reading are not part of the formal
686 best practices, coding conventions, and style cannon, they are
687 informative and useful guides for improving the style and quality of the
690 . Jet Propulsion Laboratory.
691 http://lars-lab.jpl.nasa.gov/JPL_Coding_Standard_C.pdf[JPL
692 Institutional Coding Standard for the C Programming Language.] Version
694 . Jet Propulsion Laboratory.
695 http://pixelscommander.com/wp-content/uploads/2014/12/P10.pdf[The
696 Power of Ten – Rules for Developing Safety Critical Code]. December
698 . Meyers, Scott. Effective C{plusplus}: 55 Specific Ways to Improve Your
699 Programs and Designs. Third Edition. 2005.
700 . Meyers, Scott. More Effective C{plusplus}: 35 New Ways to Improve Your
701 Programs and Designs. 1996.
702 . Meyers. Scott. https://www.artima.com/shop/effective_cpp_in_an_embedded_environment[Effective C{plusplus} in an Embedded Environment]. 2015.
703 . Motor Industry Software Reliability Association. Guidelines for the
704 Use of the C Language in Critical Systems. March 2013.
705 . Motor Industry Software Reliability Association. Guidelines for the
706 Use of the C{plusplus} Language in Critical Systems. June 2008.
710 [cols="^1,^1,<2,<3",options="header"]
712 |Revision |Date |Modified By |Description
713 |5 |2020-09-22 |Grant Erickson |Added Tightly-constrained Systems and Shared Infrastructure > Avoid Heap-based Resource Allocation
714 |4 |2020-09-15 |Grant Erickson |Added Common > Language-dependent > Avoid `using namespace` Statements in Headers
715 |3 |2020-09-01 |Grant Erickson |Added Common > Language-independent > Use C _stdint.h_ or C{plusplus} _cstdint_ for Plain Old Data Types
716 |2 |2020-07-09 |Grant Erickson |Added Common > Language-independent > Commenting Out or Disabling Code
717 |1 |2020-07-08 |Grant Erickson |Initial revision.
721 _Project Connect Home over IP Public Information_