Skip to content

Commit 047e24f

Browse files
committed
Refactors and support for constexpr serialization.
- Remove total order of fields test for unbounded logical buffers; it is not possible to do this in standards-compliant way at this time. Replace with an explicit tag to enable unbounded operation. - Clean up member pointer utilities to support constexpr contexts. - Add constexpr serialization tests. - Disable test coverage analysis by default in the makefile. - Deprecate NOP_EXTERNAL_TEMPLATE, NOP_EXTERNAL_STRUCTURE can now handle un-elaborated template types as well.
1 parent e917fe0 commit 047e24f

File tree

11 files changed

+418
-282
lines changed

11 files changed

+418
-282
lines changed

Makefile

+10-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ TOOLCHAIN ?=
66

77
HOST_OS := $(shell uname -s)
88

9+
WITH_COVERAGE ?= false
10+
911
# Location of gtest in case it's not installed in a default path for the compiler.
1012
GTEST_INSTALL ?= /opt/local
1113
GTEST_LIB ?= $(GTEST_INSTALL)/lib
@@ -42,7 +44,7 @@ else
4244

4345
# Build tests if gtest is found.
4446
M_NAME := test
45-
M_CFLAGS := -I$(GTEST_INCLUDE) --coverage -O0 -g
47+
M_CFLAGS := -I$(GTEST_INCLUDE) -O0 -g
4648
M_LDFLAGS := -L$(GTEST_LIB) -lgtest -lgmock
4749
M_OBJS := \
4850
test/nop_tests.o \
@@ -59,9 +61,15 @@ M_OBJS := \
5961
test/optional_tests.o \
6062
test/result_tests.o \
6163
test/endian_tests.o \
64+
test/constexpr_tests.o \
65+
66+
ifeq ($(WITH_COVERAGE),true)
67+
M_CFLAGS += --coverage
68+
endif
6269

6370
include build/host-executable.mk
6471

72+
ifeq ($(WITH_COVERAGE),true)
6573
# Generate coverage report with lcov and genhtml. A bit hacky but works okay.
6674
$(OUT)/coverage.info: $(OUT)/test
6775
$(QUIET)find $(OUT) -name "*.gcda" -exec rm {} \+
@@ -71,6 +79,7 @@ $(OUT)/coverage.info: $(OUT)/test
7179
genhtml -o $(OUT)/coverage $@
7280

7381
coverage:: $(OUT)/coverage.info
82+
endif
7483

7584
endif
7685

docs/getting-started.md

