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