• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1[article Boost.Predef
2    [quickbook 1.7]
3    [version 1.10]
4    [authors [Rivera, Rene]]
5    [copyright 2005-2019 Rene Rivera]
6    [copyright 2015 Charly Chevalier]
7    [copyright 2015 Joel Falcou]
8    [purpose Identification and specification of predefined macros.]
9    [license
10        Distributed under the Boost Software License, Version 1.0.
11        (See accompanying file LICENSE_1_0.txt or copy at
12        [@http://www.boost.org/LICENSE_1_0.txt])
13    ]
14    [source-mode c++]
15    [category miscellaneous]
16    [id predef]
17    [dirname predef]
18]
19
20[section Introduction]
21
22This library defines a set of compiler, architecture, operating system,
23library, and other version numbers from the information it can gather of
24C, C++, Objective C, and Objective C++ predefined macros or those defined
25in generally available headers. The idea for this library grew out of a
26proposal to extend the Boost Config library to provide more, and consistent,
27information than the feature definitions it supports. What follows is
28an edited version of that brief proposal.
29
30[heading Proposal]
31
32The idea is to define a set of macros to identify compilers and
33consistently represent their version. This includes:
34
35* A unique BOOST_VERSION_NUMBER(major,minor,patch) macro to specify version
36  numbers (unfortunately, the name BOOST_VERSION is already taken to designate
37  the version number of boost itself).
38* A compiler identification macro, suitable for use in `#if`/`#elif` directives,
39  for each of the supported compilers. All macros would be defined, regardless
40  of the compiler. The one macro corresponding to the compiler being used would
41  be defined, in terms of BOOST_VERSION_NUMBER, to carry the exact compiler
42  version. All other macros would expand to an expression evaluating to false
43  (for instance, the token 0) to indicate that the corresponding compiler is not
44  present.
45* "Null values" could be set, for all macros, in
46  boost/config/select_compiler.hpp; then, for each compiler the corresponding
47  identification macro would be #undef and re-#defined in the corresponding
48  boost/compiler/(cc).hpp; however in the context of the Boost.Config
49  infrastructure using a "prefix" header (to be introduced) or
50  boost/config/suffix.hpp is a better solution.
51
52[heading Current Library]
53
54The current Predef library is now, both an independent library, and expanded
55in scope. It includes detection and definition of architectures, compilers,
56languages, libraries, operating systems, and endianness. The key benefits are:
57
58* Version numbers that are always defined so that one doesn't have to guard
59  with `#ifdef`.
60* Guard macros that can be used for `#ifdef` checks.
61* All possible definitions are included with the single `#include <boost/predef.h>`
62  so that it's friendly to precompiled header usage.
63* Specific definitions can be included, ex. `#include <boost/predef/os/windows.h>`
64  for single checks.
65* Predefs can be directly used in both preprocessor and compiler expressions
66  for comparison to other similarly defined values.
67* The headers are usable from multiple languages, that support the C preprocessor.
68  In particular C++, C, Objective C, and Objective C++.
69
70[heading Design choices]
71
72An important design choice concerns how to represent compiler versions by means
73of a single integer number suitable for use in preprocessing directives. Let's
74do some calculation. The "basic" signed type for preprocessing
75constant-expressions is long in C90 (and C++, as of 2006) and intmax_t in C99.
76The type long shall at least be able to represent the number [^+2 147 483 647].
77This means the most significant digit can only be 0, 1 or 2; and if we want all
78decimal digits to be able to vary between 0 and 9, the largest range we can
79consider is [^\[0, 999 999 999\]]. Distributing evenly, this means 3 decimal
80digits for each version number part.
81
82So we can:
83
84# use an uneven distribution or
85# use more bits (a larger type) or
86# use 3/3/3 and have the particular compiler/platform/stdlib deal with setting
87  the numbers within the 3-digit range.
88
89It appears relatively safe to go for the first option and set it at 2/2/5. That
90covers CodeWarrior and others, which are up to and past 10 for the major number.
91Some compilers use the build number in lieu of the patch one; five digits
92(which is already reached by VC++ 8) seems a reasonable limit even in this case.
93
94[note A 2/2/6 scheme would allow for bigger patch/build numbers at the cost,
95for instance, of limiting the major version number to 20 (or, with further
96constraints, to 21).]
97
98It might reassure the reader that this decision is actually encoded in one place
99in the code; the definition of `BOOST_VERSION_NUMBER`.
100
101[heading Future work]
102
103Even though the basics of this library are done, there is much work that can be
104done:
105
106* Right now we limit the detection of libraries to known built-in predefined
107  macros, and to guaranteed to exist system and library headers. It might be
108  interesting to add something like auto-configuration predefs. This way we can
109  add definitions for user specific libraries and features.
110* Along with the above, it might be good to add some user control as to which
111  headers are included with the top-level header. Although in the current
112  form of the library this is less of an issue as one can include the
113  specific headers one needs.
114* Additionally, even if there is no auto-configure style option.. It would be
115  good to add optionally included headers so that user can get consistent
116  version number definitions for libraries they use.
117* And obviously there's lots of work to do in reformulating the existing
118  Boost libraries to use the Predef library.
119* And there's the continuing work of adding definitions for present and
120  future compilers, platforms, architectures, languages, and libraries.
121
122[endsect] [/Introduction]
123
124[section Using the predefs]
125
126To use the automatically defined predefs one needs to only include the
127single top-level header:
128
129``
130  #include <boost/predef.h>
131``
132
133This defines [*all] the version macros known to the library. For each
134macro it will be defined to either a /zero/ valued expression for when
135the particular item is not detected, and to a /positive/ value if it
136is detected. The predef macros fall onto five categories each with
137macros of a particular prefix:
138
139* `BOOST_ARCH_`for system/CPU architecture one is compiling for.
140* `BOOST_COMP_` for the compiler one is using.
141* `BOOST_LANG_` for language standards one is compiling against.
142* `BOOST_LIB_C_` and `BOOST_LIB_STD_` for the C and C++ standard library
143  in use.
144* `BOOST_OS_` for the operating system we are compiling to.
145* `BOOST_PLAT_` for platforms on top of operating system or compilers.
146* `BOOST_ENDIAN_` for endianness of the os and architecture combination.
147* `BOOST_HW_` for hardware specific features.
148* `BOOST_HW_SIMD` for SIMD (Single Instruction Multiple Data) detection.
149
150[note The detected definitions are for the configuration one is targeting
151during the compile. In particular in a cross-compile this means the target
152system, and not the host system.]
153
154One uses the individual definitions to compare against specific versions
155by comparing against the `BOOST_VERSION_NUMBER` macro. For example, to make
156a choice based on the version of the GCC C++ compiler one would:
157
158``
159  #include <boost/predef.h>
160  #include <iostream>
161
162  int main()
163  {
164    if (BOOST_COMP_GNUC >= BOOST_VERSION_NUMBER(4,0,0))
165      std::cout << "GCC compiler is at least version 4.0.0" << std::endl;
166    else
167      std::cout << "GCC compiler is at older than version 4.0.0, or not a GCC compiler" << std::endl;
168    return 0;
169  }
170``
171
172As you might notice above the `else` clause also covers the case where
173the particular compiler is not detected. But one can make the test
174also test for the detection. All predef definitions are defined
175as a zero (0) expression when not detected. Hence one could use the
176detection with a natural single condition. For example:
177
178``
179  #include <boost/predef.h>
180  #include <iostream>
181
182  int main()
183  {
184    if (BOOST_COMP_GNUC)
185      std::cout << "This is GNU GCC!" << std::endl;
186    else
187      std::cout << "Not GNU GCC." << std::endl;
188    return 0;
189  }
190``
191
192And since the predef's are preprocessor definitions the same is possible
193from the preprocessor:
194
195``
196  #include <boost/predef.h>
197  #include <iostream>
198
199  #if BOOST_COMP_GNUC
200    #if BOOST_COMP_GNUC >= BOOST_VERSION_NUMBER(4,0,0)
201      const char * the_compiler = "GNU GCC, of at least version 4."
202    #else
203      const char * the_compiler = "GNU GCC, less than version 4."
204    #endif
205  #else
206    const char * the_compiler = "Not GNU GCC."
207  #endif
208
209  int main()
210  {
211    std::cout << the_compiler << std::endl;
212    return 0;
213  }
214``
215
216In addition, for each version macro defined there is an
217`*_AVAILABLE` macro defined only when the particular aspect is
218detected. I.e. a definition equivalent to:
219
220``
221  #if BOOST_PREDEF_ABC
222    #define BOOST_PREDEF_ABC_AVAILABLE
223  #endif
224``
225
226Also for each aspect there is a macro defined with a descriptive
227name of what the detection is.
228
229[heading The `*_EMULATED` macros]
230
231Predef definitions are guaranteed to be uniquely detected within one category.
232But there are contexts under which multiple underlying detections are possible.
233The well known example of this is detection of GCC and MSVC compilers which are
234commonly emulated by other compilers by defining the same base macros. To
235account for this detection headers are allowed to define `*_EMULATED` predefs
236when this situation is detected. The emulated predefs will be set to the
237version number of the detection instead of the regular predef macro for that
238detection. For example MSVC will set `BOOST_COMP_MSVC_EMULATED` but not set `BOOST_COMP_MSVC`, and it will also set `BOOST_COMP_MSVC_AVAILABLE`.
239
240[heading Using the `BOOST_VERSION_NUMBER` macro]
241
242All the predefs are defined to be a use of the `BOOST_VERSION_NUMBER` macro.
243The macro takes individual major, minor, and patch value expressions:
244
245``
246  #define BOOST_VERSION_NUMBER( major, minor, patch ) ...
247``
248
249The arguments are:
250
251# Major version number, as a constant value expression in the range [0,99].
252# Minor version number, as a constant value expression in the range [0,99].
253# Patch-level version number, as a constant value expression in the
254  range [0,99999].
255
256The ranges for each are "enforced" by the use of a modulo ("%"), i.e. truncation,
257as opposed to a clamp. And hence this means that the limits are enforced only
258enough to keep from having out-of-range problems. But not enough to prevent
259other kinds of problems. Like exceeding the range and getting false detections,
260or non-detections. It is up to the individual predefs to ensure correct
261usage beyond the range guarantee.
262
263The values for the arguments can be any preprocessor valid constant value expression.
264Only constant value arithmetic is used in the definition of the `BOOST_VERSION_NUMBER`
265macro and in any of the other predef macros. This means that any allowed base is
266possible, i.e. binary, octal, decimal, and hexadecimal. For example:
267
268``
269  #define MY_APPLICATION_VERSION_NUMBER BOOST_VERSION_NUMBER(2,0xA,015)
270``
271
272Is equivalent to:
273
274``
275  #define MY_APPLICATION_VERSION_NUMBER BOOST_VERSION_NUMBER(2,10,13)
276``
277
278[endsect]
279
280[section Adding new predefs]
281
282We know that a library like this one will be an eternal work-in-progress. And
283as such we expect, and look forward to, others contributing corrections and
284additions to the predefs. With that in mind we need to keep a consistent way
285of defining the new predefs. Hence all current, and future, predefs follow
286the same structure and requirements.
287
288[heading Requirements of the header]
289
290All predefs need to follow a set of requirements:
291
292* The headers must use the Boost Software License.
293* The predef must, by default, be defined as `BOOST_VERSION_NUMBER_NOT_AVAILABLE`.
294* The predef must be redefined to a non-zero value once detected.
295* The predef must, by default, be defined to `BOOST_VERSION_NUMBER_AVAILABLE`
296  when the predef is detected.
297* If possible, the predef will be defined as the version number detected.
298* The predef must define `*_AVAILABLE` macros as needed.
299* The predef must define a symbolic constant string name macro.
300* The predef must declare itself, after being defined, for the testing
301  system.
302* The predef must guarantee that it is the only one defined as detected
303  per category.
304* But a predef can define `*_EMULATED` macros to indicate that it was
305  previously detected by another header and is being "emulated" by the
306  system. Note that the `*_AVAILABLE` macros must still be defined in this
307  situation.
308
309And there are some extra guidelines that predef headers should follow:
310
311* The detection should avoid including extra headers that might otherwise
312  not be included by default.
313* If the detection must include a header, prefer guarding it within the
314  detection if possible.
315* If the detection must include headers unconditionally, and has a choice
316  of headers to include, prefer the ones with the least impact. I.e.
317  include the one with the minimal set of definitions and other
318  dependencies.
319
320[heading Structure of the header]
321
322For general consistency it's suggested that new predef headers follow the
323structure below, as current predef headers do. First we have the copyright
324and license statement, followed by the include guard:
325
326``
327/*
328Copyright Jane Doe YYYY
329Distributed under the Boost Software License, Version 1.0.
330(See accompanying file LICENSE_1_0.txt or copy at
331http://www.boost.org/LICENSE_1_0.txt)
332*/
333
334#ifndef BOOST_PREDEF_category_tag_H
335#define BOOST_PREDEF_category_tag_H
336``
337
338If the detection depends on the detection of another predef you should
339include those headers here.
340
341``
342#include <boost/predef/CATEGORY_TAG/DEPENDENCY.h>
343``
344
345Depending on how you are defining the predef you will at minimum have
346to include the `version_number.h` header. But you might also want to
347include the `make.h` header for the version number decomposing utility
348macros:
349
350``
351#include <boost/predef/version_number.h>
352#include <boost/predef/make.h>
353``
354
355The Predef library uses Quickbook for documentation and for the
356individual predefs to appear in the reference section we add in-code
357documentation followed by the zero-value default definition of the
358predef macro. We strongly recommend this particular placement of the
359documentation and default definition because some development
360environments automatically interpret this and provide in-line help
361for the macro. In particular this works for the popular Eclipse IDE:
362
363``
364/*`
365[heading `BOOST_category_tag`]
366
367Documentation about what is detected.
368*/
369
370#define BOOST_category_tag BOOST_VERSION_NUMBER_NOT_AVAILABLE
371``
372
373Next is the detection and definition of the particular predef. The
374structure for this is to do a single overall check (`condition_a`) and
375place further version detection inside this. The first action inside
376the overall check is to "`#undef BOOST_category_tag`" which undefines
377the zero-value default. The rest is up to the you how to do the checks
378for defining the version. But at minimum it must
379"`#define BOOST_category_tag BOOST_VERSION_NUMBER_AVAILABLE`" as the fallback
380to minimally indicate that the predef was detected:
381
382``
383#if (condition_a)
384#   undef BOOST_category_tag
385#   if (condition_b)
386#        define BOOST_category_tag BOOST_VERSION_NUMBER(major,minor,patch)
387#    else
388#        define BOOST_category_tag BOOST_VERSION_NUMBER_AVAILABLE
389#    endif
390#endif
391``
392
393We also need to provide the `*_AVAILABLE` versions of the predef.
394
395``
396#if BOOST_category_tag
397#   define BOOST_category_tag_AVAILABLE
398#endif
399``
400
401And for convenience we also want to provide a `*_NAME` macro:
402
403``
404#define BOOST_category_tag_NAME "Name"
405``
406
407The testing of the predef macros is automated to generate checks for all
408the defined predefs, whether detected or not. To do this we need to
409declare the predef to the test system. This declaration is empty for
410regular use. And during the test programs they expand out specially
411to create informational output:
412
413``
414#include <boost/predef/detail/test.h>
415BOOST_PREDEF_DECLARE_TEST(BOOST_category_tag,BOOST_category_tag_NAME)
416``
417
418And, of course, we last need to close out the include guard:
419
420``
421#endif
422``
423
424[heading Adding exclusive predefs]
425
426For headers of predefs that need to be mutually exclusive in the detection
427we need to add checks and definitions to detect when the predef is
428detected by multiple headers.
429
430Internally compiler, operating system, and platforms define
431`BOOST_PREDEF_DETAIL_COMP_DETECTED`, `BOOST_PREDEF_DEFAIL_OS_DETECTED`, and
432`BOOST_PREDEF_DETAIL_PLAT_DETECTED` respectively when the predef is first
433detected. This is used to guard against multiple definition of the detection
434in later included headers. In those cases the detection would instead be
435written as:
436
437``
438#if !BOOST_PREDEF_DETAIL_category_DETECTED && (condition_a)
439#   undef BOOST_category_tag
440#   if (condition_b)
441#        define BOOST_category_tag BOOST_VERSION_NUMBER(major,minor,patch)
442#    else
443#        define BOOST_category_tag BOOST_VERSION_NUMBER(0,0,1)
444#    endif
445#endif
446``
447
448And we also include a header that defines the `*_DETECTED` macro when we have
449the detection:
450
451``
452#if BOOST_category_tag
453#   define BOOST_category_tag_AVAILABLE
454#   include <boost/predef/detail/CATEGORY_detected.h>
455#endif
456``
457
458Everything else about the header is the same as the basic detection header.
459
460[heading Adding an exclusive but emulated predef]
461
462Because compilers are frequently emulated by other compilers we both want
463to have exclusive detection of the compiler and also provide information
464that we detected the emulation of the compiler. To accomplish this we define
465a local `*_DETECTION` macro for the compiler detection. And conditionally
466define either the base compiler predef `BOOST_COMP_compiler` or the alternate
467`BOOST_COMP_compiler_EMULATED` predef.
468
469The initial detection would look like:
470
471``
472#if (condition_a)
473#   if (condition_b)
474#        define BOOST_COMP_tag_DETECTION BOOST_VERSION_NUMBER(major,minor,patch)
475#    else
476#        define BOOST_COMP_tag_DETECTION BOOST_VERSION_NUMBER_AVAILABLE
477#    endif
478#endif
479``
480
481And then we can conditionally define the base or emulated predefs:
482
483``
484#ifdef BOOST_COMP_tag_DETECTION
485#   if defined(BOOST_PREDEF_DETAIL_COMP_DETECTED)
486#       define BOOST_COMP_tag_EMULATED BOOST_COMP_tag_DETECTION
487#   else
488#       undef BOOST_COMP_tag
489#       define BOOST_COMP_tag BOOST_COMP_tag_DETECTION
490#   endif
491#   define BOOST_category_tag_AVAILABLE
492#   include <boost/predef/detail/comp_detected.h>
493#endif
494``
495
496[heading Using utility pattern macros]
497
498By including:
499
500``
501#include <boost/predef/make.h>
502``
503
504One will get a set of utility macros to decompose common version
505macros as defined by compilers. For example the EDG compiler
506uses a simple 3-digit version macro (M,N,P). It can be decomposed
507and defined as:
508
509``
510#define BOOST_COMP_EDG BOOST_PREDEF_MAKE_N_N_N(__EDG_VERSION__)
511``
512
513The decomposition macros are split into three types: decimal
514decomposition, hexadecimal decomposition, and date decomposition.
515They follow the format of using "N" for decimal, "F" for hexadecimal,
516and "Y", "M", "D" for dates.
517
518[endsect]
519
520[def __predef_symbol__ Symbol]
521[def __predef_version__ Version]
522[def __predef_detection__ *detection*]
523
524[section Reference]
525
526[section `BOOST_ARCH` architecture macros]
527[include ../include/boost/predef/architecture/*.h]
528[include ../include/boost/predef/architecture/x86/*.h]
529[endsect]
530
531[section `BOOST_COMP` compiler macros]
532[include ../include/boost/predef/compiler/*.h]
533[endsect]
534
535[section `BOOST_LANG` language standards macros]
536[include ../include/boost/predef/language/*.h]
537[endsect]
538
539[section `BOOST_LIB` library macros]
540[include ../include/boost/predef/library/c/*.h]
541[include ../include/boost/predef/library/std/*.h]
542[endsect]
543
544[section `BOOST_OS` operating system macros]
545[include ../include/boost/predef/os/*.h]
546[include ../include/boost/predef/os/bsd/*.h]
547[endsect]
548
549[section `BOOST_PLAT` platform macros]
550[include ../include/boost/predef/platform/*.h]
551[endsect]
552
553[section `BOOST_HW` hardware macros]
554[include ../include/boost/predef/hardware/*.h]
555[endsect]
556
557[section Other macros]
558[include ../include/boost/predef/other/*.h]
559[endsect]
560
561[section Version definition macros]
562[include ../include/boost/predef/version_number.h]
563[include ../include/boost/predef/make.h]
564[endsect]
565
566[endsect]
567
568[section Check Utilities]
569
570The `predef_check` utility provides a facility for building a
571program that will check a given set of expressions against
572the definitions it detected when it was built.
573
574[heading [^predef_check] programs]
575
576Even though there is only one `predef_check` program, there
577are variations for each of the languages that are detected
578by Predef to match the convention for sources files. For all
579of them one invokes with a list of expression arguments. The
580expressions are evaluated within the context of the particular
581[^predef_check] program and if they all are true zero (0) is returned.
582Otherwise the index of the first false expression is returned.
583
584The expression syntax is simple:
585
586[teletype]
587``
588predef-definition [ relational-operator version-value ]
589``
590[c++]
591
592[~predef-definition] can be any of the Predef definitions. For
593example `BOOST_COMP_GCC`.
594
595[~relational-operator] can be any of: [^>], [^<], [^>=], [^<=],
596[^==] and [^!=].
597
598[~version-number] can be a full or partial version triplet value.
599If it's a partial version triple it is completed with zeros. That
600is [^x.y] is equivalent to [^x.y.0] and [^x] is equivalent to
601[^x.0.0].
602
603The [~relations-operator] and [~version-number] can be ommited. In
604which case it is equivalent to:
605
606[teletype]
607``
608predef-definition > 0.0.0
609``
610[c++]
611
612[heading Using with Boost.Build]
613
614You can use the [^predef_check] programs directly from Boost Build
615to configure target requirements. This is useful for controlling
616what gets built as part of your project based on the detailed
617version information available in Predef. The basic use is simple:
618
619[teletype]
620``
621import path-to-predef-src/tools/check/predef
622    : check require
623    : predef-check predef-require ;
624
625exe my_windows_program : windows_source.cpp
626    : [ predef-require "BOOST_OS_WINDOWS" ] ;
627``
628[c++]
629
630That simple use case will skip building the [^my_windows_program]
631unless one is building for Windows. Like the direct [^predef_check]
632you can pass mutiple expressions using relational comparisons.
633For example:
634
635[teletype]
636``
637import path-to-predef-src/tools/check/predef
638    : check require
639    : predef-check predef-require ;
640
641lib my_special_lib : source.cpp
642    : [ predef-require "BOOST_OS_WINDOWS != 0" "BOOST_OS_VMS != 0"] ;
643``
644[c++]
645
646And in that case the [^my_special_lib] is built only when the OS is
647not Windows or VMS. The [^requires] rule is a special case of the
648[^check] rule. And is defined in terms of it:
649
650[teletype]
651``
652rule require ( expressions + : language ? )
653{
654    return [ check $(expressions) : $(language) : : <build>no ] ;
655}
656``
657[c++]
658
659The expression can also use explicit "and", "or" logical operators
660to for more complex checks:
661
662
663[teletype]
664``
665import path-to-predef-src/tools/check/predef
666    : check require
667    : predef-check predef-require ;
668
669lib my_special_lib : source.cpp
670    : [ predef-require "BOOST_OS_WINDOWS" or "BOOST_OS_VMS"] ;
671``
672[c++]
673
674You can use the [^check] rule for more control and to implement
675something other than control of what gets built. The definition
676for the [^check] rule is:
677
678[teletype]
679``
680rule check ( expressions + : language ? : true-properties * : false-properties * )
681``
682[c++]
683
684When invoked as a reuirement of a Boost Build target this rule
685will add the [^true-properties] to the target if all the [^expressions]
686evaluate to true. Otherwise the [^false-properties] get added as
687requirements. For example you could use it to enable or disable
688features in your programs:
689
690[teletype]
691``
692import path-to-predef-src/tools/check/predef
693    : check require
694    : predef-check predef-require ;
695
696exe my_special_exe : source.cpp
697    : [ predef-check "BOOST_OS_WINDOWS == 0"
698        : : <define>ENABLE_WMF=0
699        : <define>ENABLE_WMF=1 ] ;
700``
701[c++]
702
703For both [^check] and [^require] the [^language] argument controls
704which variant of the [^predef_check] program is used to check the
705expressions. It defaults to "c++", but can be any of: "c", "cpp",
706"objc", and "objcpp".
707
708[endsect]
709
710[include history.qbk]
711[include todo.qbk]
712
713[section Acknoledgements]
714
715The comprehensiveness of this library would not be
716possible without the existence of the indispensable
717resource that is the
718[@http://sourceforge.net/p/predef/ Pre-defined C/C++ Compiler Macros]
719Project. It was, and continues to be, the primary source
720of the definitions that make up this library. Thanks
721to Bjorn Reese and all the volunteers that make that
722resource possible.
723
724This library would be an incoherent mess if it weren't for
725Boost community that provided invaluable feedback for the
726eight years that it took to polish into a useable form.
727In particular I would like to thank: Mathias Gaunard,
728Robert Stewart, Joël Lamotte, Lars Viklund, Nathan Ridge,
729Artyom Beilis, Joshua Boyce, Gottlob Frege, Thomas Heller,
730Edward Diener, Dave Abrahams, Iain Denniston, Dan Price,
731Ioannis Papadopoulos, and Robert Ramey. And thanks to
732Joel Falcou for managing the review of this library.
733
734[endsect]
735