Skip to content

Commit 9f99c7d

Browse files
committed
mpgen: support primitive std::optional struct fields
Currently optional primitive fields like `std::optional<int>` are not well supported as struct members. Non-primitive optional fields like `std::optional<std::string>` and optional struct fields are well-supported because Cap'n Proto allows non-primitive fields to be unset, but primitive fields are always considered set so there is natural way to represent null values. Libmultiprocess does already support primitive optional method parameters and result values, by allowing the .capnp files to declare extra boolean parameters prefixed with "has" and treating the extra boolean parameters as indicators of whether options are set or unset. This commit just this functionality to work for struct members as well. For example a C++ `std::optional<int> param` parameter can be represented by 'param :Int32, hasParam :Bool` parameters in a .capnp file and libmultiprocess will use both Cap'n Proto fields together to represent the C++ value. Now C++ struct fields can be represented the same way (see unit changes test for an example).
1 parent 5a52ea0 commit 9f99c7d

File tree

5 files changed

+32
-14
lines changed

5 files changed

+32
-14
lines changed

src/mp/gen.cpp

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,13 @@ static void Generate(kj::StringPtr src_prefix,
411411

412412
if (node.getProto().isStruct()) {
413413
const auto& struc = node.asStruct();
414+
415+
FieldList fields;
416+
for (const auto schema_field : struc.getFields()) {
417+
fields.addField(schema_field, true);
418+
}
419+
fields.mergeFields();
420+
414421
std::ostringstream generic_name;
415422
generic_name << node_name;
416423
dec << "template<";
@@ -431,22 +438,23 @@ static void Generate(kj::StringPtr src_prefix,
431438
dec << "struct ProxyStruct<" << message_namespace << "::" << generic_name.str() << ">\n";
432439
dec << "{\n";
433440
dec << " using Struct = " << message_namespace << "::" << generic_name.str() << ";\n";
434-
for (const auto field : struc.getFields()) {
435-
auto field_name = field.getProto().getName();
441+
for (const auto& field : fields.fields) {
442+
if (field.skip) continue;
443+
auto field_name = field.param.getProto().getName();
436444
add_accessor(field_name);
437445
dec << " using " << Cap(field_name) << "Accessor = Accessor<" << base_name
438446
<< "_fields::" << Cap(field_name) << ", FIELD_IN | FIELD_OUT";
439-
if (BoxedType(field.getType())) dec << " | FIELD_BOXED";
447+
if (field.optional) dec << " | FIELD_OPTIONAL";
448+
if (field.requested) dec << " | FIELD_REQUESTED";
449+
if (BoxedType(field.param.getType())) dec << " | FIELD_BOXED";
440450
dec << ">;\n";
441451
}
442452
dec << " using Accessors = std::tuple<";
443453
size_t i = 0;
444-
for (const auto field : struc.getFields()) {
445-
if (AnnotationExists(field.getProto(), SKIP_ANNOTATION_ID)) {
446-
continue;
447-
}
454+
for (const auto& field : fields.fields) {
455+
if (field.skip) continue;
448456
if (i) dec << ", ";
449-
dec << Cap(field.getProto().getName()) << "Accessor";
457+
dec << Cap(field.param.getProto().getName()) << "Accessor";
450458
++i;
451459
}
452460
dec << ">;\n";
@@ -460,13 +468,11 @@ static void Generate(kj::StringPtr src_prefix,
460468
inl << "public:\n";
461469
inl << " using Struct = " << message_namespace << "::" << node_name << ";\n";
462470
size_t i = 0;
463-
for (const auto field : struc.getFields()) {
464-
if (AnnotationExists(field.getProto(), SKIP_ANNOTATION_ID)) {
465-
continue;
466-
}
467-
auto field_name = field.getProto().getName();
471+
for (const auto& field : fields.fields) {
472+
if (field.skip) continue;
473+
auto field_name = field.param.getProto().getName();
468474
auto member_name = field_name;
469-
GetAnnotationText(field.getProto(), NAME_ANNOTATION_ID, &member_name);
475+
GetAnnotationText(field.param.getProto(), NAME_ANNOTATION_ID, &member_name);
470476
inl << " static decltype(auto) get(std::integral_constant<size_t, " << i << ">) { return "
471477
<< "&" << proxied_class_type << "::" << member_name << "; }\n";
472478
++i;

test/mp/test/foo-types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <mp/type-map.h>
2020
#include <mp/type-message.h>
2121
#include <mp/type-number.h>
22+
#include <mp/type-optional.h>
2223
#include <mp/type-set.h>
2324
#include <mp/type-string.h>
2425
#include <mp/type-struct.h>

test/mp/test/foo.capnp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ struct FooStruct $Proxy.wrap("mp::test::FooStruct") {
5353
name @0 :Text;
5454
setint @1 :List(Int32);
5555
vbool @2 :List(Bool);
56+
optionalInt @3 :Int32 $Proxy.name("optional_int");
57+
hasOptionalInt @4 :Bool;
5658
}
5759

5860
struct FooCustom $Proxy.wrap("mp::test::FooCustom") {

test/mp/test/foo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <functional>
1010
#include <map>
1111
#include <memory>
12+
#include <optional>
1213
#include <string>
1314
#include <set>
1415
#include <vector>
@@ -21,6 +22,7 @@ struct FooStruct
2122
std::string name;
2223
std::set<int> setint;
2324
std::vector<bool> vbool;
25+
std::optional<int> optional_int;
2426
};
2527

2628
enum class FooEnum : uint8_t { ONE = 1, TWO = 2, };

test/mp/test/test.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ KJ_TEST("Call FooInterface methods")
141141
in.vbool.push_back(false);
142142
in.vbool.push_back(true);
143143
in.vbool.push_back(false);
144+
in.optional_int = 3;
144145
FooStruct out = foo->pass(in);
145146
KJ_EXPECT(in.name == out.name);
146147
KJ_EXPECT(in.setint.size() == out.setint.size());
@@ -151,6 +152,12 @@ KJ_TEST("Call FooInterface methods")
151152
for (size_t i = 0; i < in.vbool.size(); ++i) {
152153
KJ_EXPECT(in.vbool[i] == out.vbool[i]);
153154
}
155+
KJ_EXPECT(in.optional_int == out.optional_int);
156+
157+
// Additional checks for std::optional member
158+
KJ_EXPECT(foo->pass(in).optional_int == 3);
159+
in.optional_int.reset();
160+
KJ_EXPECT(!foo->pass(in).optional_int);
154161

155162
FooStruct err;
156163
try {

0 commit comments

Comments
 (0)