Add `NaN` and `Inf` defaults to the C++ generated code. (#5102)

* Add `NaN` and `Inf` defaults to the C++ generated code.

* Refactoring: add FloatConstantGenerator

* Refactoring-2:

- remove isnan checking for all float/double values
- add most probable implementation of virtual methods of FloatConstantGenerator

* Add conditional (FLATBUFFERS_NAN_DEFAULTS) isnan checking
diff --git a/appveyor.yml b/appveyor.yml
index f8e4e86..2cd22e9 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -13,8 +13,13 @@
 
   matrix:
     - CMAKE_VS_VERSION: "10 2010"
+      MONSTER_EXTRA: "skip"
+
     - CMAKE_VS_VERSION: "12 2013"
+      MONSTER_EXTRA: "skip"
+
     - CMAKE_VS_VERSION: "14 2015"
+      MONSTER_EXTRA: ""
 
 platform:
   - x86
@@ -25,6 +30,7 @@
   - Release
 
 before_build:
+  - set MONSTER_EXTRA=%MONSTER_EXTRA%
   - cmake -G"Visual Studio %CMAKE_VS_VERSION%"
   # This cuts down on a lot of noise generated by xamarin warnings.
   - del "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets"
diff --git a/include/flatbuffers/code_generators.h b/include/flatbuffers/code_generators.h
index d19002e..8077d30 100644
--- a/include/flatbuffers/code_generators.h
+++ b/include/flatbuffers/code_generators.h
@@ -130,6 +130,33 @@
                        std::string *code_ptr, const CommentConfig *config,
                        const char *prefix = "");
 
+class FloatConstantGenerator {
+ public:
+  virtual ~FloatConstantGenerator(){};
+  std::string GenFloatConstant(const FieldDef &field) const;
+
+ private:
+  virtual std::string Inf(double v) const = 0;
+  virtual std::string NaN(double v) const = 0;
+  virtual std::string Value(double v, const std::string &src) const {
+    (void)v;
+    return src;
+  }
+
+  virtual std::string Inf(float v) const {
+    return this->Inf(static_cast<double>(v));
+  }
+  virtual std::string NaN(float v) const {
+    return this->NaN(static_cast<double>(v));
+  }
+  virtual std::string Value(float v, const std::string &src) const {
+    return this->Value(static_cast<double>(v), src);
+  }
+
+  template<typename T>
+  std::string GenFloatConstantImpl(const FieldDef &field) const;
+};
+
 }  // namespace flatbuffers
 
 #endif  // FLATBUFFERS_CODE_GENERATORS_H_
diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h
index 2c41834..cb796b0 100644
--- a/include/flatbuffers/flatbuffers.h
+++ b/include/flatbuffers/flatbuffers.h
@@ -19,7 +19,25 @@
 
 #include "flatbuffers/base.h"
 
+#if defined(FLATBUFFERS_NAN_DEFAULTS)
+#include <cmath>
+#endif
+
 namespace flatbuffers {
+// Generic 'operator==' with conditional specialisations.
+template<typename T> inline bool IsTheSameAs(T e, T def) { return e == def; }
+
+#if defined(FLATBUFFERS_NAN_DEFAULTS) && \
+    (!defined(_MSC_VER) || _MSC_VER >= 1800)
+// Like `operator==(e, def)` with weak NaN if T=(float|double).
+template<> inline bool IsTheSameAs<float>(float e, float def) {
+  return (e == def) || (std::isnan(def) && std::isnan(e));
+}
+template<> inline bool IsTheSameAs<double>(double e, double def) {
+  return (e == def) || (std::isnan(def) && std::isnan(e));
+}
+#endif
+
 // Wrapper for uoffset_t to allow safe template specialization.
 // Value is allowed to be 0 to indicate a null object (see e.g. AddOffset).
 template<typename T> struct Offset {
@@ -1087,7 +1105,7 @@
   // Like PushElement, but additionally tracks the field this represents.
   template<typename T> void AddElement(voffset_t field, T e, T def) {
     // We don't serialize values equal to the default.
-    if (e == def && !force_defaults_) return;
+    if (IsTheSameAs(e, def) && !force_defaults_) return;
     auto off = PushElement(e);
     TrackField(field, off);
   }
@@ -2241,7 +2259,7 @@
 
   template<typename T> bool SetField(voffset_t field, T val, T def) {
     auto field_offset = GetOptionalFieldOffset(field);
-    if (!field_offset) return val == def;
+    if (!field_offset) return IsTheSameAs(val, def);
     WriteScalar(data_ + field_offset, val);
     return true;
   }
diff --git a/src/code_generators.cpp b/src/code_generators.cpp
index 2ecd5e3..b78b5ca 100644
--- a/src/code_generators.cpp
+++ b/src/code_generators.cpp
@@ -19,6 +19,8 @@
 #include "flatbuffers/base.h"
 #include "flatbuffers/util.h"
 
+#include <cmath>
+
 #if defined(_MSC_VER)
 #  pragma warning(push)
 #  pragma warning(disable : 4127)  // C4127: conditional expression is constant
@@ -157,6 +159,35 @@
   }
 }
 
