Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / docs / style / coding / CODING_STYLE_GUIDE.adoc
1 [.text-center]
2 = Project Connected Home over IP Software
3 :listing-caption: *Listing*
4 :toc: macro
5 :toclevels: 7
6 :sectnumlevels: 7
7 :sectanchors:
8 :sectlinks:
9
10 :plusplus: ++
11
12 :sectnums!:
13
14 == Best Practices, Coding Conventions, and Style
15
16 [.text-center]
17 _Revision 5_ +
18 _2020-09-22_
19
20 [.text-center]
21 *Status:* [red]*Approved* / [red]*Active*
22
23 toc::[]
24
25 == Typographic and Syntactic Conventions
26
27 The following syntactic conventions are used throughout this document:
28
29 _shall_::
30
31 is used to indicate a mandatory rule or guideline that must be adhered
32 to without exception to claim compliance with this specification.
33
34 _should_::
35
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.
39
40 _may_::
41
42 is used to indicate a rule or guideline that serves as a reference to
43 suggested practice.
44
45 == Introduction
46
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
53 environments.
54
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.
59
60 The document is broadly categorized at the highest level into:
61
62 * Best Practices and Conventions
63 * Format and Style
64
65 And, within conventions, further sub-categorized into those that apply
66 to:
67
68 * Tightly-constrained
69 * Loosely-constrained
70
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.
74
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.
78
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.
83
84 image:CODING_STYLE_GUIDE-figure1.png[Figure 1. Graphical summary of the
85 qualitative and quantitative applicability to Project CHIP software.]
86
87 [[id.jzphr1iiku89]]
88
89 [.text-center]
90 *Figure 1.* Graphical summary of the qualitative and quantitative
91 applicability to Project CHIP software.
92
93 :sectnums:
94
95 == Standards
96
97 Project CHIP embedded software development adopts the minimum C and C{plusplus}
98 standards listed in Table 2.1 below.
99
100 [[t.4d8bfeef046f29261fc72f1a903d6d10a909957a]][[t.2]]
101
102 [cols=3,options="header"]
103 |===
104 |Language |Minimum Standard |Aliases
105
106 |C|ISO9899:1999|ISO C99, C99
107 |C{plusplus}|ISO14882:2014|ISO C{plusplus}14, C{plusplus}14
108 |===
109 [.text-center]
110 *Table 2.1.* C and C{plusplus} language minimum standards adopted by Project CHIP
111 software.
112
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.
115
116 === C
117
118 Project CHIP embedded software development uses and enforces the
119 ISO9899:1999 (aka ISO C99, C99) C language standard as the minimum.
120
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.
125
126 ==== Motivation and Rationale
127
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
131 20-plus years.
132
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.
137
138 === C{plusplus}
139
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
145 requirement.
146
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.
151
152 ==== Motivation and Rationale
153
154 CHIP strives to use the latest C++ functionality as long as existing compilers
155 support such standards.
156
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.
160
161 == Conventions and Best Practices
162
163 === Common
164
165 The following sections summarize those best practices that are
166 independent of particular nuances of either the C or C{plusplus} languages.
167
168 ==== When in Rome
169
170 The most important convention and practice in the Project CHIP embedded
171 software is "_When in Rome..._", per the quote below.
172
173 [quote, St. Ambrose]
174 ____
175 If you should be in Rome, live in the Roman manner; if you should be
176 elsewhere, live as they do there.
177 ____
178
179 ===== Motivation and Rationale
180
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.
185
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.
191
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.
196
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.
202
203 ==== Language-independent
204
205 ===== Commenting Out or Disabling Code
206
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.
209
210 ====== Motivation and Rationale
211
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
215 in the code.
216
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.
219
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.
224
225 ===== Use C _stdint.h_ or C{plusplus} _cstdint_ for Plain Old Data Types
226
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
230 is concerned.
231
232 Examples of these are: `uint8_t`, `int8_t`, etc.
233
234 ====== Motivation and Rationale
235
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.
242
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.
249
250 ==== Language-dependent
251
252 =====   C{plusplus}
253
254 ======  Avoid `using namespace` Statements in Headers
255
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.
260
261 === Tightly-constrained Systems and Shared Infrastructure
262
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.
266
267 ==== Avoid Heap-based Resource Allocation
268
269 Heap-based resource allocation should be avoided.
270
271 ===== Motivation and Rationale
272
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.
281
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.
289
290 ===== Alternatives
291
292 In either case, recommended resource allocation alternatives are:
293
294 * In Place Allocation and Initialization
295 * Pool-based Allocators
296 * Platform-defined and -assigned Allocators
297
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.
300
301 ====== Use In Place Allocation and Initialization
302
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.
314
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.
318
319 [source,C,caption='',title='{listing-caption} *{counter:refnum}*. Using in place allocation and initialization in C or C{plusplus}.']
320 ----
321 #include <pthread.h>
322 #include <stdint.h>
323
324 ...
325
326 // Preprocessor Definitions
327
328 // Allocate the structure using "raw" storage.
329
330 #if defined(__cplusplus) && (__cplusplus >= 201103L)
331 #include <type_traits>
332
333 #define chipDEFINE_ALIGNED_VAR(name, size, align_type) \
334         typename std::aligned_storage<size, alignof(align_type)>::type name;
335
336 #else
337 #define chipDEFINE_ALIGNED_VAR(name, size, align_type) \
338     align_type name[(((size) + (sizeof (align_type) - 1)) / sizeof (align_type))]
339
340 #endif // defined(__cplusplus) && (__cplusplus >= 201103L)
341
342 // Forward Declarations
343
344 extern void * foobar_entry(void *aArgument);
345
346 // Global Variables
347
348 #if USE_STRUCT_STORAGE
349 // Allocate the structure directly.
350 static pthread_attr_t sThreadAttributes;
351
352 #elif USE_RAW_STORAGE
353 static chipDEFINE_ALIGNED_VAR(sThreadAttributes, sizeof (pthread_attr_t), uint64_t);
354
355 #endif // USE_STRUCT_STORAGE
356
357 int foobar(void)
358 {
359     int              retval;
360     int              status;
361     pthread_t        thread;
362     pthread_attr_t * attrs = (pthread_attr_t *)&sThreadAttributes;
363
364     // Now "construct" or initialize the storage.
365     retval = pthread_attr_init(attrs);
366
367     if (retval == 0)
368     {
369         retval = pthread_create(&thread, attrs, foobar_entry, NULL);
370
371         if (retval == 0)
372         {
373             status = pthread_join(thread, NULL);
374
375             if (status != 0)
376             {
377                 retval = status;
378             }
379
380             status = pthread_attr_destroy(attrs);
381
382             if (status != 0)
383             {
384                 retval = status;
385             }
386         }
387     }
388
389     return (retval);
390 }
391 ----
392
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.
399
400 [source,C++,caption='',title='{listing-caption} *{counter:refnum}*. Using C{plusplus} placement new for in place allocation and initialization.']
401 ----
402 #include <new>
403
404 #include <pthread.h>
405 #include <stdint.h>
406
407 ...
408
409 // Preprocessor Definitions
410
411 // Allocate the structure using "raw" storage.
412
413 #if defined(__cplusplus) && (__cplusplus >= 201103L)
414 #include <type_traits>
415
416 #define chipDEFINE_ALIGNED_VAR(name, size, align_type) \
417         typename std::aligned_storage<size, alignof(align_type)>::type name;
418
419 #else
420 #define chipDEFINE_ALIGNED_VAR(name, size, align_type) \
421     align_type name[(((size) + (sizeof (align_type) - 1)) / sizeof (align_type))]
422
423 #endif // defined(__cplusplus) && (__cplusplus >= 201103L)
424
425 // Type Declarations
426
427 class ThreadAttributes
428 {
429 public:
430     ThreadAttributes(void) {};
431     ~ThreadAttributes(void) {};
432
433     operator pthread_attr_t *(void) { return &mAttributes; }
434
435 private:
436     pthread_attr_t mAttributes;
437 };
438
439 // Forward Declarations
440
441 extern void * foobar_entry(void *aArgument);
442
443 // Global Variables
444
445 static chipDEFINE_ALIGNED_VAR(sThreadAttributes, sizeof (ThreadAttributes), uint64_t);
446
447 int foobar(void)
448 {
449     int                retval = -1;
450     int                status;
451     pthread_t          thread;
452     ThreadAttributes * ta;
453     pthread_attr_t *   attrs;
454
455     ta = new (&sThreadAttributes) ThreadAttributes;
456
457     if (ta != NULL)
458     {
459         attrs = static_cast<pthread_attr_t *>(*ta);
460
461         // Now "construct" or initialize the storage.
462         retval = pthread_attr_init(attrs);
463
464         if (retval == 0)
465         {
466             retval = pthread_create(&thread, attrs, foobar_entry, NULL);
467
468             if (retval == 0)
469             {
470                 status = pthread_join(thread, NULL);
471
472                 if (status != 0)
473                 {
474                     retval = status;
475                 }
476
477                 status = pthread_attr_destroy(attrs);
478
479                 if (status != 0)
480                 {
481                     retval = status;
482                 }
483             }
484         }
485
486         ta->~ThreadAttributes();
487     }
488
489     return retval;
490 }
491 ----
492
493 ====== Use Pool-based Allocators
494
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.
501
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.
506
507 [source,C++,caption='',title='{listing-caption} *{counter:refnum}*. Using pool-based allocators.']
508 ----
509
510 #include <stdint.h>
511
512 // Preprocessor Definitions
513
514 // Allocate the structure using "raw" storage.
515
516 #if defined(__cplusplus) && (__cplusplus >= 201103L)
517 #include <type_traits>
518
519 #define chipDEFINE_ALIGNED_VAR(name, size, align_type) \
520         typename std::aligned_storage<size, alignof(align_type)>::type name;
521
522 #else
523 #define chipDEFINE_ALIGNED_VAR(name, size, align_type) \
524     align_type name[(((size) + (sizeof (align_type) - 1)) / sizeof (align_type))]
525
526 #endif // defined(__cplusplus) && (__cplusplus >= 201103L)
527
528 // Type Definitions
529
530 class Foo
531 {
532 public:
533     Foo(void);
534     Foo(const Foo &inFoo);
535     ~Foo(void);
536 };
537
538 // Global Variables
539
540 static chipDEFINE_ALIGNED_VAR(sFooAllocatorBuffer, sizeof (StaticAllocatorBitmap), uint32_t);
541 static StaticAllocatorBitmap *sFooAllocator;
542
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)
548 {
549     sFooAllocator = new (sFooAllocatorBuffer)
550         StaticAllocatorBitmap(inStorage,
551                               inStorageSize,
552                               inElementCount,
553                               inInitialize,
554                               inDestroy);
555 }
556
557 static StaticAllocatorBitmap &GetFooAllocator(void)
558 {
559     return *sFooAllocator;
560 }
561
562 static void *FooInitialize(AllocatorBase &inAllocator, void *inObject)
563 {
564     memset(inObject, 0, sizeof(Foo));
565
566     return inObject;
567 }
568
569 static void FooDestroy(AllocatorBase &inAllocator, void *inObject)
570 {
571     return;
572 }
573
574 int Init(void)
575 {
576     static const size_t sFooCount = CHIP_FOO_COUNT;
577     static chipAllocatorStaticBitmapStorageDefine(sFooStorage, Foo, sFooCount, uint32_t, sizeof (void *));
578     int retval = 0;
579
580     CreateFooAllocator(sFooStorage,
581                        sizeof (sFooStorage),
582                        sFooCount,
583                        FooInitialize,
584                        FooDestroy);
585
586     return retval;
587 }
588
589 Foo * FooAllocate(void)
590 {
591     Foo *foo;
592
593     foo = static_cast<Foo *>(GetFooAllocator().allocate());
594
595     return foo;
596 }
597
598 void FooDeallocate(Foo *inFoo)
599 {
600     GetFooAllocator().deallocate(inFoo);
601 }
602 ----
603
604 ====== Use Platform-defined and -assigned Allocators
605
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.
610
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.
614
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.
618
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.
622
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.']
624 ----
625
626 chipPlatformInitFooAllocator();
627 chipPlatformInitBarAllocator();
628
629 foo = chipPlatformGetFooAllocator().allocate();
630
631 chipPlatformGetFooAllocator().deallocate(foo);
632
633 bar = chipPlatformGetBarAllocator().allocate();
634
635 chipPlatformGetBarAllocator().deallocate(bar);
636 ----
637
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.']
639 ----
640 chipPlatformInitAllocator(CHIP_FOO_T);
641 chipPlatformInitAllocator(CHIP_BAR_T);
642
643 foo = chipPlatformGetAllocator(CHIP_FOO_T).allocate();
644
645 chipPlatformGetAllocator(CHIP_FOO_T).deallocate(foo);
646
647 bar = chipPlatformGetAllocator(CHIP_BAR_T).allocate();
648
649 chipPlatformGetAllocator(CHIP_BAR_T).deallocate(bar);
650 ----
651
652 [source,C,caption='',title='{listing-caption} *{counter:refnum}*. Using unique allocators per object.']
653 ----
654 chipPlatformInitFooAllocator();
655 chipPlatformInitBarAllocator();
656
657 foo = chipPlatformFooAllocate();
658
659 chipPlatformFooDeallocate(foo);
660
661 bar = chipPlatformBarAllocate();
662
663 chipPlatformBarDeallocate(bar);
664 ----
665
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.']
667 ----
668
669 chipPlatformInitAllocator(CHIP_FOO_T);
670 chipPlatformInitAllocator(CHIP_BAR_T);
671
672 foo = chipPlatformAllocate(CHIP_FOO_T);
673
674 chipPlatformDeallocate(CHIP_FOO_T, foo);
675
676 bar = chipPlatformAllocate(CHIP_BAR_T);
677
678 chipPlatformBarDeallocate(CHIP_BAR_T, bar);
679 ----
680
681 :sectnums!:
682
683 == Recommended Reading
684
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
688 code you write:
689
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
693 1.0. March 3, 2009.
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
697 2014.
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.
707
708 == Revision History
709
710 [cols="^1,^1,<2,<3",options="header"]
711 |===
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.
718 |===
719
720 [.text-center]
721 _Project Connect Home over IP Public Information_