Skip to content

Commit 60a186f

Browse files
committed
proto: Add example.cpp to get working JSON serialization
In a separate branch where I tried to handle Any in Go, I got [1]: go run ./example.go go/config.pb.go:26:8: cannot find package "google/protobuf" in any of: /usr/lib/go/src/google/protobuf (from $GOROOT) /home/wking/.local/lib/go/src/google/protobuf (from $GOPATH) Makefile:31: recipe for target 'example' failed This commit switches the main example to C++, because it's protobuf's implementation language, so new features like Any support tend to land there first. The example tries to round-trip source JSON into a protobuf message and back. It does that by reading from config.json (I've skipped runtime.json for now) into a message, and then writing JSON serialized from that message to stdout. Attributes in the input JSON file that aren't represented in the protobuf message structure seem to be silently dropped (which makes forward compatibility somewhat easier, but explicit validation somewhat harder ;). The docs for this sort of thing seem sparse (although there is a stale C++ Any example here [2]). The source skeleton [3] and Makefile rule [4] are based on the protobuf examples. kTypeUrlPrefix [5], GetTypeUrl [6], the resolver handling [7,8], and the basic to/from JSON conversion [9]. The commented-out LinuxUser section was how I figured out what to put in the process.user block of config.json. The bits of that that weren't in [10], I figured out just by reading proto/cpp/config.pb.h. The Makefile rule should likely be using: $(filter %.cc, $(CPP_FILES)) but my more restrictive filter removes runtime_config.pb.cc to avoid: $ make example_cpp c++ example.cc ./cpp/config.pb.cc ./cpp/runtime_config.pb.cc -o example_cpp $(pkg-config --cflags --libs protobuf) -I ./cpp ./cpp/runtime_config.pb.h:647:30: error: expected unqualified-id before numeric constant const ::oci::LinuxRuntime& linux() const; ^ ... [1]: vbatts#2 [2]: https://developers.google.com/protocol-buffers/docs/proto3#any Although it recommends: status.add_details()->PackFrom(details); And the actual usage would be: status.mutable_details()->PackFrom(details); See [10]. [3]: https://github.com/google/protobuf/blob/v3.0.0-beta-1/examples/add_person.cc [4]: https://github.com/google/protobuf/blob/v3.0.0-beta-1/examples/Makefile#L24-L26 [5]: https://github.com/google/protobuf/blob/v3.0.0-beta-1/src/google/protobuf/util/json_util_test.cc#L52 [6]: https://github.com/google/protobuf/blob/v3.0.0-beta-1/src/google/protobuf/util/json_util_test.cc#L54-L56 [7]: https://github.com/google/protobuf/blob/v3.0.0-beta-1/src/google/protobuf/util/json_util_test.cc#L66-L67 [8]: https://github.com/google/protobuf/blob/v3.0.0-beta-1/src/google/protobuf/util/json_util_test.cc#L85 [9]: https://github.com/google/protobuf/blob/v3.0.0-beta-1/src/google/protobuf/util/json_util_test.cc#L70-L83 [10]: https://github.com/google/protobuf/blob/v3.0.0-beta-1/src/google/protobuf/any_test.cc#L42 Signed-off-by: W. Trevor King <[email protected]>
1 parent 932c50d commit 60a186f

File tree

3 files changed

+121
-2
lines changed

3 files changed

+121
-2
lines changed

proto/Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ $(CPP_DIR)/%.pb.cc: %.proto
4242
@mkdir -p $(CPP_DIR)
4343
protoc --cpp_out=$(CPP_DIR)/ $^
4444

45+
%_cpp: %.cc $(CPP_FILES)
46+
pkg-config --cflags protobuf # fails if protobuf is not installed
47+
c++ $< $(filter $(CPP_DIR)/config%.cc, $(CPP_FILES)) -o $@ $$(pkg-config --cflags --libs protobuf) -I $(CPP_DIR)
48+
4549
py: $(PY_FILES)
4650

4751
$(PY_DIR)/%_pb2.py: %.proto
@@ -52,5 +56,4 @@ proto3: Dockerfile run.sh
5256
$(DOCKER) build -t $@ . && $(DOCKER) run -it -v $(CWD):/proto:ro -v $(CWD)/output:/output:rw $@
5357

5458
clean:
55-
rm -rf *~ $(GO_FILES) $(C_FILES) $(PY_FILES) $(CPP_FILES) $(CWD)/output
56-
59+
rm -rf *~ *_cpp $(GO_FILES) $(C_FILES) $(PY_FILES) $(CPP_FILES) $(CWD)/output

proto/config.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"version": "",
3+
"platform": {
4+
"os": "linux",
5+
"arch": "x86_64"
6+
},
7+
"process": {
8+
"terminal": false,
9+
"user": {
10+
"@type": "type.googleapis.com/oci.LinuxUser",
11+
"uid": 1,
12+
"gid": 1,
13+
"additionalGids": [
14+
5,
15+
6
16+
]
17+
},
18+
"env": [
19+
"TERM=linux"
20+
],
21+
"cwd": "/"
22+
},
23+
"hostname": ""
24+
}