+template<typename T>
+std::string FloatConstantGenerator::GenFloatConstantImpl(
+    const FieldDef &field) const {
+  const auto &constant = field.value.constant;
+  T v;
+  auto done = StringToNumber(constant.c_str(), &v);
+  FLATBUFFERS_ASSERT(done);
+  if (done) {
+#if (!defined(_MSC_VER) || (_MSC_VER >= 1800))
+    if (std::isnan(v)) return NaN(v);
+    if (std::isinf(v)) return Inf(v);
+#endif
+    return Value(v, constant);
+  }
+  return "#";  // compile time error
+}
+
+std::string FloatConstantGenerator::GenFloatConstant(
+    const FieldDef &field) const {
+  switch (field.value.type.base_type) {
+    case BASE_TYPE_FLOAT: return GenFloatConstantImpl<float>(field);
+    case BASE_TYPE_DOUBLE: return GenFloatConstantImpl<double>(field);
+    default: {
+      FLATBUFFERS_ASSERT(false);
+      return "INVALID_BASE_TYPE";
+    }
+  };
+}
+
 }  // namespace flatbuffers
 
 #if defined(_MSC_VER)
diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp
index f91f14e..06896d9 100644
--- a/src/idl_gen_cpp.cpp
+++ b/src/idl_gen_cpp.cpp
@@ -34,6 +34,44 @@
 }
 
 namespace cpp {
+class CppFloatConstantGenerator : public FloatConstantGenerator {
+ protected:
+  std::string Value(double v,
+                    const std::string &src) const FLATBUFFERS_OVERRIDE {
+    (void)v;
+    return src;
+  };
+
+  std::string Value(float v,
+                    const std::string &src) const FLATBUFFERS_OVERRIDE {
+    (void)v;
+    return src + "f";
+  }
+
+  std::string NaN(double v) const FLATBUFFERS_OVERRIDE {
+    (void)v;
+    return "std::numeric_limits<double>::quiet_NaN()";
+  }
+  std::string NaN(float v) const FLATBUFFERS_OVERRIDE {
+    (void)v;
+    return "std::numeric_limits<float>::quiet_NaN()";
+  }
+
+  std::string Inf(double v) const FLATBUFFERS_OVERRIDE {
+    if(v < 0)
+      return "-std::numeric_limits<double>::infinity()";
+    else
+      return "std::numeric_limits<double>::infinity()";
+  }
+
+  std::string Inf(float v) const FLATBUFFERS_OVERRIDE {
+    if (v < 0)
+      return "-std::numeric_limits<float>::infinity()";
+    else
+      return "std::numeric_limits<float>::infinity()";
+  }
+};
+
 class CppGenerator : public BaseGenerator {
  public:
   CppGenerator(const Parser &parser, const std::string &path,
@@ -1392,9 +1430,10 @@
   }
 
   std::string GenDefaultConstant(const FieldDef &field) {
-    return field.value.type.base_type == BASE_TYPE_FLOAT
-               ? field.value.constant + "f"
-               : field.value.constant;
+    if(IsFloat(field.value.type.base_type))
+      return float_const_gen_.GenFloatConstant(field);
+    else
+      return field.value.constant;
   }
 
   std::string GetDefaultScalarValue(const FieldDef &field, bool is_ctor) {
@@ -2745,6 +2784,8 @@
 
     cur_name_space_ = ns;
   }
+
+  const CppFloatConstantGenerator float_const_gen_;
 };
 
 }  // namespace cpp
diff --git a/tests/generate_code.bat b/tests/generate_code.bat
index 304fece..d2e8364 100644
--- a/tests/generate_code.bat
+++ b/tests/generate_code.bat
@@ -20,6 +20,14 @@
 ..\%buildtype%\flatc.exe --cpp --js --ts --php --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs || goto FAIL
 ..\%buildtype%\flatc.exe -b --schema --bfbs-comments --bfbs-builtins -I include_test monster_test.fbs || goto FAIL
 ..\%buildtype%\flatc.exe --jsonschema --schema -I include_test monster_test.fbs || goto FAIL
