Skip to content

Commit e9beae1

Browse files
add hpp_options
1 parent ed1f805 commit e9beae1

20 files changed

+193
-65
lines changed

Diff for: docs/Code_Generation_Guide.md

+28-15
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ protobuf_generate_hpp(
1616
[PLUGIN_OPTIONS <plugin_options>])
1717
```
1818

19-
- IMPORT_DIRS: Specifies common parent directories for schema files.
20-
- PROTOC_OUT_DIR: Output directory for generated source files. Defaults to CMAKE_CURRENT_BINARY_DIR.
21-
- PROTOS: List of proto schema files. If omitted, then every source file ending in proto of TARGET will be used.
22-
- PLUGIN_OPTIONS: A comma-separated string forwarded to the protoc-gen-hpp plugin to customize code generation. Options include:
23-
* `namespace_prefix=`: Specifies a namespace prefix to be added to the generated C++ code, in addition to the package namespace.
24-
* `directory_prefix=`: Prepends a directory to all import dependencies.
25-
* `proto2_explicit_presence=`: For Proto2 only, makes optional scalar fields implicitly present except for specified scopes. This option can be specified multiple times. For example: the option `proto2_explicit_presence=.pkg1.msg1.field1,proto2_explicit_presence=.pkg1.msg2` instructs the code generator that explicit presence is only applicable for the `field1` of `pkg1.msg1` and all fields of `pkg1.msg2`.
26-
* `non_owning`: Generates non-owning messages.
19+
- IMPORT_DIRS: Specifies common parent directories for schema files.
20+
- PROTOC_OUT_DIR: Output directory for generated source files. Defaults to CMAKE_CURRENT_BINARY_DIR.
21+
- PROTOS: List of proto schema files. If omitted, then every source file ending in proto of TARGET will be used.
22+
- <a id="plugin-options">PLUGIN_OPTIONS</a>: A comma-separated string forwarded to the protoc-gen-hpp plugin to customize code generation. Options include:
23+
* `namespace_prefix=`: Specifies a namespace prefix to be added to the generated C++ code, in addition to the package namespace.
24+
* `directory_prefix=`: Prepends a directory to included non-system dependencies.
25+
* `proto2_explicit_presence=`: For Proto2 only, makes optional scalar fields implicitly present except for specified scopes. This option can be specified multiple times. For example: the option `proto2_explicit_presence=.pkg1.msg1.field1,proto2_explicit_presence=.pkg1.msg2` instructs the code generator that explicit presence is only applicable for the `field1` of `pkg1.msg1` and all fields of `pkg1.msg2`.
26+
* `non_owning`: Generates non-owning messages.
27+
* `string_keyed_map`: Uses an alternative type template such as 'std::unordered_map' for generated string keyed map; only works for __regular mode__ map fields.
28+
* `numeric_keyed_map`: Uses an alternative type template such as 'std::map' for generated numeric keyed map; only works for __regular mode__ map fields.
2729

2830
The compiler generates several header files for each .proto file, transforming the filename and extension as follows:
2931

@@ -65,6 +67,10 @@ protoc --proto_path=src --hpp_out build/gen --hpp_opt=namespace_prefix=baz src/
6567
```
6668
This command will place all declarations in the `baz::foo::bar` namespace.
6769

