From 42b3613c38019a910f81ac930c521111ca2a22e0 Mon Sep 17 00:00:00 2001 From: ttroy50 Date: Sat, 28 Nov 2015 11:34:52 +0000 Subject: [PATCH] add protobuf example for code generation --- 03-code-generation/{README.md => README.adoc} | 0 03-code-generation/protobuf/AddressBook.proto | 24 ++ 03-code-generation/protobuf/CMakeLists.txt | 33 +++ 03-code-generation/protobuf/README.adoc | 224 ++++++++++++++++++ 03-code-generation/protobuf/main.cpp | 93 ++++++++ 5 files changed, 374 insertions(+) rename 03-code-generation/{README.md => README.adoc} (100%) create mode 100644 03-code-generation/protobuf/AddressBook.proto create mode 100644 03-code-generation/protobuf/CMakeLists.txt create mode 100644 03-code-generation/protobuf/README.adoc create mode 100644 03-code-generation/protobuf/main.cpp diff --git a/03-code-generation/README.md b/03-code-generation/README.adoc similarity index 100% rename from 03-code-generation/README.md rename to 03-code-generation/README.adoc diff --git a/03-code-generation/protobuf/AddressBook.proto b/03-code-generation/protobuf/AddressBook.proto new file mode 100644 index 0000000..13913b8 --- /dev/null +++ b/03-code-generation/protobuf/AddressBook.proto @@ -0,0 +1,24 @@ +package tutorial; + +message Person { + required string name = 1; + required int32 id = 2; + optional string email = 3; + + enum PhoneType { + MOBILE = 0; + HOME = 1; + WORK = 2; + } + + message PhoneNumber { + required string number = 1; + optional PhoneType type = 2 [default = HOME]; + } + + repeated PhoneNumber phone = 4; +} + +message AddressBook { + repeated Person person = 1; +} diff --git a/03-code-generation/protobuf/CMakeLists.txt b/03-code-generation/protobuf/CMakeLists.txt new file mode 100644 index 0000000..03769a6 --- /dev/null +++ b/03-code-generation/protobuf/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 2.8) + +# Set the project name +project (protobuf_example) + +# find the protobuf compiler and libraries +find_package(Protobuf REQUIRED) + +# check if protobuf was found +if(PROTOBUF_FOUND) + message ("protobuf found") + include_directories(${PROTOBUF_INCLUDE_DIRS}) + include_directories(${CMAKE_CURRENT_BINARY_DIR}) +else() + message (FATAL_ERROR "Cannot find Protobuf") +endif() + +# Generate the .h and .cxx files +PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS AddressBook.proto) + +# Print path to generated files +message ("PROTO_SRCS = ${PROTO_SRCS}") +message ("PROTO_HDRS = ${PROTO_HDRS}") + +# Add an executable +add_executable(protobuf_example + main.cpp + ${PROTO_SRCS} + ${PROTO_HDRS}) + +# link the exe against the libraries +target_link_libraries(protobuf_example + ${PROTOBUF_LIBRARIES}) diff --git a/03-code-generation/protobuf/README.adoc b/03-code-generation/protobuf/README.adoc new file mode 100644 index 0000000..97ba2f1 --- /dev/null +++ b/03-code-generation/protobuf/README.adoc @@ -0,0 +1,224 @@ += Protobuf Code Generation + +:toc: +:toc-placement!: + +toc::[] + + +[[intro]] +Introduction +------------ + +This example shows how to generate class information using https://github.com/google/protobuf[protobuf]. +Protocol Buffers is a data serialization format from Google. A user provides a +`.proto` file with a description of the data. Then using the protobuf compiler, the proto file +can be translated into source code in a number of languages including C++. + +The files in this tutorial are below: + +``` +$ tree +. +├── AddressBook.proto +├── CMakeLists.txt +├── main.cpp +``` + + * AddressBook.proto - proto file from main protocol buffer https://developers.google.com/protocol-buffers/docs/cpptutorial[example] + * CMakeLists.txt - Contains the CMake commands you wish to run + * main.cpp - The source file from the protobuf example. + +[[requirements]] +Requirements +~~~~~~~~~~~~ + +This example requires the protocol buffers binary and libraries to be installed. + +This can be installed on Ubuntu using + +``` +sudo apt-get install protobuf-compiler sudo apt-get install protobuf-compiler +``` + +[[concepts]] +Concepts +~~~~~~~~ + +[[exported_variables]] +Exported Variables +^^^^^^^^^^^^^^^^^^ + +The variables exported by the CMake protobuf package and used in this example include: + + * `PROTOBUF_FOUND` - If Protocol Buffers is installed + * `PROTOBUF_INCLUDE_DIRS` - The protobuf header files + * `PROTOBUF_LIBRARIES` - The protobuf library + +More variables are defined and can be found by examining the documentation at the +top of your `FindProtobuf.cmake` file. + +[[generating-protobuf]] +Generating Source +^^^^^^^^^^^^^^^^^ + +The protobuf CMake package, includes a number of helper functions to make the +code generation easier. In this example we are generating C++ source and use +the following code: + +[source,cmake] +---- +PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS AddressBook.proto) +---- + +The arguments are: + + * PROTO_SRCS - Name of the variable that will store the .pb.cc files. + * PROTO_HDRS- Name of the variable that will store the .pb.h files. + * AddressBook.proto - The .proto file to generate code from. + +[[generated-files]] +Generated Files +^^^^^^^^^^^^^^^ + +After the `PROTOBUF_GENERATE_CPP` function is called, you will have the above +mentioned variables available. These will be marked as the output to a custom command +which calls the protobuf compiler binary to generate them. + +To then have the files generated you should add them to a library or executable. +For example: + +[source,cmake] +---- +add_executable(protobuf_example + main.cpp + ${PROTO_SRCS} + ${PROTO_HDRS}) +--- + +This will cause the protobuf compiler to be called when you call `make` on that +executables target. + +When changes are made to the .proto file, the associated source files will be +autogenerated again. However, if no changes are made to the .proto file and you re-run +make, then nothing will be done. + +[[building-the-example]] +Building the Example +~~~~~~~~~~~~~~~~~~~~ + +[source,bash] +---- +$ mkdir build + +$ cd build/ + +$ cmake .. +-- The C compiler identification is GNU 4.8.4 +-- The CXX compiler identification is GNU 4.8.4 +-- Check for working C compiler: /usr/bin/cc +-- Check for working C compiler: /usr/bin/cc -- works +-- Detecting C compiler ABI info +-- Detecting C compiler ABI info - done +-- Check for working CXX compiler: /usr/bin/c++ +-- Check for working CXX compiler: /usr/bin/c++ -- works +-- Detecting CXX compiler ABI info +-- Detecting CXX compiler ABI info - done +-- Looking for include file pthread.h +-- Looking for include file pthread.h - found +-- Looking for pthread_create +-- Looking for pthread_create - not found +-- Looking for pthread_create in pthreads +-- Looking for pthread_create in pthreads - not found +-- Looking for pthread_create in pthread +-- Looking for pthread_create in pthread - found +-- Found Threads: TRUE +-- Found PROTOBUF: /usr/lib/x86_64-linux-gnu/libprotobuf.so +protobuf found +PROTO_SRCS = /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/AddressBook.pb.cc +PROTO_HDRS = /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/AddressBook.pb.h +-- Configuring done +-- Generating done +-- Build files have been written to: /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build + +$ ls +CMakeCache.txt CMakeFiles cmake_install.cmake Makefile + +$ make VERBOSE=1 +/usr/bin/cmake -H/home/matrim/workspace/cmake-examples/03-code-generation/protobuf -B/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build --check-build-system CMakeFiles/Makefile.cmake 0 +/usr/bin/cmake -E cmake_progress_start /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles/progress.marks +make -f CMakeFiles/Makefile2 all +make[1]: Entering directory `/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build' +make -f CMakeFiles/protobuf_example.dir/build.make CMakeFiles/protobuf_example.dir/depend +make[2]: Entering directory `/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build' +/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles 1 +[ 33%] Running C++ protocol buffer compiler on AddressBook.proto +/usr/bin/protoc --cpp_out /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build -I /home/matrim/workspace/cmake-examples/03-code-generation/protobuf /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/AddressBook.proto +cd /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/matrim/workspace/cmake-examples/03-code-generation/protobuf /home/matrim/workspace/cmake-examples/03-code-generation/protobuf /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles/protobuf_example.dir/DependInfo.cmake --color= +Dependee "/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles/protobuf_example.dir/DependInfo.cmake" is newer than depender "/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles/protobuf_example.dir/depend.internal". +Dependee "/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles/protobuf_example.dir/depend.internal". +Scanning dependencies of target protobuf_example +make[2]: Leaving directory `/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build' +make -f CMakeFiles/protobuf_example.dir/build.make CMakeFiles/protobuf_example.dir/build +make[2]: Entering directory `/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build' +/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles 2 +[ 66%] Building CXX object CMakeFiles/protobuf_example.dir/main.cpp.o +/usr/bin/c++ -I/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build -o CMakeFiles/protobuf_example.dir/main.cpp.o -c /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/main.cpp +/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles 3 +[100%] Building CXX object CMakeFiles/protobuf_example.dir/AddressBook.pb.cc.o +/usr/bin/c++ -I/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build -o CMakeFiles/protobuf_example.dir/AddressBook.pb.cc.o -c /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/AddressBook.pb.cc +Linking CXX executable protobuf_example +/usr/bin/cmake -E cmake_link_script CMakeFiles/protobuf_example.dir/link.txt --verbose=1 +/usr/bin/c++ CMakeFiles/protobuf_example.dir/main.cpp.o CMakeFiles/protobuf_example.dir/AddressBook.pb.cc.o -o protobuf_example -rdynamic -lprotobuf -lpthread +make[2]: Leaving directory `/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build' +/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles 1 2 3 +[100%] Built target protobuf_example +make[1]: Leaving directory `/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build' +/usr/bin/cmake -E cmake_progress_start /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles 0 +$ make VERBOSE=1 +/usr/bin/cmake -H/home/matrim/workspace/cmake-examples/03-code-generation/protobuf -B/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build --check-build-system CMakeFiles/Makefile.cmake 0 +/usr/bin/cmake -E cmake_progress_start /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles/progress.marks +make -f CMakeFiles/Makefile2 all +make[1]: Entering directory `/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build' +make -f CMakeFiles/protobuf_example.dir/build.make CMakeFiles/protobuf_example.dir/depend +make[2]: Entering directory `/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build' +/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles 1 +[ 33%] Running C++ protocol buffer compiler on AddressBook.proto +/usr/bin/protoc --cpp_out /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build -I /home/matrim/workspace/cmake-examples/03-code-generation/protobuf /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/AddressBook.proto +cd /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/matrim/workspace/cmake-examples/03-code-generation/protobuf /home/matrim/workspace/cmake-examples/03-code-generation/protobuf /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles/protobuf_example.dir/DependInfo.cmake --color= +Dependee "/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles/protobuf_example.dir/DependInfo.cmake" is newer than depender "/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles/protobuf_example.dir/depend.internal". +Dependee "/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles/protobuf_example.dir/depend.internal". +Scanning dependencies of target protobuf_example +make[2]: Leaving directory `/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build' +make -f CMakeFiles/protobuf_example.dir/build.make CMakeFiles/protobuf_example.dir/build +make[2]: Entering directory `/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build' +/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles 2 +[ 66%] Building CXX object CMakeFiles/protobuf_example.dir/main.cpp.o +/usr/bin/c++ -I/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build -o CMakeFiles/protobuf_example.dir/main.cpp.o -c /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/main.cpp +/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles 3 +[100%] Building CXX object CMakeFiles/protobuf_example.dir/AddressBook.pb.cc.o +/usr/bin/c++ -I/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build -o CMakeFiles/protobuf_example.dir/AddressBook.pb.cc.o -c /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/AddressBook.pb.cc +Linking CXX executable protobuf_example +/usr/bin/cmake -E cmake_link_script CMakeFiles/protobuf_example.dir/link.txt --verbose=1 +/usr/bin/c++ CMakeFiles/protobuf_example.dir/main.cpp.o CMakeFiles/protobuf_example.dir/AddressBook.pb.cc.o -o protobuf_example -rdynamic -lprotobuf -lpthread +make[2]: Leaving directory `/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build' +/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles 1 2 3 +[100%] Built target protobuf_example +make[1]: Leaving directory `/home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build' +/usr/bin/cmake -E cmake_progress_start /home/matrim/workspace/cmake-examples/03-code-generation/protobuf/build/CMakeFiles 0 + +$ ls +AddressBook.pb.cc CMakeCache.txt cmake_install.cmake protobuf_example +AddressBook.pb.h CMakeFiles Makefile + +$ ./protobuf_example test.db +test.db: File not found. Creating a new file. +Enter person ID number: 11 +Enter name: John Doe +Enter email address (blank for none): wolly@sheep.ie +Enter a phone number (or leave blank to finish): + +$ ls +AddressBook.pb.cc CMakeCache.txt cmake_install.cmake protobuf_example +AddressBook.pb.h CMakeFiles Makefile test.db +---- diff --git a/03-code-generation/protobuf/main.cpp b/03-code-generation/protobuf/main.cpp new file mode 100644 index 0000000..e519fa5 --- /dev/null +++ b/03-code-generation/protobuf/main.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include "AddressBook.pb.h" +using namespace std; + +// This function fills in a Person message based on user input. +void PromptForAddress(tutorial::Person* person) { + cout << "Enter person ID number: "; + int id; + cin >> id; + person->set_id(id); + cin.ignore(256, '\n'); + + cout << "Enter name: "; + getline(cin, *person->mutable_name()); + + cout << "Enter email address (blank for none): "; + string email; + getline(cin, email); + if (!email.empty()) { + person->set_email(email); + } + + while (true) { + cout << "Enter a phone number (or leave blank to finish): "; + string number; + getline(cin, number); + if (number.empty()) { + break; + } + + tutorial::Person::PhoneNumber* phone_number = person->add_phone(); + phone_number->set_number(number); + + cout << "Is this a mobile, home, or work phone? "; + string type; + getline(cin, type); + if (type == "mobile") { + phone_number->set_type(tutorial::Person::MOBILE); + } else if (type == "home") { + phone_number->set_type(tutorial::Person::HOME); + } else if (type == "work") { + phone_number->set_type(tutorial::Person::WORK); + } else { + cout << "Unknown phone type. Using default." << endl; + } + } +} + +// Main function: Reads the entire address book from a file, +// adds one person based on user input, then writes it back out to the same +// file. +int main(int argc, char* argv[]) { + // Verify that the version of the library that we linked against is + // compatible with the version of the headers we compiled against. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + if (argc != 2) { + cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl; + return -1; + } + + tutorial::AddressBook address_book; + + { + // Read the existing address book. + fstream input(argv[1], ios::in | ios::binary); + if (!input) { + cout << argv[1] << ": File not found. Creating a new file." << endl; + } else if (!address_book.ParseFromIstream(&input)) { + cerr << "Failed to parse address book." << endl; + return -1; + } + } + + // Add an address. + PromptForAddress(address_book.add_person()); + + { + // Write the new address book back to disk. + fstream output(argv[1], ios::out | ios::trunc | ios::binary); + if (!address_book.SerializeToOstream(&output)) { + cerr << "Failed to write address book." << endl; + return -1; + } + } + + // Optional: Delete all global objects allocated by libprotobuf. + google::protobuf::ShutdownProtobufLibrary(); + + return 0; +}