+
+IF NOT "%MONSTER_EXTRA%"=="skip" (
+  @echo Generate MosterExtra
+  ..\%buildtype%\flatc.exe --cpp --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes monster_extra.fbs || goto FAIL
+) else (
+  @echo monster_extra.fbs skipped (the strtod function from MSVC2013 or older doesn't support NaN/Inf arguments)
+)
+
 cd ../samples
 ..\%buildtype%\flatc.exe --cpp --lobster --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr monster.fbs || goto FAIL
 ..\%buildtype%\flatc.exe -b --schema --bfbs-comments --bfbs-builtins monster.fbs || goto FAIL
diff --git a/tests/generate_code.sh b/tests/generate_code.sh
index d5ade21..6a3a8f8 100755
--- a/tests/generate_code.sh
+++ b/tests/generate_code.sh
@@ -20,6 +20,7 @@
 ../flatc --cpp --js --ts --php --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs
 ../flatc -b --schema --bfbs-comments --bfbs-builtins -I include_test monster_test.fbs
 ../flatc --jsonschema --schema -I include_test monster_test.fbs
+../flatc --cpp --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes monster_extra.fbs || goto FAIL
 cd ../samples
 ../flatc --cpp --lobster --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr monster.fbs
 ../flatc -b --schema --bfbs-comments --bfbs-builtins monster.fbs
diff --git a/tests/monster_extra.fbs b/tests/monster_extra.fbs
new file mode 100644
index 0000000..2518f5b
--- /dev/null
+++ b/tests/monster_extra.fbs
@@ -0,0 +1,12 @@
+namespace MyGame;
+
+// Not all programmining languages support this extra table.
+table MonsterExra {
+  // Float-point values with NaN and Inf defaults.
+  testf_nan:float = nan;
+  testf_pinf:float = +inf;
+  testf_ninf:float = -inf;
+  testd_nan:double = nan;
+  testd_pinf:double = +inf;
+  testd_ninf:double = -inf;
+}
diff --git a/tests/monster_extra_generated.h b/tests/monster_extra_generated.h
new file mode 100644
index 0000000..0dad3b0
--- /dev/null
+++ b/tests/monster_extra_generated.h
@@ -0,0 +1,229 @@
+// automatically generated by the FlatBuffers compiler, do not modify
+
+
+#ifndef FLATBUFFERS_GENERATED_MONSTEREXTRA_MYGAME_H_
+#define FLATBUFFERS_GENERATED_MONSTEREXTRA_MYGAME_H_
+
+#include "flatbuffers/flatbuffers.h"
+
+namespace MyGame {
+
+struct MonsterExra;
+struct MonsterExraT;
+
+bool operator==(const MonsterExraT &lhs, const MonsterExraT &rhs);
+
+inline const flatbuffers::TypeTable *MonsterExraTypeTable();
+
+struct MonsterExraT : public flatbuffers::NativeTable {
+  typedef MonsterExra TableType;
+  float testf_nan;
+  float testf_pinf;
+  float testf_ninf;
+  double testd_nan;
+  double testd_pinf;
+  double testd_ninf;
+  MonsterExraT()
+      : testf_nan(std::numeric_limits<float>::quiet_NaN()),
+        testf_pinf(std::numeric_limits<float>::infinity()),
+        testf_ninf(-std::numeric_limits<float>::infinity()),
+        testd_nan(std::numeric_limits<double>::quiet_NaN()),
+        testd_pinf(std::numeric_limits<double>::infinity()),
+        testd_ninf(-std::numeric_limits<double>::infinity()) {
+  }
+};
+
+inline bool operator==(const MonsterExraT &lhs, const MonsterExraT &rhs) {
+  return
+      (lhs.testf_nan == rhs.testf_nan) &&
+      (lhs.testf_pinf == rhs.testf_pinf) &&
+      (lhs.testf_ninf == rhs.testf_ninf) &&
+      (lhs.testd_nan == rhs.testd_nan) &&
+      (lhs.testd_pinf == rhs.testd_pinf) &&
+      (lhs.testd_ninf == rhs.testd_ninf);
+}
+
+struct MonsterExra FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef MonsterExraT NativeTableType;
+  static const flatbuffers::TypeTable *MiniReflectTypeTable() {
+    return MonsterExraTypeTable();
+  }
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_TESTF_NAN = 4,
+    VT_TESTF_PINF = 6,
+    VT_TESTF_NINF = 8,
+    VT_TESTD_NAN = 10,
+    VT_TESTD_PINF = 12,
+    VT_TESTD_NINF = 14
+  };
+  float testf_nan() const {
+    return GetField<float>(VT_TESTF_NAN, std::numeric_limits<float>::quiet_NaN());
+  }
+  bool mutate_testf_nan(float _testf_nan) {
+    return SetField<float>(VT_TESTF_NAN, _testf_nan, std::numeric_limits<float>::quiet_NaN());
+  }
+  float testf_pinf() const {
+    return GetField<float>(VT_TESTF_PINF, std::numeric_limits<float>::infinity());
+  }
+  bool mutate_testf_pinf(float _testf_pinf) {
+    return SetField<float>(VT_TESTF_PINF, _testf_pinf, std::numeric_limits<float>::infinity());
+  }
+  float testf_ninf() const {
+    return GetField<float>(VT_TESTF_NINF, -std::numeric_limits<float>::infinity());
+  }
+  bool mutate_testf_ninf(float _testf_ninf) {
+    return SetField<float>(VT_TESTF_NINF, _testf_ninf, -std::numeric_limits<float>::infinity());
+  }
+  double testd_nan() const {
+    return GetField<double>(VT_TESTD_NAN, std::numeric_limits<double>::quiet_NaN());
+  }
+  bool mutate_testd_nan(double _testd_nan) {
+    return SetField<double>(VT_TESTD_NAN, _testd_nan, std::numeric_limits<double>::quiet_NaN());
+  }
+  double testd_pinf() const {
+    return GetField<double>(VT_TESTD_PINF, std::numeric_limits<double>::infinity());
+  }
+  bool mutate_testd_pinf(double _testd_pinf) {
+    return SetField<double>(VT_TESTD_PINF, _testd_pinf, std::numeric_limits<double>::infinity());
+  }
+  double testd_ninf() const {
+    return GetField<double>(VT_TESTD_NINF, -std::numeric_limits<double>::infinity());
+  }
+  bool mutate_testd_ninf(double _testd_ninf) {
+    return SetField<double>(VT_TESTD_NINF, _testd_ninf, -std::numeric_limits<double>::infinity());
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<float>(verifier, VT_TESTF_NAN) &&
+           VerifyField<float>(verifier, VT_TESTF_PINF) &&
+           VerifyField<float>(verifier, VT_TESTF_NINF) &&
+           VerifyField<double>(verifier, VT_TESTD_NAN) &&
+           VerifyField<double>(verifier, VT_TESTD_PINF) &&
+           VerifyField<double>(verifier, VT_TESTD_NINF) &&
+           verifier.EndTable();
+  }
+  MonsterExraT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  void UnPackTo(MonsterExraT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const;
+  static flatbuffers::Offset<MonsterExra> Pack(flatbuffers::FlatBufferBuilder &_fbb, const MonsterExraT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+};
+
+struct MonsterExraBuilder {
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_testf_nan(float testf_nan) {
+    fbb_.AddElement<float>(MonsterExra::VT_TESTF_NAN, testf_nan, std::numeric_limits<float>::quiet_NaN());
+  }
+  void add_testf_pinf(float testf_pinf) {
+    fbb_.AddElement<float>(MonsterExra::VT_TESTF_PINF, testf_pinf, std::numeric_limits<float>::infinity());
+  }
+  void add_testf_ninf(float testf_ninf) {
+    fbb_.AddElement<float>(MonsterExra::VT_TESTF_NINF, testf_ninf, -std::numeric_limits<float>::infinity());
+  }
+  void add_testd_nan(double testd_nan) {
+    fbb_.AddElement<double>(MonsterExra::VT_TESTD_NAN, testd_nan, std::numeric_limits<double>::quiet_NaN());
+  }
+  void add_testd_pinf(double testd_pinf) {
+    fbb_.AddElement<double>(MonsterExra::VT_TESTD_PINF, testd_pinf, std::numeric_limits<double>::infinity());
+  }
+  void add_testd_ninf(double testd_ninf) {
+    fbb_.AddElement<double>(MonsterExra::VT_TESTD_NINF, testd_ninf, -std::numeric_limits<double>::infinity());
+  }
+  explicit MonsterExraBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  MonsterExraBuilder &operator=(const MonsterExraBuilder &);
+  flatbuffers::Offset<MonsterExra> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<MonsterExra>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<MonsterExra> CreateMonsterExra(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    float testf_nan = std::numeric_limits<float>::quiet_NaN(),
+    float testf_pinf = std::numeric_limits<float>::infinity(),
+    float testf_ninf = -std::numeric_limits<float>::infinity(),
+    double testd_nan = std::numeric_limits<double>::quiet_NaN(),
+    double testd_pinf = std::numeric_limits<double>::infinity(),
+    double testd_ninf = -std::numeric_limits<double>::infinity()) {
+  MonsterExraBuilder builder_(_fbb);
+  builder_.add_testd_ninf(testd_ninf);
+  builder_.add_testd_pinf(testd_pinf);
+  builder_.add_testd_nan(testd_nan);
+  builder_.add_testf_ninf(testf_ninf);
+  builder_.add_testf_pinf(testf_pinf);
+  builder_.add_testf_nan(testf_nan);
+  return builder_.Finish();
+}
+
+flatbuffers::Offset<MonsterExra> CreateMonsterExra(flatbuffers::FlatBufferBuilder &_fbb, const MonsterExraT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr);
+
+inline MonsterExraT *MonsterExra::UnPack(const flatbuffers::resolver_function_t *_resolver) const {
+  auto _o = new MonsterExraT();
+  UnPackTo(_o, _resolver);
+  return _o;
+}
+
+inline void MonsterExra::UnPackTo(MonsterExraT *_o, const flatbuffers::resolver_function_t *_resolver) const {
+  (void)_o;
+  (void)_resolver;
+  { auto _e = testf_nan(); _o->testf_nan = _e; };
+  { auto _e = testf_pinf(); _o->testf_pinf = _e; };
+  { auto _e = testf_ninf(); _o->testf_ninf = _e; };
+  { auto _e = testd_nan(); _o->testd_nan = _e; };
+  { auto _e = testd_pinf(); _o->testd_pinf = _e; };
+  { auto _e = testd_ninf(); _o->testd_ninf = _e; };
+}
+
+inline flatbuffers::Offset<MonsterExra> MonsterExra::Pack(flatbuffers::FlatBufferBuilder &_fbb, const MonsterExraT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
+  return CreateMonsterExra(_fbb, _o, _rehasher);
+}
+
+inline flatbuffers::Offset<MonsterExra> CreateMonsterExra(flatbuffers::FlatBufferBuilder &_fbb, const MonsterExraT *_o, const flatbuffers::rehasher_function_t *_rehasher) {
+  (void)_rehasher;
+  (void)_o;
+  struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const MonsterExraT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
+  auto _testf_nan = _o->testf_nan;
+  auto _testf_pinf = _o->testf_pinf;
+  auto _testf_ninf = _o->testf_ninf;
+  auto _testd_nan = _o->testd_nan;
+  auto _testd_pinf = _o->testd_pinf;
+  auto _testd_ninf = _o->testd_ninf;
+  return MyGame::CreateMonsterExra(
+      _fbb,
+      _testf_nan,
+      _testf_pinf,
+      _testf_ninf,
+      _testd_nan,
+      _testd_pinf,
+      _testd_ninf);
+}
+
+inline const flatbuffers::TypeTable *MonsterExraTypeTable() {
+  static const flatbuffers::TypeCode type_codes[] = {
+    { flatbuffers::ET_FLOAT, 0, -1 },
+    { flatbuffers::ET_FLOAT, 0, -1 },
+    { flatbuffers::ET_FLOAT, 0, -1 },
+    { flatbuffers::ET_DOUBLE, 0, -1 },
+    { flatbuffers::ET_DOUBLE, 0, -1 },
+    { flatbuffers::ET_DOUBLE, 0, -1 }
+  };
+  static const char * const names[] = {
+    "testf_nan",
+    "testf_pinf",
+    "testf_ninf",
+    "testd_nan",
+    "testd_pinf",
+    "testd_ninf"
+  };
+  static const flatbuffers::TypeTable tt = {
+    flatbuffers::ST_TABLE, 6, type_codes, nullptr, nullptr, names
+  };
+  return &tt;
+}
+
+}  // namespace MyGame
+
+#endif  // FLATBUFFERS_GENERATED_MONSTEREXTRA_MYGAME_H_
diff --git a/tests/test.cpp b/tests/test.cpp
index 656849a..76d3efe 100644
--- a/tests/test.cpp
+++ b/tests/test.cpp
@@ -33,6 +33,7 @@
 #include "namespace_test/namespace_test1_generated.h"
 #include "namespace_test/namespace_test2_generated.h"
 #include "union_vector/union_vector_generated.h"
+#include "monster_extra_generated.h"
 #include "test_assert.h"
 
 #include "flatbuffers/flexbuffers.h"