+3-5
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ There are two ways to annotate externally-defined template types:
350350
template type. This method can handle both type and non-type template
351351
arguments, but is less flexible because you must decide which arguments to
352352
use at annotation time.
353-
* Use `NOP_EXTERNAL_TEMPLATE()` to annotate the base template type name. This
353+
* Use `NOP_EXTERNAL_STRUCTURE()` to annotate the base template type name. This
354354
method is more flexible in that the external template type may be annotated
355355
once and then any instantiation is automatically recognized by the
356356
serializer at the site of use. The down side is that non-type template
@@ -397,10 +397,8 @@ namespace foo {
397397
NOP_EXTERNAL_STRUCTURE(SimpleType<std::uint32_t>, data);
398398
NOP_EXTERNAL_STRUCTURE(SimpleType<std::string>, data);
399399

400-
// OR
401-
402400
// Example of annotating the base template type name:
403-
NOP_EXTERNAL_TEMPLATE(SimpleType, data);
401+
NOP_EXTERNAL_STRUCTURE(SimpleType, data);
404402

405403
// ... code to read and write SimpleType ...
406404

@@ -578,7 +576,7 @@ The following are examples of value wrappers:
578576
579577
// Simple template type that stores floating point values as fixed point
580578
// integer with the specified number of fractional bits. This type has
581-
the same wire format as Integer.
579+
// the same wire format as Integer.
582580
template <typename Integer, std::size_t FractionalBits_>
583581
class Fixed {
584582
public:

examples/shared.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ struct CPolyhedron {
7474
size_t size;
7575
Triangle triangles[1];
7676
NOP_VALUE(CPolyhedron, (triangles, size));
77+
NOP_UNBOUNDED_BUFFER(CPolyhedron);
7778
};
7879

7980
// Assert that Polyhedron and CPolyhedron have compatible wire formats.

include/nop/base/logical_buffer.h

+21-120
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
#include <array>
2121
#include <cstddef>
2222
#include <limits>
23-
#include <numeric>
2423
#include <type_traits>
2524

2625
#include <nop/base/encoding.h>
@@ -80,60 +79,29 @@
8079

8180
namespace nop {
8281

83-
// Common glue shared by integral and non-integral LogicalBuffer encodings.
84-
template <typename T>
85-
struct LogicalBufferCommon {
86-
template <typename Writer>
87-
static constexpr Status<void> WriteUnbounded(const T& value, Writer* writer) {
88-
EncodingByte prefix = Encoding<T>::Prefix(value);
89-
auto status = writer->Write(static_cast<std::uint8_t>(prefix));
90-
if (!status)
91-
return status;
92-
else
93-
return Encoding<T>::WriteUnboundedPayload(prefix, value, writer);
94-
}
95-
96-
template <typename Reader>
97-
static constexpr Status<void> ReadUnbounded(T* value, Reader* reader) {
98-
std::uint8_t prefix_byte = 0;
99-
auto status = reader->Read(&prefix_byte);
100-
if (!status)
101-
return status;
102-
103-
const EncodingByte prefix = static_cast<EncodingByte>(prefix_byte);
104-
if (Encoding<T>::Match(prefix))
105-
return Encoding<T>::ReadUnboundedPayload(prefix, value, reader);
106-
else
107-
return ErrorStatus::UnexpectedEncodingType;
108-
}
109-
};
110-
11182
// Encoding type that handles non-integral element types. Logical buffers of
11283
// non-integral element types are encoded the same as non-integral arrays using
11384
// the ARRAY encoding.
114-
template <typename BufferType, typename SizeType>
85+
template <typename BufferType, typename SizeType, bool IsUnbounded>
11586
struct Encoding<
116-
LogicalBuffer<BufferType, SizeType>,
87+
LogicalBuffer<BufferType, SizeType, IsUnbounded>,
11788
EnableIfNotIntegral<typename ArrayTraits<BufferType>::ElementType>>
118-
: EncodingIO<LogicalBuffer<BufferType, SizeType>>,
119-
LogicalBufferCommon<LogicalBuffer<BufferType, SizeType>> {
120-
using Type = LogicalBuffer<BufferType, SizeType>;
121-
using ValueType =
122-
std::remove_const_t<typename ArrayTraits<BufferType>::ElementType>;
123-
enum : std::size_t { Length = ArrayTraits<BufferType>::Length };
89+
: EncodingIO<LogicalBuffer<BufferType, SizeType, IsUnbounded>> {
90+
using Type = LogicalBuffer<BufferType, SizeType, IsUnbounded>;
91+
using ValueType = std::remove_const_t<typename Type::ValueType>;
92+
enum : std::size_t { Length = Type::Length };
12493

12594
static constexpr EncodingByte Prefix(const Type& /*value*/) {
12695
return EncodingByte::Array;
12796
}
12897

12998
static constexpr std::size_t Size(const Type& value) {
99+
std::size_t element_size_sum = 0;
100+
for (const ValueType& element : value)
101+
element_size_sum += Encoding<ValueType>::Size(element);
102+
130103
return BaseEncodingSize(Prefix(value)) +
131-
Encoding<SizeType>::Size(value.size()) +
132-
std::accumulate(
133-
std::begin(value), std::end(value), 0U,
134-
[](const std::size_t& sum, const ValueType& element) {
135-
return sum + Encoding<ValueType>::Size(element);
136-
});
104+
Encoding<SizeType>::Size(value.size()) + element_size_sum;
137105
}
138106

139107
static constexpr bool Match(EncodingByte prefix) {
@@ -145,7 +113,7 @@ struct Encoding<
145113
const Type& value,
146114
Writer* writer) {
147115
const SizeType size = static_cast<SizeType>(value.size());
148-
if (size > Length)
116+
if (!IsUnbounded && size > Length)
149117
return ErrorStatus::InvalidContainerLength;
150118

151119
auto status = Encoding<SizeType>::Write(size, writer);
@@ -161,32 +129,14 @@ struct Encoding<
161129
return {};
162130
}
163131

164-
template <typename Writer>
165-
static constexpr Status<void> WriteUnboundedPayload(EncodingByte /*prefix*/,
166-
const Type& value,
167-
Writer* writer) {
168-
const SizeType size = static_cast<SizeType>(value.size());
169-
auto status = Encoding<SizeType>::Write(size, writer);
170-
if (!status)
171-
return status;
172-
173-
for (SizeType i = 0; i < size; i++) {
174-
status = Encoding<ValueType>::Write(value[i], writer);
175-
if (!status)
176-
return status;
177-
}
178-
179-
return {};
180-
}
181-
182132
template <typename Reader>
183133
static constexpr Status<void> ReadPayload(EncodingByte /*prefix*/,
184134
Type* value, Reader* reader) {
185135
SizeType size;
186136
auto status = Encoding<SizeType>::Read(&size, reader);
187137
if (!status)
188138
return status;
189-
else if (size > Length)
139+
else if (!IsUnbounded && size > Length)
190140
return ErrorStatus::InvalidContainerLength;
191141

192142
for (SizeType i = 0; i < size; i++) {
@@ -198,39 +148,18 @@ struct Encoding<
198148
value->size() = size;
199149
return {};
200150
}
201-
202-
template <typename Reader>
203-
static constexpr Status<void> ReadUnboundedPayload(EncodingByte /*prefix*/,
204-
Type* value,
205-
Reader* reader) {
206-
SizeType size;
207-
auto status = Encoding<SizeType>::Read(&size, reader);
208-
if (!status)
209-
return status;
210-
211-
for (SizeType i = 0; i < size; i++) {
212-
status = Encoding<ValueType>::Read(&(*value)[i], reader);
213-
if (!status)
214-
return status;
215-
}
216-
217-
value->size() = size;
218-
return {};
219-
}
220151
};
221152

222153
// Encoding type that handles integral element types. Logical buffers of
223154
// integral element types are encoded the same as arrays with integral elements
224155
// using the BINARY encoding.
225-
template <typename BufferType, typename SizeType>
226-
struct Encoding<LogicalBuffer<BufferType, SizeType>,
156+
template <typename BufferType, typename SizeType, bool IsUnbounded>
157+
struct Encoding<LogicalBuffer<BufferType, SizeType, IsUnbounded>,
227158
EnableIfIntegral<typename ArrayTraits<BufferType>::ElementType>>
228-
: EncodingIO<LogicalBuffer<BufferType, SizeType>>,
229-
LogicalBufferCommon<LogicalBuffer<BufferType, SizeType>> {
230-
using Type = LogicalBuffer<BufferType, SizeType>;
231-
using ValueType =
232-
std::remove_const_t<typename ArrayTraits<BufferType>::ElementType>;
233-
enum : std::size_t { Length = ArrayTraits<BufferType>::Length };
159+
: EncodingIO<LogicalBuffer<BufferType, SizeType, IsUnbounded>> {
160+
using Type = LogicalBuffer<BufferType, SizeType, IsUnbounded>;
161+
using ValueType = std::remove_const_t<typename Type::ValueType>;
162+
enum : std::size_t { Length = Type::Length };
234163

235164
static constexpr EncodingByte Prefix(const Type& /*value*/) {
236165
return EncodingByte::Binary;
@@ -251,7 +180,7 @@ struct Encoding<LogicalBuffer<BufferType, SizeType>,
251180
const Type& value,
252181
Writer* writer) {
253182
const SizeType size = value.size();
254-
if (size > Length)
183+
if (!IsUnbounded && size > Length)
255184
return ErrorStatus::InvalidContainerLength;
256185

257186
auto status = Encoding<SizeType>::Write(size * sizeof(ValueType), writer);
@@ -261,26 +190,14 @@ struct Encoding<LogicalBuffer<BufferType, SizeType>,
261190
return writer->Write(value.begin(), value.end());
262191
}
263192

264-
template <typename Writer>
265-
static constexpr Status<void> WriteUnboundedPayload(EncodingByte /*prefix*/,
266-
const Type& value,
267-
Writer* writer) {
268-
const SizeType size = value.size();
269-
auto status = Encoding<SizeType>::Write(size * sizeof(ValueType), writer);
270-
if (!status)
271-
return status;
272-
273-
return writer->Write(value.begin(), value.end());
274-
}
275-
276193
template <typename Reader>
277194
static constexpr Status<void> ReadPayload(EncodingByte /*prefix*/,
278195
Type* value, Reader* reader) {
279196
SizeType size_bytes = 0;
280197
auto status = Encoding<SizeType>::Read(&size_bytes, reader);
281198
if (!status) {
282199
return status;
283-
} else if (size_bytes > Length * sizeof(ValueType) ||
200+
} else if ((!IsUnbounded && size_bytes > Length * sizeof(ValueType)) ||
284201
size_bytes % sizeof(ValueType) != 0) {
285202
return ErrorStatus::InvalidContainerLength;
286203
}
@@ -289,22 +206,6 @@ struct Encoding<LogicalBuffer<BufferType, SizeType>,
289206
value->size() = size;
290207
return reader->Read(value->begin(), value->end());
291208
}
292-
293-
template <typename Reader>
294-
static constexpr Status<void> ReadUnboundedPayload(EncodingByte /*prefix*/,
295-
Type* value,
296-
Reader* reader) {
297-
SizeType size_bytes = 0;
298-
auto status = Encoding<SizeType>::Read(&size_bytes, reader);
299-
if (!status)
300-
return status;
301-
else if (size_bytes % sizeof(ValueType) != 0)
302-
return ErrorStatus::InvalidContainerLength;
303-
304-
const SizeType size = size_bytes / sizeof(ValueType);
305-
value->size() = size;
306-
return reader->Read(value->begin(), value->end());
307-
}
308209
};
309210

310211
} // namespace nop

include/nop/base/utility.h

+19-6
Original file line numberDiff line numberDiff line change
@@ -89,28 +89,32 @@ using First = typename FirstType<Ts...>::Type;
8989

9090
// Determines the value type and extent of C/C++ array types.
9191
template <typename T>
92-
struct ArrayTraits {
93-
static_assert(sizeof(T) != sizeof(T), "Unsupported array type.");
94-
};
92+
struct ArrayTraits : std::false_type {};
93+
9594
template <typename T, std::size_t Length_>
96-
struct ArrayTraits<T[Length_]> {
95+
struct ArrayTraits<T[Length_]> : std::true_type {
9796
enum : std::size_t { Length = Length_ };
9897
using ElementType = T;
9998
using Type = T[Length];
10099
};
101100
template <typename T, std::size_t Length_>
102-
struct ArrayTraits<std::array<T, Length_>> {
101+
struct ArrayTraits<std::array<T, Length_>> : std::true_type {
103102
enum : std::size_t { Length = Length_ };
104103
using ElementType = T;
105104
using Type = std::array<T, Length>;
106105
};
107106
template <typename T, std::size_t Length_>
108-
struct ArrayTraits<const std::array<T, Length_>> {
107+
struct ArrayTraits<const std::array<T, Length_>> : std::true_type {
109108
enum : std::size_t { Length = Length_ };
110109
using ElementType = T;
111110
using Type = const std::array<T, Length>;
112111
};
113112

113+
// Enable if T is an array type.
114+
template <typename T, typename Return = void>
115+
using EnableIfArray =
116+
typename std::enable_if<ArrayTraits<T>::value, Return>::type;
117+
114118
// Logical AND over template parameter pack.
115119
template <typename... T>
116120
struct And : std::true_type {};
@@ -175,6 +179,15 @@ template <template <typename, typename> class Same, typename First,
175179
typename... Rest>
176180
struct IsSame<Same, First, Rest...> : And<Same<First, Rest>...> {};
177181

182+
// Utility types for SFINAE expression matching either regular or template
183+
// types.
184+
template <typename T, typename U>
185+
std::is_same<T, U> MatchType();
186+
187+
template <template <typename...> class TT, template <typename...> class UU,
188+
typename... Ts>
189+
std::is_same<TT<Ts...>, UU<Ts...>> MatchTemplate();
190+
178191
} // namespace nop
179192

180193
#endif // LIBNOP_INCLUDE_NOP_BASE_UTILITY_H_

0 commit comments

Comments
 (0)