From 8599ab5a5934579e9cb775b2cf2bcc69e67c2747 Mon Sep 17 00:00:00 2001 From: Protobuf Team Bot Date: Mon, 25 Mar 2024 12:51:39 -0700 Subject: [PATCH] Add an RAII namespace printer helper for C++ codegen. This can be used to open and close namespace automatically within generated C++ code. PiperOrigin-RevId: 618930720 --- src/google/protobuf/compiler/cpp/BUILD.bazel | 18 +++++ .../compiler/cpp/namespace_printer.cc | 37 +++++++++ .../protobuf/compiler/cpp/namespace_printer.h | 58 ++++++++++++++ .../cpp/namespace_printer_unittest.cc | 78 +++++++++++++++++++ 4 files changed, 191 insertions(+) create mode 100644 src/google/protobuf/compiler/cpp/namespace_printer.cc create mode 100644 src/google/protobuf/compiler/cpp/namespace_printer.h create mode 100644 src/google/protobuf/compiler/cpp/namespace_printer_unittest.cc diff --git a/src/google/protobuf/compiler/cpp/BUILD.bazel b/src/google/protobuf/compiler/cpp/BUILD.bazel index c664a4bba0..7552a03051 100644 --- a/src/google/protobuf/compiler/cpp/BUILD.bazel +++ b/src/google/protobuf/compiler/cpp/BUILD.bazel @@ -71,6 +71,7 @@ cc_library( "generator.cc", "ifndef_guard.cc", "message.cc", + "namespace_printer.cc", "padding_optimizer.cc", "parse_function_generator.cc", "service.cc", @@ -86,6 +87,7 @@ cc_library( "ifndef_guard.h", "message.h", "message_layout_helper.h", + "namespace_printer.h", "padding_optimizer.h", "parse_function_generator.h", "service.h", @@ -359,6 +361,22 @@ cc_test( ], ) +cc_test( + name = "namespace_printer_unittest", + srcs = ["namespace_printer_unittest.cc"], + deps = [ + ":cpp", + "//:protobuf", + "//src/google/protobuf/io", + "//src/google/protobuf/io:printer", + "@com_google_absl//absl/log:absl_check", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + ################################################################################ # Distribution packaging ################################################################################ diff --git a/src/google/protobuf/compiler/cpp/namespace_printer.cc b/src/google/protobuf/compiler/cpp/namespace_printer.cc new file mode 100644 index 0000000000..3531d7c737 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/namespace_printer.cc @@ -0,0 +1,37 @@ +#include "google/protobuf/compiler/cpp/namespace_printer.h" + +#include +#include +#include + +#include "absl/log/die_if_null.h" +#include "absl/strings/substitute.h" +#include "google/protobuf/io/printer.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +NamespacePrinter::NamespacePrinter( + google::protobuf::io::Printer* const p, std::vector namespace_components) + : p_(ABSL_DIE_IF_NULL(p)), + namespace_components_(std::move(namespace_components)) { + // Open the namespace. + for (const std::string& ns : namespace_components_) { + p_->Print(absl::Substitute("namespace $0 {\n", ns)); + } + p_->Print("\n"); +} + +NamespacePrinter::~NamespacePrinter() { + // Close the namespace. + for (const std::string& ns : namespace_components_) { + p_->Print(absl::Substitute("} // namespace $0\n", ns)); + } +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/namespace_printer.h b/src/google/protobuf/compiler/cpp/namespace_printer.h new file mode 100644 index 0000000000..bb39037566 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/namespace_printer.h @@ -0,0 +1,58 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2024 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +// An RAII type for printing a namespace. +// +// Example: +// { +// Printer printer(output_stream.get(), '$'); +// const NamespacePrinter namespace_printer(&printer, {"a", "b", "c"}); +// // namespace opening will be opened here +// ... +// // namespace closing will be emitted here +// } +// +// By default, the filename will be converted to a macro by substituting '/' and +// '.' characters with '_'. If a different transformation is required, an +// optional transformation function can be provided. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_NAMESPACE_PRINTER_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_NAMESPACE_PRINTER_H__ + +#include +#include + +#include "google/protobuf/io/printer.h" + +// Must be included last. +#include "google/protobuf/port_def.inc" + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +// An RAII type for printing a namespace. +class PROTOC_EXPORT NamespacePrinter final { + public: + explicit NamespacePrinter(google::protobuf::io::Printer* p, + std::vector namespace_components); + ~NamespacePrinter(); + + private: + google::protobuf::io::Printer* const p_; + const std::vector namespace_components_; +}; + +#include "google/protobuf/port_undef.inc" + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_NAMESPACE_PRINTER_H__ diff --git a/src/google/protobuf/compiler/cpp/namespace_printer_unittest.cc b/src/google/protobuf/compiler/cpp/namespace_printer_unittest.cc new file mode 100644 index 0000000000..3e7fab9b79 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/namespace_printer_unittest.cc @@ -0,0 +1,78 @@ +#include "google/protobuf/compiler/cpp/namespace_printer.h" + +#include + +#include +#include "absl/log/absl_check.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "google/protobuf/io/printer.h" +#include "google/protobuf/io/zero_copy_stream.h" +#include "google/protobuf/io/zero_copy_stream_impl_lite.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +namespace { + +class NamespacePrinterTest : public testing::Test { + protected: + io::ZeroCopyOutputStream* output() { + ABSL_CHECK(stream_.has_value()); + return &*stream_; + } + absl::string_view written() { + stream_.reset(); + return out_; + } + + std::string out_; + absl::optional stream_{&out_}; +}; + +TEST_F(NamespacePrinterTest, Basic) { + { + io::Printer printer(output(), '$'); + + const NamespacePrinter namespace_printer(&printer, {"A", "B", "E"}); + + EXPECT_FALSE(printer.failed()); + } + + EXPECT_EQ(written(), + "namespace A {\n" + "namespace B {\n" + "namespace E {\n" + "\n" + "} // namespace A\n" + "} // namespace B\n" + "} // namespace E\n"); +} + +TEST_F(NamespacePrinterTest, DifferentDelim) { + { + io::Printer printer(output(), '\0'); + + const NamespacePrinter namespace_printer(&printer, {"A", "B", "E"}); + + EXPECT_FALSE(printer.failed()); + } + + EXPECT_EQ(written(), + "namespace A {\n" + "namespace B {\n" + "namespace E {\n" + "\n" + "} // namespace A\n" + "} // namespace B\n" + "} // namespace E\n"); +} + +} // namespace + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google