• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Language Guide: C++
2
3## Before you get started
4
5Before diving into the FlatBuffers usage in C++, it should be noted that
6the [Tutorial](../tutorial.md) page has a complete guide
7to general FlatBuffers usage in all of the supported languages (including C++).
8This page is designed to cover the nuances of FlatBuffers usage, specific to
9C++.
10
11#### Prerequisites
12
13This page assumes you have written a FlatBuffers schema and compiled it
14with the Schema Compiler. If you have not, please see
15[Using the schema compiler](../flatc.md)
16and [Writing a schema](../schema.md).
17
18Assuming you wrote a schema, say `mygame.fbs` (though the extension doesn't
19matter), you've generated a C++ header called `mygame_generated.h` using the
20compiler (e.g. `flatc -c mygame.fbs`), you can now start using this in
21your program by including the header. As noted, this header relies on
22`flatbuffers/flatbuffers.h`, which should be in your include path.
23
24## FlatBuffers C++ library code location
25
26The code for the FlatBuffers C++ library can be found at
27`flatbuffers/include/flatbuffers`. You can browse the library code on the
28[FlatBuffers GitHub page](https://github.com/google/flatbuffers/tree/master/include/flatbuffers).
29
30## Testing the FlatBuffers C++ library
31
32The code to test the C++ library can be found at `flatbuffers/tests`.
33The test code itself is located in
34[test.cpp](https://github.com/google/flatbuffers/blob/master/tests/test.cpp).
35
36This test file is built alongside `flatc`. To review how to build the project,
37please read the [Building](../building.md) documentation.
38
39To run the tests, execute `flattests` from the root `flatbuffers/` directory.
40For example, on [Linux](https://en.wikipedia.org/wiki/Linux), you would simply
41run: `./flattests`.
42
43## Using the FlatBuffers C++ library
44
45*Note: See [Tutorial](../tutorial.md) for a more in-depth
46example of how to use FlatBuffers in C++.*
47
48FlatBuffers supports both reading and writing FlatBuffers in C++.
49
50To use FlatBuffers in your code, first generate the C++ classes from your
51schema with the `--cpp` option to `flatc`. Then you can include both FlatBuffers
52and the generated code to read or write FlatBuffers.
53
54For example, here is how you would read a FlatBuffer binary file in C++:
55First, include the library and generated code. Then read the file into
56a `char *` array, which you pass to `GetMonster()`.
57
58```cpp
59    #include "flatbuffers/flatbuffers.h"
60    #include "monster_test_generate.h"
61    #include <iostream> // C++ header file for printing
62    #include <fstream> // C++ header file for file access
63
64
65    std::ifstream infile;
66    infile.open("monsterdata_test.mon", std::ios::binary | std::ios::in);
67    infile.seekg(0,std::ios::end);
68    int length = infile.tellg();
69    infile.seekg(0,std::ios::beg);
70    char *data = new char[length];
71    infile.read(data, length);
72    infile.close();
73
74    auto monster = GetMonster(data);
75```
76
77`monster` is of type `Monster *`, and points to somewhere *inside* your
78buffer (root object pointers are not the same as `buffer_pointer` \!).
79If you look in your generated header, you'll see it has
80convenient accessors for all fields, e.g. `hp()`, `mana()`, etc:
81
82```cpp
83    std::cout << "hp : " << monster->hp() << std::endl;              // '80'
84    std::cout << "mana : " << monster->mana() << std::endl;          // default value of '150'
85    std::cout << "name : " << monster->name()->c_str() << std::endl; // "MyMonster"
86```
87
88*Note: That we never stored a `mana` value, so it will return the default.*
89
90The following attributes are supported:
91
92-   `shared` (on a field): For string fields, this enables the usage of string
93    pooling (i.e. `CreateSharedString`) as default serialization behavior.
94
95    Specifically, `CreateXxxDirect` functions and `Pack` functions for object
96    based API (see below) will use `CreateSharedString` to create strings.
97
98## Object based API
99
100FlatBuffers is all about memory efficiency, which is why its base API is written
101around using as little as possible of it. This does make the API clumsier
102(requiring pre-order construction of all data, and making mutation harder).
103
104For times when efficiency is less important a more convenient object based API
105can be used (through `--gen-object-api`) that is able to unpack & pack a
106FlatBuffer into objects and standard STL containers, allowing for convenient
107construction, access and mutation.
108
109To use:
110
111```cpp
112    // Autogenerated class from table Monster.
113    MonsterT monsterobj;
114
115    // Deserialize from buffer into object.
116    GetMonster(flatbuffer)->UnPackTo(&monsterobj);
117
118    // Update object directly like a C++ class instance.
119    cout << monsterobj.name;  // This is now a std::string!
120    monsterobj.name = "Bob";  // Change the name.
121
122    // Serialize into new flatbuffer.
123    FlatBufferBuilder fbb;
124    fbb.Finish(Monster::Pack(fbb, &monsterobj));
125```
126
127The following attributes are specific to the object-based API code generation:
128
129-   `native_inline` (on a field): Because FlatBuffer tables and structs are
130    optionally present in a given buffer, they are best represented as pointers
131    (specifically std::unique_ptrs) in the native class since they can be null.
132    This attribute changes the member declaration to use the type directly
133    rather than wrapped in a unique_ptr.
134
135-   `native_default("value")` (on a field): For members that are declared
136    "native_inline", the value specified with this attribute will be included
137    verbatim in the class constructor initializer list for this member.
138
139-   `native_custom_alloc("custom_allocator")` (on a table or struct): When using the
140    object-based API all generated NativeTables that  are allocated when unpacking
141    your  flatbuffer will use "custom allocator". The allocator is also used by
142    any std::vector that appears in a table defined with `native_custom_alloc`.
143    This can be  used to provide allocation from a pool for example, for faster
144    unpacking when using the object-based API.
145
146Minimal Example:
147
148schema:
149
150```cpp
151    table mytable(native_custom_alloc:"custom_allocator") {
152      ...
153    }
154```
155
156with `custom_allocator` defined before `flatbuffers.h` is included, as:
157
158```cpp
159    template <typename T> struct custom_allocator : public std::allocator<T> {
160
161      typedef T *pointer;
162
163      template <class U>
164      struct rebind {
165        typedef custom_allocator<U> other;
166      };
167
168      pointer allocate(const std::size_t n) {
169        return std::allocator<T>::allocate(n);
170      }
171
172      void deallocate(T* ptr, std::size_t n) {
173        return std::allocator<T>::deallocate(ptr,n);
174      }
175
176      custom_allocator() throw() {}
177
178      template <class U>
179      custom_allocator(const custom_allocator<U>&) throw() {}
180    };
181```
182
183-   `native_type("type")` (on a struct): In some cases, a more optimal C++ data
184type exists for a given struct.  For example, the following schema:
185
186```cpp
187    struct Vec2 {
188      x: float;
189      y: float;
190    }
191```
192
193generates the following Object-Based API class:
194
195```cpp
196    struct Vec2T : flatbuffers::NativeTable {
197      float x;
198      float y;
199    };
200```
201
202However, it can be useful to instead use a user-defined C++ type since it
203can provide more functionality, eg.
204
205```cpp
206    struct vector2 {
207      float x = 0, y = 0;
208      vector2 operator+(vector2 rhs) const { ... }
209      vector2 operator-(vector2 rhs) const { ... }
210      float length() const { ... }
211      // etc.
212    };
213```
214
215The `native_type` attribute will replace the usage of the generated class
216with the given type.  So, continuing with the example, the generated
217code would use `vector2` in place of `Vec2T` for all generated code of
218the Object-Based API.
219
220However, because the `native_type` is unknown to flatbuffers, the user must
221provide the following functions to aide in the serialization process:
222
223```cpp
224    namespace flatbuffers {
225      Vec2 Pack(const vector2& obj);
226      vector2 UnPack(const Vec2& obj);
227    }
228```
229
230-   `native_type_pack_name("name")` (on a struct when `native_type` is
231    specified, too): when you want to use the same `native_type` multiple times
232    (e. g. with different precision) you must make the names of the Pack/UnPack
233    functions unique, otherwise you will run into compile errors. This attribute
234    appends a name to the expected Pack/UnPack functions. So when you
235    specify `native_type_pack_name("Vec2")` in the above example you now need to
236    implement these serialization functions instead:
237
238```cpp
239    namespace flatbuffers {
240      Vec2 PackVec2(const vector2& obj);
241      vector2 UnPackVec2(const Vec2& obj);
242    }
243```
244
245Finally, the following top-level attributes:
246
247-   `native_include("path")` (at file level): Because the `native_type` attribute
248    can be used to introduce types that are unknown to flatbuffers, it may be
249    necessary to include "external" header files in the generated code.  This
250    attribute can be used to directly add an #include directive to the top of
251    the generated code that includes the specified path directly.
252
253-   `force_align`: this attribute may not be respected in the object API,
254    depending on the aligned of the allocator used with `new`.
255
256## External references
257
258An additional feature of the object API is the ability to allow you to load
259multiple independent FlatBuffers, and have them refer to eachothers objects
260using hashes which are then represented as typed pointers in the object API.
261
262To make this work have a field in the objects you want to referred to which is
263using the string hashing feature (see `hash` attribute in the
264[schema](../schema.md) documentation). Then you have
265a similar hash in the field referring to it, along with a `cpp_type`
266attribute specifying the C++ type this will refer to (this can be any C++
267type, and will get a `*` added).
268
269Then, in JSON or however you create these buffers, make sure they use the
270same string (or hash).
271
272When you call `UnPack` (or `Create`), you'll need a function that maps from
273hash to the object (see `resolver_function_t` for details).
274
275## Using different pointer types
276
277By default the object tree is built out of `std::unique_ptr`, but you can
278influence this either globally (using the `--cpp-ptr-type` argument to
279`flatc`) or per field (using the `cpp_ptr_type` attribute) to by any smart
280pointer type (`my_ptr<T>`), or by specifying `naked` as the type to get `T *`
281pointers. Unlike the smart pointers, naked pointers do not manage memory for
282you, so you'll have to manage their lifecycles manually.  To reference the
283pointer type specified by the `--cpp-ptr-type` argument to `flatc` from a
284flatbuffer field set the `cpp_ptr_type` attribute to `default_ptr_type`.
285
286## Using different string type
287
288By default the object tree is built out of `std::string`, but you can
289influence this either globally (using the `--cpp-str-type` argument to
290`flatc`) or per field using the `cpp_str_type` attribute.
291
292The type must support `T::c_str()`, `T::length()` and `T::empty()` as member functions.
293
294Further, the type must be constructible from std::string, as by default a
295std::string instance is constructed and then used to initialize the custom
296string type. This behavior impedes efficient and zero-copy construction of
297custom string types; the `--cpp-str-flex-ctor` argument to `flatc` or the
298per field attribute `cpp_str_flex_ctor` can be used to change this behavior,
299so that the custom string type is constructed by passing the pointer and
300length of the FlatBuffers String. The custom string class will require a
301constructor in the following format: `custom_str_class(const char *, size_t)`.
302Please note that the character array is not guaranteed to be NULL terminated,
303you should always use the provided size to determine end of string.
304
305## Reflection (& Resizing)
306
307There is experimental support for reflection in FlatBuffers, allowing you to
308read and write data even if you don't know the exact format of a buffer, and
309even allows you to change sizes of strings and vectors in-place.
310
311The way this works is very elegant; there is actually a FlatBuffer schema that
312describes schemas (\!) which you can find in `reflection/reflection.fbs`.
313The compiler, `flatc`, can write out any schemas it has just parsed as a binary
314FlatBuffer, corresponding to this meta-schema.
315
316Loading in one of these binary schemas at runtime allows you traverse any
317FlatBuffer data that corresponds to it without knowing the exact format. You
318can query what fields are present, and then read/write them after.
319
320For convenient field manipulation, you can include the header
321`flatbuffers/reflection.h` which includes both the generated code from the meta
322schema, as well as a lot of helper functions.
323
324And example of usage, for the time being, can be found in
325`test.cpp/ReflectionTest()`.
326
327## Mini Reflection
328
329A more limited form of reflection is available for direct inclusion in
330generated code, which doesn't do any (binary) schema access at all. It was designed
331to keep the overhead of reflection as low as possible (on the order of 2-6
332bytes per field added to your executable), but doesn't contain all the
333information the (binary) schema contains.
334
335You add this information to your generated code by specifying `--reflect-types`
336(or instead `--reflect-names` if you also want field / enum names).
337
338You can now use this information, for example to print a FlatBuffer to text:
339
340    auto s = flatbuffers::FlatBufferToString(flatbuf, MonsterTypeTable());
341
342`MonsterTypeTable()` is declared in the generated code for each type. The
343string produced is very similar to the JSON produced by the `Parser` based
344text generator.
345
346You'll need `flatbuffers/minireflect.h` for this functionality. In there is also
347a convenient visitor/iterator so you can write your own output / functionality
348based on the mini reflection tables without having to know the FlatBuffers or
349reflection encoding.
350
351## Storing maps / dictionaries in a FlatBuffer
352
353FlatBuffers doesn't support maps natively, but there is support to
354emulate their behavior with vectors and binary search, which means you
355can have fast lookups directly from a FlatBuffer without having to unpack
356your data into a `std::map` or similar.
357
358To use it:
359-   Designate one of the fields in a table as they "key" field. You do this
360    by setting the `key` attribute on this field, e.g.
361    `name:string (key)`.
362    You may only have one key field, and it must be of string or scalar type.
363-   Write out tables of this type as usual, collect their offsets in an
364    array or vector.
365-   Instead of `CreateVector`, call `CreateVectorOfSortedTables`,
366    which will first sort all offsets such that the tables they refer to
367    are sorted by the key field, then serialize it.
368-   Now when you're accessing the FlatBuffer, you can use `Vector::LookupByKey`
369    instead of just `Vector::Get` to access elements of the vector, e.g.:
370    `myvector->LookupByKey("Fred")`, which returns a pointer to the
371    corresponding table type, or `nullptr` if not found.
372    `LookupByKey` performs a binary search, so should have a similar speed to
373    `std::map`, though may be faster because of better caching. `LookupByKey`
374    only works if the vector has been sorted, it will likely not find elements
375    if it hasn't been sorted.
376
377## Direct memory access
378
379As you can see from the above examples, all elements in a buffer are
380accessed through generated accessors. This is because everything is
381stored in little endian format on all platforms (the accessor
382performs a swap operation on big endian machines), and also because
383the layout of things is generally not known to the user.
384
385For structs, layout is deterministic and guaranteed to be the same
386across platforms (scalars are aligned to their
387own size, and structs themselves to their largest member), and you
388are allowed to access this memory directly by using `sizeof()` and
389`memcpy` on the pointer to a struct, or even an array of structs.
390
391To compute offsets to sub-elements of a struct, make sure they
392are a structs themselves, as then you can use the pointers to
393figure out the offset without having to hardcode it. This is
394handy for use of arrays of structs with calls like `glVertexAttribPointer`
395in OpenGL or similar APIs.
396
397It is important to note is that structs are still little endian on all
398machines, so only use tricks like this if you can guarantee you're not
399shipping on a big endian machine (an `assert(FLATBUFFERS_LITTLEENDIAN)`
400would be wise).
401
402## Access of untrusted buffers
403
404The generated accessor functions access fields over offsets, which is
405very quick. These offsets are not verified at run-time, so a malformed
406buffer could cause a program to crash by accessing random memory.
407
408When you're processing large amounts of data from a source you know (e.g.
409your own generated data on disk), this is acceptable, but when reading
410data from the network that can potentially have been modified by an
411attacker, this is undesirable.
412
413For this reason, you can optionally use a buffer verifier before you
414access the data. This verifier will check all offsets, all sizes of
415fields, and null termination of strings to ensure that when a buffer
416is accessed, all reads will end up inside the buffer.
417
418Each root type will have a verification function generated for it,
419e.g. for `Monster`, you can call:
420
421```cpp
422	bool ok = VerifyMonsterBuffer(Verifier(buf, len));
423```
424
425if `ok` is true, the buffer is safe to read.
426
427Besides untrusted data, this function may be useful to call in debug
428mode, as extra insurance against data being corrupted somewhere along
429the way.
430
431While verifying a buffer isn't "free", it is typically faster than
432a full traversal (since any scalar data is not actually touched),
433and since it may cause the buffer to be brought into cache before
434reading, the actual overhead may be even lower than expected.
435
436In specialized cases where a denial of service attack is possible,
437the verifier has two additional constructor arguments that allow
438you to limit the nesting depth and total amount of tables the
439verifier may encounter before declaring the buffer malformed. The default is
440`Verifier(buf, len, 64 /* max depth */, 1000000, /* max tables */)` which
441should be sufficient for most uses.
442
443## Text & schema parsing
444
445Using binary buffers with the generated header provides a super low
446overhead use of FlatBuffer data. There are, however, times when you want
447to use text formats, for example because it interacts better with source
448control, or you want to give your users easy access to data.
449
450Another reason might be that you already have a lot of data in JSON
451format, or a tool that generates JSON, and if you can write a schema for
452it, this will provide you an easy way to use that data directly.
453
454(see the schema documentation for some specifics on the JSON format
455accepted).
456
457Schema evolution compatibility for the JSON format follows the same rules as the binary format (JSON formatted data will be forwards/backwards compatible with schemas that evolve in a compatible way).
458
459There are two ways to use text formats:
460
461#### Using the compiler as a conversion tool
462
463This is the preferred path, as it doesn't require you to add any new
464code to your program, and is maximally efficient since you can ship with
465binary data. The disadvantage is that it is an extra step for your
466users/developers to perform, though you might be able to automate it.
467
468    flatc -b myschema.fbs mydata.json
469
470This will generate the binary file `mydata_wire.bin` which can be loaded
471as before.
472
473#### Making your program capable of loading text directly
474
475This gives you maximum flexibility. You could even opt to support both,
476i.e. check for both files, and regenerate the binary from text when
477required, otherwise just load the binary.
478
479This option is currently only available for C++, or Java through JNI.
480
481As mentioned in the section "Building" above, this technique requires
482you to link a few more files into your program, and you'll want to include
483`flatbuffers/idl.h`.
484
485Load text (either a schema or json) into an in-memory buffer (there is a
486convenient `LoadFile()` utility function in `flatbuffers/util.h` if you
487wish). Construct a parser:
488
489```cpp
490    flatbuffers::Parser parser;
491```
492
493Now you can parse any number of text files in sequence:
494
495```cpp
496    parser.Parse(text_file.c_str());
497```
498
499This works similarly to how the command-line compiler works: a sequence
500of files parsed by the same `Parser` object allow later files to
501reference definitions in earlier files. Typically this means you first
502load a schema file (which populates `Parser` with definitions), followed
503by one or more JSON files.
504
505As optional argument to `Parse`, you may specify a null-terminated list of
506include paths. If not specified, any include statements try to resolve from
507the current directory.
508
509If there were any parsing errors, `Parse` will return `false`, and
510`Parser::error_` contains a human readable error string with a line number
511etc, which you should present to the creator of that file.
512
513After each JSON file, the `Parser::fbb` member variable is the
514`FlatBufferBuilder` that contains the binary buffer version of that
515file, that you can access as described above.
516
517`samples/sample_text.cpp` is a code sample showing the above operations.
518
519## Threading
520
521Reading a FlatBuffer does not touch any memory outside the original buffer,
522and is entirely read-only (all const), so is safe to access from multiple
523threads even without synchronisation primitives.
524
525Creating a FlatBuffer is not thread safe. All state related to building
526a FlatBuffer is contained in a FlatBufferBuilder instance, and no memory
527outside of it is touched. To make this thread safe, either do not
528share instances of FlatBufferBuilder between threads (recommended), or
529manually wrap it in synchronisation primitives. There's no automatic way to
530accomplish this, by design, as we feel multithreaded construction
531of a single buffer will be rare, and synchronisation overhead would be costly.
532
533## Advanced union features
534
535The C++ implementation currently supports vectors of unions (i.e. you can
536declare a field as `[T]` where `T` is a union type instead of a table type). It
537also supports structs and strings in unions, besides tables.
538
539For an example of these features, see `tests/union_vector`, and
540`UnionVectorTest` in `test.cpp`.
541
542Since these features haven't been ported to other languages yet, if you
543choose to use them, you won't be able to use these buffers in other languages
544(`flatc` will refuse to compile a schema that uses these features).
545
546These features reduce the amount of "table wrapping" that was previously
547needed to use unions.
548
549To use scalars, simply wrap them in a struct.
550
551## Depth limit of nested objects and stack-overflow control
552The parser of Flatbuffers schema or json-files is kind of recursive parser.
553To avoid stack-overflow problem the parser has a built-in limiter of
554recursion depth. Number of nested declarations in a schema or number of
555nested json-objects is limited. By default, this depth limit set to `64`.
556It is possible to override this limit with `FLATBUFFERS_MAX_PARSING_DEPTH`
557definition. This definition can be helpful for testing purposes or embedded
558applications. For details see [build](../building.md) of
559CMake-based projects.
560
561## Dependence from C-locale {#flatbuffers_locale_cpp}
562The Flatbuffers [grammar](../grammar.md) uses ASCII
563character set for identifiers, alphanumeric literals, reserved words.
564
565Internal implementation of the Flatbuffers depends from functions which
566depend from C-locale: `strtod()` or `strtof()`, for example.
567The library expects the dot `.` symbol as the separator of an integer
568part from the fractional part of a float number.
569Another separator symbols (`,` for example) will break the compatibility
570and may lead to an error while parsing a Flatbuffers schema or a json file.
571
572The Standard C locale is a global resource, there is only one locale for
573the entire application. Some modern compilers and platforms have
574locale-independent or locale-narrow functions `strtof_l`, `strtod_l`,
575`strtoll_l`, `strtoull_l` to resolve this dependency.
576These functions use specified locale rather than the global or per-thread
577locale instead. They are part of POSIX-2008 but not part of the C/C++
578standard library, therefore, may be missing on some platforms.
579The Flatbuffers library try to detect these functions at configuration and
580compile time:
581- CMake `"CMakeLists.txt"`:
582  - Check existence of `strtol_l` and `strtod_l` in the `<stdlib.h>`.
583- Compile-time `"/include/base.h"`:
584  - `_MSC_VER >= 1900`: MSVC2012 or higher if build with MSVC.
585  - `_XOPEN_SOURCE>=700`: POSIX-2008 if build with GCC/Clang.
586
587After detection, the definition `FLATBUFFERS_LOCALE_INDEPENDENT` will be
588set to `0` or `1`.
589To override or stop this detection use CMake `-DFLATBUFFERS_LOCALE_INDEPENDENT={0|1}`
590or predefine `FLATBUFFERS_LOCALE_INDEPENDENT` symbol.
591
592To test the compatibility of the Flatbuffers library with
593a specific locale use the environment variable `FLATBUFFERS_TEST_LOCALE`:
594```sh
595>FLATBUFFERS_TEST_LOCALE="" ./flattests
596>FLATBUFFERS_TEST_LOCALE="ru_RU.CP1251" ./flattests
597```
598
599## Support of floating-point numbers
600The Flatbuffers library assumes that a C++ compiler and a CPU are
601compatible with the `IEEE-754` floating-point standard.
602The schema and json parser may fail if `fast-math` or `/fp:fast` mode is active.
603
604### Support of hexadecimal and special floating-point numbers
605According to the [grammar](../grammar.md) `fbs` and `json` files
606may use hexadecimal and special (`NaN`, `Inf`) floating-point literals.
607The Flatbuffers uses `strtof` and `strtod` functions to parse floating-point
608literals. The Flatbuffers library has a code to detect a compiler compatibility
609with the literals. If necessary conditions are met the preprocessor constant
610`FLATBUFFERS_HAS_NEW_STRTOD` will be set to `1`.
611The support of floating-point literals will be limited at compile time
612if `FLATBUFFERS_HAS_NEW_STRTOD` constant is less than `1`.
613In this case, schemas with hexadecimal or special literals cannot be used.
614
615### Comparison of floating-point NaN values
616The floating-point `NaN` (`not a number`) is special value which
617representing an undefined or unrepresentable value.
618`NaN` may be explicitly assigned to variables, typically as a representation
619for missing values or may be a result of a mathematical operation.
620The `IEEE-754` defines two kind of `NaNs`:
621- Quiet NaNs, or `qNaNs`.
622- Signaling NaNs, or `sNaNs`.
623
624According to the `IEEE-754`, a comparison with `NaN` always returns
625an unordered result even when compared with itself. As a result, a whole
626Flatbuffers object will be not equal to itself if has one or more `NaN`.
627Flatbuffers scalar fields that have the default value are not actually stored
628in the serialized data but are generated in code (see [Writing a schema](../schema.md)).
629Scalar fields with `NaN` defaults break this behavior.
630If a schema has a lot of `NaN` defaults the Flatbuffers can override
631the unordered comparison by the ordered: `(NaN==NaN)->true`.
632This ordered comparison is enabled when compiling a program with the symbol
633`FLATBUFFERS_NAN_DEFAULTS` defined.
634Additional computations added by `FLATBUFFERS_NAN_DEFAULTS` are very cheap
635if GCC or Clang used. These compilers have a compile-time implementation
636of `isnan` checking which MSVC does not.
637
638## gRPC
639
640### Before you get started
641
642Before diving into the FlatBuffers gRPC usage in C++, you should already be
643familiar with the following:
644
645- FlatBuffers as a serialization format
646- [gRPC](http://www.grpc.io/docs/) usage
647
648### Using the FlatBuffers gRPC C++ library
649
650NOTE: The examples below are also in the `grpc/samples/greeter` directory.
651
652We will illustrate usage with the following schema:
653
654```c++ title="grpc/samples/greeter/greeter.fbs"
655--8<-- "https://raw.githubusercontent.com/google/flatbuffers/refs/heads/master/grpc/samples/greeter/greeter.fbs"
656```
657
658When we run `flatc`, we pass in the `--grpc` option and generage an additional
659`greeter.grpc.fb.h` and `greeter.grpc.fb.cc`.
660
661Example server code looks like this:
662
663```c++ title="grpc/samples/greeter/server.cpp"
664--8<-- "https://raw.githubusercontent.com/google/flatbuffers/refs/heads/master/grpc/samples/greeter/server.cpp"
665```
666
667Example client code looks like this:
668
669```c++ title="grpc/samples/greeter/client.cpp"
670--8<-- "https://raw.githubusercontent.com/google/flatbuffers/refs/heads/master/grpc/samples/greeter/client.cpp"
671```
672
673