Add support for infinity and nan floating-point defaults (#530) (#538)
diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py
index ae16064..b572940 100755
--- a/generator/nanopb_generator.py
+++ b/generator/nanopb_generator.py
@@ -354,6 +354,7 @@
self.ctype = None
self.fixed_count = False
self.callback_datatype = field_options.callback_datatype
+ self.math_include_required = False
if field_options.type == nanopb_pb2.FT_INLINE:
# Before nanopb-0.3.8, fixed length bytes arrays were specified
@@ -487,6 +488,10 @@
else:
raise NotImplementedError(desc.type)
+ if self.default and self.pbtype in ['FLOAT', 'DOUBLE']:
+ if 'inf' in self.default or 'nan' in self.default:
+ self.math_include_required = True
+
def __lt__(self, other):
return self.tag < other.tag
@@ -584,11 +589,15 @@
inner_init = str(self.default) + 'ull'
elif self.pbtype in ['SFIXED64', 'INT64']:
inner_init = str(self.default) + 'll'
- elif self.pbtype == 'FLOAT':
+ elif self.pbtype in ['FLOAT', 'DOUBLE']:
inner_init = str(self.default)
- if not '.' in inner_init:
+ if 'inf' in inner_init:
+ inner_init = inner_init.replace('inf', 'INFINITY')
+ elif 'nan' in inner_init:
+ inner_init = inner_init.replace('nan', 'NAN')
+ elif (not '.' in inner_init) and self.pbtype == 'FLOAT':
inner_init += '.0f'
- else:
+ elif self.pbtype == 'FLOAT':
inner_init += 'f'
else:
inner_init = str(self.default)
@@ -964,6 +973,7 @@
self.fields = []
self.oneofs = {}
self.desc = desc
+ self.math_include_required = False
if message_options.msgid:
self.msgid = message_options.msgid
@@ -1017,6 +1027,8 @@
self.oneofs[f.oneof_index].add_field(field)
else:
self.fields.append(field)
+ if field.math_include_required:
+ self.math_include_required = True
if len(desc.extension_range) > 0:
field_options = get_nanopb_suboptions(desc, message_options, self.name + 'extensions')
@@ -1354,7 +1366,12 @@
self.fdesc = fdesc
self.file_options = file_options
self.dependencies = {}
+ self.math_include_required = False
self.parse()
+ for message in self.messages:
+ if message.math_include_required:
+ self.math_include_required = True
+ break
# Some of types used in this file probably come from the file itself.
# Thus it has implicit dependency on itself.
@@ -1489,6 +1506,8 @@
symbol = make_identifier(headername)
yield '#ifndef PB_%s_INCLUDED\n' % symbol
yield '#define PB_%s_INCLUDED\n' % symbol
+ if self.math_include_required:
+ yield '#include <math.h>\n'
try:
yield options.libformat % ('pb.h')
except TypeError:
diff --git a/tests/infinity_nan/SConscript b/tests/infinity_nan/SConscript
new file mode 100644
index 0000000..748719d
--- /dev/null
+++ b/tests/infinity_nan/SConscript
@@ -0,0 +1,8 @@
+# Build and run a basic test for floating point default values.
+
+Import("env")
+
+env.NanopbProto("floats")
+test = env.Program(["infinity_nan_test.c", "floats.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"])
+
+env.RunTest(test)
diff --git a/tests/infinity_nan/floats.proto b/tests/infinity_nan/floats.proto
new file mode 100644
index 0000000..7a5f40d
--- /dev/null
+++ b/tests/infinity_nan/floats.proto
@@ -0,0 +1,12 @@
+syntax = "proto2";
+
+message Floats {
+ optional float float_pos_inf = 1 [default = inf];
+ optional float float_neg_inf = 2 [default = -inf];
+ optional float float_pos_nan = 3 [default = nan];
+ optional float float_neg_nan = 4 [default = -nan];
+ optional double double_pos_inf = 5 [default = inf];
+ optional double double_neg_inf = 6 [default = -inf];
+ optional double double_pos_nan = 7 [default = nan];
+ optional double double_neg_nan = 8 [default = -nan];
+}
diff --git a/tests/infinity_nan/infinity_nan_test.c b/tests/infinity_nan/infinity_nan_test.c
new file mode 100644
index 0000000..ce06c62
--- /dev/null
+++ b/tests/infinity_nan/infinity_nan_test.c
@@ -0,0 +1,62 @@
+/* Tests for floating point default values +-infinity and nan. */
+#undef __STRICT_ANSI__
+#include <math.h>
+#include <pb_decode.h>
+#include "floats.pb.h"
+#include "unittests.h"
+
+bool check_floats(Floats *floats)
+{
+ int status = 0;
+
+ TEST(!floats->has_float_pos_inf);
+ TEST(isinf(floats->float_pos_inf));
+ TEST(!signbit(floats->float_pos_inf));
+
+ TEST(!floats->has_float_neg_inf);
+ TEST(isinf(floats->float_neg_inf));
+ TEST(signbit(floats->float_neg_inf));
+
+ TEST(!floats->has_float_pos_nan);
+ TEST(isnan(floats->float_pos_nan));
+
+ TEST(!floats->has_float_neg_nan);
+ TEST(isnan(floats->float_neg_nan));
+
+ TEST(!floats->has_double_pos_inf);
+ TEST(isinf(floats->double_pos_inf));
+ TEST(!signbit(floats->double_pos_inf));
+
+ TEST(!floats->has_double_neg_inf);
+ TEST(isinf(floats->double_neg_inf));
+ TEST(signbit(floats->double_neg_inf));
+
+ TEST(!floats->has_double_pos_nan);
+ TEST(isnan(floats->double_pos_nan));
+
+ TEST(!floats->has_double_neg_nan);
+ TEST(isnan(floats->double_neg_nan));
+
+ return status == 0;
+}
+
+int main()
+{
+ int status = 0;
+
+ {
+ Floats floats = Floats_init_default;
+ COMMENT("Checking init_default");
+ TEST(check_floats(&floats));
+ }
+
+ {
+ Floats floats = {0};
+ pb_istream_t stream = pb_istream_from_buffer(NULL, 0);
+ COMMENT("Check decoded");
+ TEST(pb_decode(&stream, Floats_fields, &floats));
+ TEST(check_floats(&floats));
+ }
+
+ return status;
+}