WIP: c++
diff --git a/examples/MODULE.bazel b/examples/MODULE.bazel
index 5984bdd..221af6c 100644
--- a/examples/MODULE.bazel
+++ b/examples/MODULE.bazel
@@ -24,8 +24,8 @@
 protoc = use_extension("@toolchains_protoc//protoc:extensions.bzl", "protoc")
 protoc.toolchain(
     google_protobuf = "com_google_protobuf",
-    # Demonstrate overriding the default version
-    version = "v28.0",
+    # NB: c++ codegen requires this version to exactly match the protobuf bazel_dep
+    version = "v29.3",
 )
 use_repo(protoc, "com_google_protobuf", "toolchains_protoc_hub")
 
diff --git a/examples/cc/BUILD.bazel b/examples/cc/BUILD.bazel
new file mode 100644
index 0000000..1d2bee9
--- /dev/null
+++ b/examples/cc/BUILD.bazel
@@ -0,0 +1,7 @@
+load("@rules_cc//cc:defs.bzl", "cc_binary")
+
+cc_binary(
+    name = "cc",
+    srcs = ["greeter_example.cc"],
+    deps = ["//proto:greeter_cc_proto"],
+)
diff --git a/examples/cc/greeter_example.cc b/examples/cc/greeter_example.cc
new file mode 100644
index 0000000..273934e
--- /dev/null
+++ b/examples/cc/greeter_example.cc
@@ -0,0 +1,93 @@
+#include <iostream>
+#include <fstream>
+#include <string>
+#include "greeter.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_phones();
+    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::PHONE_TYPE_MOBILE);
+    } else if (type == "home") {
+      phone_number->set_type(tutorial::Person::PHONE_TYPE_HOME);
+    } else if (type == "work") {
+      phone_number->set_type(tutorial::Person::PHONE_TYPE_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_people());
+
+  {
+    // 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;
+}
diff --git a/examples/proto/BUILD.bazel b/examples/proto/BUILD.bazel
index 88c206c..a5a520c 100644
--- a/examples/proto/BUILD.bazel
+++ b/examples/proto/BUILD.bazel
@@ -2,6 +2,7 @@
 load("@rules_go//proto:def.bzl", "go_proto_library")
 load("@rules_proto//proto:defs.bzl", "proto_library")
 load("@rules_rust_prost//:defs.bzl", "rust_prost_library")
+load("@protobuf//bazel:cc_proto_library.bzl", "cc_proto_library")
 load("@protobuf//bazel:py_proto_library.bzl", "py_proto_library")
 
 package(default_visibility = ["//visibility:public"])
@@ -12,6 +13,11 @@
     deps = ["@com_google_protobuf//:any_proto"],
 )
 
+cc_proto_library(
+    name = "greeter_cc_proto",
+    deps = [":greeter_proto"],
+)
+
 py_proto_library(
     name = "greeter_py_proto",
     deps = [":greeter_proto"],