proto/example.cc

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// See README.txt for information and build instructions.
2+
3+
#include <fstream>
4+
#include <iostream>
5+
#include <string>
6+
7+
#include <google/protobuf/descriptor.h>
8+
#include <google/protobuf/util/json_util.h>
9+
#include <google/protobuf/util/type_resolver.h>
10+
#include <google/protobuf/util/type_resolver_util.h>
11+
12+
#include "config.pb.h"
13+
14+
using namespace std;
15+
16+
static const char kTypeUrlPrefix[] = "type.googleapis.com";
17+
18+
static string GetTypeUrl(const google::protobuf::Descriptor* message) {
19+
return string(kTypeUrlPrefix) + "/" + message->full_name();
20+
}
21+
22+
static bool ReadMessage(string path, google::protobuf::Message *message) {
23+
string binary;
24+
ifstream input(path.c_str(), ios::in | ios::binary);
25+
if (!input) {
26+
cout << path << ": File not found." << endl;
27+
return false;
28+
}
29+
string json( (istreambuf_iterator<char>(input)),
30+
istreambuf_iterator<char>() );
31+
google::protobuf::scoped_ptr<google::protobuf::util::TypeResolver> resolver;
32+
resolver.reset(google::protobuf::util::NewTypeResolverForDescriptorPool(
33+
kTypeUrlPrefix,
34+
google::protobuf::DescriptorPool::generated_pool()));
35+
GOOGLE_CHECK_OK(google::protobuf::util::JsonToBinaryString(
36+
resolver.get(),
37+
GetTypeUrl(message->GetDescriptor()),
38+
json,
39+
&binary));
40+
return message->ParseFromString(binary);
41+
}
42+
43+
static bool WriteMessage(const google::protobuf::Message& message) {
44+
string json;
45+
google::protobuf::util::JsonOptions options;
46+
google::protobuf::scoped_ptr<google::protobuf::util::TypeResolver> resolver;
47+
resolver.reset(google::protobuf::util::NewTypeResolverForDescriptorPool(
48+
kTypeUrlPrefix,
49+
google::protobuf::DescriptorPool::generated_pool()));
50+
options.add_whitespace = true;
51+
GOOGLE_CHECK_OK(google::protobuf::util::BinaryToJsonString(
52+
resolver.get(),
53+
GetTypeUrl(message.GetDescriptor()),
54+
message.SerializeAsString(),
55+
&json,
56+
options));
57+
cout << json;
58+
return true;
59+
}
60+
61+
// Main function: Reads the config from a file and writes it to stdout.
62+
int main(int argc, char* argv[]) {
63+
// Verify that the version of the library that we linked against is
64+
// compatible with the version of the headers we compiled against.
65+
GOOGLE_PROTOBUF_VERIFY_VERSION;
66+
67+
oci::Spec config;
68+
69+
if (!ReadMessage("config.json", &config)) {
70+
cerr << "config.json: Failed to load." << endl;
71+
return -1;
72+
}
73+
74+
/*
75+
oci::LinuxUser user;
76+
user.set_uid(1);
77+
user.set_gid(1);
78+
user.add_additional_gids(5);
79+
user.add_additional_gids(6);
80+
config.mutable_process()->mutable_user()->PackFrom(user);
81+
*/
82+
83+
if (!WriteMessage(config)) {
84+
cerr << "config.json: Failed to write to stdout." << endl;
85+
return -1;
86+
}
87+
88+
// Optional: Delete all global objects allocated by libprotobuf.
89+
google::protobuf::ShutdownProtobufLibrary();
90+
91+
return 0;
92+
}

0 commit comments

Comments
 (0)