70+
## Non-owning mode only for specific files, messages or fields.
71+
72+
In addition to using the [protoc plugin option](#plugin-options) to enable __non-owning mode__ globally, the `hpp_options` extension can be used to apply __non-owning mode__ selectively to specific files, messages, or fields. The file [hpp_options_test.proto](hpp_options_test.proto) provides an example of how to mix __regular__ and __non-owning mode__ code generation within a single proto file.
73+
6874
## Messages
6975

7076
Given a simple message declaration like:
@@ -190,7 +196,6 @@ message TestOneof {
190196
int32 foo_int = 1;
191197
string foo_string = 2;
192198
NestedMessage foo_message = 3;
193-
NestedMessage foo_lazy_message = 4 [lazy = true];
194199
}
195200
message NestedMessage {
196201
double required_double = 1;
@@ -210,11 +215,10 @@ struct TestOneof {
210215
enum foo_oneof_case : int {
211216
foo_int = 1,
212217
foo_string = 2,
213-
foo_message = 3,
214-
foo_lazy_message = 4
218+
foo_message = 3
215219
};
216220

217-
std::variant<std::monostate, int32_t, std::string, NestedMessage, NestedMessage> foo;
221+
std::variant<std::monostate, int32_t, std::string, NestedMessage> foo;
218222

219223
bool operator == (const TestOneof&) const = default;
220224
};
@@ -224,20 +228,27 @@ struct TestOneof {
224228
### Map fields
225229
For `map` fields:
226230
```proto
231+
import "hpp_proto/hpp_options.proto";
232+
227233
message TestMap {
228234
map<int32, int32> map1 = 1;
235+
map<string, int32> map2 = 2 [(hpp.proto.hpp_field_opts).string_keyed_map = 'std::unordered_map'];
236+
map<int32, int32> map3 = 3 [(hpp.proto.hpp_field_opts).numeric_keyed_map = 'std::map'];;
229237
}
230238
```
231-
The compiler generates `hpp::proto::flat_map<key_type, mapped_type>` for regular mode and with `hpp::proto::equality_comparable_span<std::pair<key_type, mapped_type>>` for non-owning mode. In the non-owning mode,
232-
the library does not handle key deduplication during serialization or deserialization.
233-
Given a deserialized message with duplicate map keys, users are responsible to make sure that only the last key seen should be treated as valid.
239+
240+
By default, in __regular mode__, the compiler generates `hpp::proto::flat_map<key_type, mapped_type>` for map fields. Alternatively, a template class conforming to the `std::map interface` can be used by specifying either `string_keyed_map` or `numeric_keyed_map` in the [protoc plugin options](#plugin-options) globally, or by annotating a specific file, message, or field with the hpp_options extension.
241+
242+
In __non-owning mode__, the compiler always generates `hpp::proto::equality_comparable_span<std::pair<key_type, mapped_type>>` for map fields. Additionally, the library does not handle key deduplication during serialization or deserialization. If a deserialized message contains duplicate map keys, users must ensure that only the last occurrence of each key is considered valid.
234243

235244
<details open><summary> Regular Mode </summary>
236245
<p>
237246

238247
```cpp
239248
struct TestMap {
240249
hpp::proto::flat_map<int32_t,int32_t> map1;
250+
std::unordered_map<std::string, int32_t> map2;
251+
std::map<int32_t, int32_t> map3;
241252
bool operator == (const TestMap&) const = default;
242253
};
243254
```
@@ -249,6 +260,8 @@ struct TestMap {
249260
```cpp
250261
struct TestMap {
251262
hpp::proto::equality_comparable_span<std::pair<int32_t,int32_t>> map1;
263+
hpp::proto::equality_comparable_span<std::pair<std::string,int32_t>> map2;
264+
hpp::proto::equality_comparable_span<std::pair<int32_t,int32_t>> map3;
252265
bool operator == (const TestMap&) const = default;
253266
};
254267
```

Diff for: docs/hpp_options_test.proto

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../tests/hpp_options_test.proto

Diff for: include/google/protobuf/compiler/plugin.desc.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// NO CHECKED-IN PROTOBUF GENCODE
44
// generation command line:
55
// protoc --plugin=protoc-gen-hpp=/path/to/protoc-gen-hpp
6-
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed:${out_dir}
6+
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed,export_request=descriptor.request.binpb:${out_dir}
77
// google/protobuf/compiler/plugin.proto
88

99
#pragma once

Diff for: include/google/protobuf/compiler/plugin.glz.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// NO CHECKED-IN PROTOBUF GENCODE
44
// generation command line:
55
// protoc --plugin=protoc-gen-hpp=/path/to/protoc-gen-hpp
6-
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed:${out_dir}
6+
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed,export_request=descriptor.request.binpb:${out_dir}
77
// google/protobuf/compiler/plugin.proto
88

99
#pragma once

Diff for: include/google/protobuf/compiler/plugin.msg.hpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
// NO CHECKED-IN PROTOBUF GENCODE
44
// generation command line:
55
// protoc --plugin=protoc-gen-hpp=/path/to/protoc-gen-hpp
6-
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed:${out_dir}
6+
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed,export_request=descriptor.request.binpb:${out_dir}
77
// google/protobuf/compiler/plugin.proto
88

99
#pragma once
1010

1111
#include <hpp_proto/field_types.hpp>
1212
#include "google/protobuf/descriptor.msg.hpp"
13+
// @@protoc_insertion_point(includes)
14+
1315

1416
namespace google::protobuf::compiler {
1517
//NOLINTBEGIN(performance-enum-size)

Diff for: include/google/protobuf/compiler/plugin.pb.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// NO CHECKED-IN PROTOBUF GENCODE
44
// generation command line:
55
// protoc --plugin=protoc-gen-hpp=/path/to/protoc-gen-hpp
6-
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed:${out_dir}
6+
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed,export_request=descriptor.request.binpb:${out_dir}
77
// google/protobuf/compiler/plugin.proto
88

99
#pragma once

Diff for: include/google/protobuf/descriptor.desc.hpp

+2-2
Large diffs are not rendered by default.

Diff for: include/google/protobuf/descriptor.glz.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// NO CHECKED-IN PROTOBUF GENCODE
44
// generation command line:
55
// protoc --plugin=protoc-gen-hpp=/path/to/protoc-gen-hpp
6-
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed:${out_dir}
6+
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed,export_request=descriptor.request.binpb:${out_dir}
77
// google/protobuf/descriptor.proto
88

99
#pragma once

Diff for: include/google/protobuf/descriptor.msg.hpp

+47-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
// NO CHECKED-IN PROTOBUF GENCODE
44
// generation command line:
55
// protoc --plugin=protoc-gen-hpp=/path/to/protoc-gen-hpp
6-
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed:${out_dir}
6+
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed,export_request=descriptor.request.binpb:${out_dir}
77
// google/protobuf/descriptor.proto
88

99
#pragma once
1010

1111
#include <hpp_proto/field_types.hpp>
12+
// @@protoc_insertion_point(includes)
13+
1214

1315
namespace google::protobuf {
1416
//NOLINTBEGIN(performance-enum-size)
@@ -130,6 +132,28 @@ struct SourceCodeInfo {
130132

131133
std::vector<Location> location;
132134

135+
struct extension_t {
136+
using pb_extension = SourceCodeInfo;
137+
hpp::proto::flat_map<uint32_t, std::vector<std::byte>> fields;
138+
bool operator==(const extension_t &other) const = default;
139+
} extensions;
140+
141+
[[nodiscard]] auto get_extension(auto meta) const {
142+
return meta.read(extensions);
143+
}
144+
template<typename Meta>
145+
[[nodiscard]] auto set_extension(Meta meta, typename Meta::set_value_type &&value) {
146+
return meta.write(extensions, std::move(value));
147+
}
148+
template<typename Meta>
149+
requires Meta::is_repeated
150+
[[nodiscard]] auto set_extension(Meta meta, std::initializer_list<typename Meta::element_type> value) {
151+
return meta.write(extensions, std::span<const typename Meta::element_type>{value.begin(), value.end()});
152+
}
153+
[[nodiscard]] bool has_extension(auto meta) const {
154+
return meta.element_of(extensions);
155+
}
156+
133157
bool operator == (const SourceCodeInfo&) const = default;
134158
};
135159

@@ -698,6 +722,28 @@ struct FileDescriptorProto {
698722
struct FileDescriptorSet {
699723
std::vector<FileDescriptorProto> file;
700724

725+
struct extension_t {
726+
using pb_extension = FileDescriptorSet;
727+
hpp::proto::flat_map<uint32_t, std::vector<std::byte>> fields;
728+
bool operator==(const extension_t &other) const = default;
729+
} extensions;
730+
731+
[[nodiscard]] auto get_extension(auto meta) const {
732+
return meta.read(extensions);
733+
}
734+
template<typename Meta>
735+
[[nodiscard]] auto set_extension(Meta meta, typename Meta::set_value_type &&value) {
736+
return meta.write(extensions, std::move(value));
737+
}
738+
template<typename Meta>
739+
requires Meta::is_repeated
740+
[[nodiscard]] auto set_extension(Meta meta, std::initializer_list<typename Meta::element_type> value) {
741+
return meta.write(extensions, std::span<const typename Meta::element_type>{value.begin(), value.end()});
742+
}
743+
[[nodiscard]] bool has_extension(auto meta) const {
744+
return meta.element_of(extensions);
745+
}
746+
701747
bool operator == (const FileDescriptorSet&) const = default;
702748
};
703749

Diff for: include/google/protobuf/descriptor.pb.hpp

+11-9
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// NO CHECKED-IN PROTOBUF GENCODE
44
// generation command line:
55
// protoc --plugin=protoc-gen-hpp=/path/to/protoc-gen-hpp
6-
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed:${out_dir}
6+
// --hpp_out proto2_explicit_presence=.google.protobuf.FieldDescriptorProto.oneof_index,proto2_explicit_presence=.google.protobuf.FieldOptions.packed,export_request=descriptor.request.binpb:${out_dir}
77
// google/protobuf/descriptor.proto
88

99
#pragma once
@@ -15,7 +15,8 @@
1515
namespace google::protobuf {
1616

1717
auto pb_meta(const FileDescriptorSet &) -> std::tuple<
18-
hpp::proto::field_meta<1, &FileDescriptorSet::file, hpp::proto::field_option::none>>;
18+
hpp::proto::field_meta<1, &FileDescriptorSet::file, hpp::proto::field_option::none>,
19+
hpp::proto::field_meta<UINT32_MAX, &FileDescriptorSet::extensions>>;
1920

2021
auto pb_meta(const FileDescriptorProto &) -> std::tuple<
2122
hpp::proto::field_meta<1, &FileDescriptorProto::name, hpp::proto::field_option::none>,
@@ -57,7 +58,7 @@ auto pb_meta(const ExtensionRangeOptions &) -> std::tuple<
5758
hpp::proto::field_meta<999, &ExtensionRangeOptions::uninterpreted_option, hpp::proto::field_option::none>,
5859
hpp::proto::field_meta<2, &ExtensionRangeOptions::declaration, hpp::proto::field_option::none>,
5960
hpp::proto::field_meta<50, &ExtensionRangeOptions::features, hpp::proto::field_option::explicit_presence>,
60-
hpp::proto::field_meta<3, &ExtensionRangeOptions::verification, hpp::proto::field_option::none, void, ::google::protobuf::ExtensionRangeOptions::VerificationState::UNVERIFIED>,
61+
hpp::proto::field_meta<3, &ExtensionRangeOptions::verification, hpp::proto::field_option::closed_enum, void, ::google::protobuf::ExtensionRangeOptions::VerificationState::UNVERIFIED>,
6162
hpp::proto::field_meta<UINT32_MAX, &ExtensionRangeOptions::extensions>>;
6263

6364
auto pb_meta(const ExtensionRangeOptions::Declaration &) -> std::tuple<
@@ -119,7 +120,7 @@ auto pb_meta(const FileOptions &) -> std::tuple<
119120
hpp::proto::field_meta<10, &FileOptions::java_multiple_files, hpp::proto::field_option::none, bool, false>,
120121
hpp::proto::field_meta<20, &FileOptions::java_generate_equals_and_hash, hpp::proto::field_option::none, bool>,
121122
hpp::proto::field_meta<27, &FileOptions::java_string_check_utf8, hpp::proto::field_option::none, bool, false>,
122-
hpp::proto::field_meta<9, &FileOptions::optimize_for, hpp::proto::field_option::none, void, ::google::protobuf::FileOptions::OptimizeMode::SPEED>,
123+
hpp::proto::field_meta<9, &FileOptions::optimize_for, hpp::proto::field_option::closed_enum, void, ::google::protobuf::FileOptions::OptimizeMode::SPEED>,
123124
hpp::proto::field_meta<11, &FileOptions::go_package, hpp::proto::field_option::none>,
124125
hpp::proto::field_meta<16, &FileOptions::cc_generic_services, hpp::proto::field_option::none, bool, false>,
125126
hpp::proto::field_meta<17, &FileOptions::java_generic_services, hpp::proto::field_option::none, bool, false>,
@@ -148,16 +149,16 @@ auto pb_meta(const MessageOptions &) -> std::tuple<
148149
hpp::proto::field_meta<UINT32_MAX, &MessageOptions::extensions>>;
149150

150151
auto pb_meta(const FieldOptions &) -> std::tuple<
151-
hpp::proto::field_meta<1, &FieldOptions::ctype, hpp::proto::field_option::none, void, ::google::protobuf::FieldOptions::CType::STRING>,
152+
hpp::proto::field_meta<1, &FieldOptions::ctype, hpp::proto::field_option::closed_enum, void, ::google::protobuf::FieldOptions::CType::STRING>,
152153
hpp::proto::field_meta<2, &FieldOptions::packed, hpp::proto::field_option::explicit_presence, bool>,
153-
hpp::proto::field_meta<6, &FieldOptions::jstype, hpp::proto::field_option::none, void, ::google::protobuf::FieldOptions::JSType::JS_NORMAL>,
154+
hpp::proto::field_meta<6, &FieldOptions::jstype, hpp::proto::field_option::closed_enum, void, ::google::protobuf::FieldOptions::JSType::JS_NORMAL>,
154155
hpp::proto::field_meta<5, &FieldOptions::lazy, hpp::proto::field_option::none, bool, false>,
155156
hpp::proto::field_meta<15, &FieldOptions::unverified_lazy, hpp::proto::field_option::none, bool, false>,
156157
hpp::proto::field_meta<3, &FieldOptions::deprecated, hpp::proto::field_option::none, bool, false>,
157158
hpp::proto::field_meta<10, &FieldOptions::weak, hpp::proto::field_option::none, bool, false>,
158159
hpp::proto::field_meta<16, &FieldOptions::debug_redact, hpp::proto::field_option::none, bool, false>,
159160
hpp::proto::field_meta<17, &FieldOptions::retention, hpp::proto::field_option::closed_enum, void, ::google::protobuf::FieldOptions::OptionRetention::RETENTION_UNKNOWN>,
160-
hpp::proto::field_meta<19, &FieldOptions::targets, hpp::proto::field_option::none>,
161+
hpp::proto::field_meta<19, &FieldOptions::targets, hpp::proto::field_option::closed_enum>,
161162
hpp::proto::field_meta<20, &FieldOptions::edition_defaults, hpp::proto::field_option::none>,
162163
hpp::proto::field_meta<21, &FieldOptions::features, hpp::proto::field_option::explicit_presence>,
163164
hpp::proto::field_meta<22, &FieldOptions::feature_support, hpp::proto::field_option::explicit_presence>,
@@ -203,7 +204,7 @@ auto pb_meta(const ServiceOptions &) -> std::tuple<
203204

204205
auto pb_meta(const MethodOptions &) -> std::tuple<
205206
hpp::proto::field_meta<33, &MethodOptions::deprecated, hpp::proto::field_option::none, bool, false>,
206-
hpp::proto::field_meta<34, &MethodOptions::idempotency_level, hpp::proto::field_option::none, void, ::google::protobuf::MethodOptions::IdempotencyLevel::IDEMPOTENCY_UNKNOWN>,
207+
hpp::proto::field_meta<34, &MethodOptions::idempotency_level, hpp::proto::field_option::closed_enum, void, ::google::protobuf::MethodOptions::IdempotencyLevel::IDEMPOTENCY_UNKNOWN>,
207208
hpp::proto::field_meta<35, &MethodOptions::features, hpp::proto::field_option::explicit_presence>,
208209
hpp::proto::field_meta<999, &MethodOptions::uninterpreted_option, hpp::proto::field_option::none>,
209210
hpp::proto::field_meta<UINT32_MAX, &MethodOptions::extensions>>;
@@ -241,7 +242,8 @@ auto pb_meta(const FeatureSetDefaults::FeatureSetEditionDefault &) -> std::tuple
241242
hpp::proto::field_meta<5, &FeatureSetDefaults::FeatureSetEditionDefault::fixed_features, hpp::proto::field_option::explicit_presence>>;
242243

243244
auto pb_meta(const SourceCodeInfo &) -> std::tuple<
244-
hpp::proto::field_meta<1, &SourceCodeInfo::location, hpp::proto::field_option::none>>;
245+
hpp::proto::field_meta<1, &SourceCodeInfo::location, hpp::proto::field_option::none>,
246+
hpp::proto::field_meta<UINT32_MAX, &SourceCodeInfo::extensions>>;
245247

246248
auto pb_meta(const SourceCodeInfo::Location &) -> std::tuple<
247249
hpp::proto::field_meta<1, &SourceCodeInfo::Location::path, hpp::proto::field_option::is_packed, hpp::proto::vint64_t>,

Diff for: include/hpp_proto/field_types.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ class equality_comparable_span {
615615
requires requires(R &r) {
616616
{ std::ranges::data(r) } -> std::convertible_to<const T *>;
617617
}
618-
// NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature)
618+
// NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature,misc-unconventional-assign-operator)
619619
constexpr equality_comparable_span &operator=(R &other) noexcept {
620620
operator=(std::span<T>(other));
621621
return *this;

0 commit comments

Comments
 (0)