This is an automated email from the ASF dual-hosted git repository.

mgrigorov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/main by this push:
     new 19501fc00 [AVRO-3967] Replace boost::format with fmt library (#2832)
19501fc00 is described below

commit 19501fc00ff1f18ecf9c65baf670dc92c0b64891
Author: Mikhail Koviazin <[email protected]>
AuthorDate: Mon Apr 29 16:44:38 2024 +0300

    [AVRO-3967] Replace boost::format with fmt library (#2832)
    
    * Replace boost::format with fmt library
    
    This reduces the binary size quite significantly and doesn't require an
    addtitional object creation during exception throwing.
    
    * cmake: get fmt with FetchContent
    
    * cmake: always use fmt-header-only
---
 .github/workflows/test-lang-c++-ARM.yml |  2 +-
 .github/workflows/test-lang-c++.yml     |  2 +-
 lang/c++/CMakeLists.txt                 | 13 +++++++++++-
 lang/c++/api/Exception.hh               |  8 +++++---
 lang/c++/api/Node.hh                    |  9 ++++++++-
 lang/c++/api/NodeImpl.hh                |  6 +++---
 lang/c++/api/Stream.hh                  |  1 +
 lang/c++/api/Types.hh                   |  8 ++++++++
 lang/c++/api/Validator.hh               |  8 ++------
 lang/c++/impl/BinaryDecoder.cc          |  8 +++-----
 lang/c++/impl/Compiler.cc               | 35 ++++++++++++++++-----------------
 lang/c++/impl/DataFile.cc               | 12 +++++------
 lang/c++/impl/FileStream.cc             | 18 ++++++++---------
 lang/c++/impl/Generic.cc                |  6 +++---
 lang/c++/impl/GenericDatum.cc           |  2 +-
 lang/c++/impl/LogicalType.cc            |  4 ++--
 lang/c++/impl/Node.cc                   |  9 ++++-----
 lang/c++/impl/NodeImpl.cc               |  4 ++--
 lang/c++/impl/ValidSchema.cc            |  7 ++-----
 lang/c++/impl/Validator.cc              |  5 ++---
 lang/c++/impl/json/JsonDom.cc           |  6 +-----
 lang/c++/impl/json/JsonIO.cc            | 26 +++++++-----------------
 lang/c++/impl/json/JsonIO.hh            |  2 +-
 lang/c++/impl/parsing/JsonCodec.cc      |  6 ++----
 24 files changed, 103 insertions(+), 104 deletions(-)

diff --git a/.github/workflows/test-lang-c++-ARM.yml 
b/.github/workflows/test-lang-c++-ARM.yml
index d27b6a5c5..f101eaeb2 100644
--- a/.github/workflows/test-lang-c++-ARM.yml
+++ b/.github/workflows/test-lang-c++-ARM.yml
@@ -44,7 +44,7 @@ jobs:
       - name: Install dependencies
         run: |
           sudo apt-get update -q
-          sudo apt-get install -q -y gcc g++ libboost-all-dev cmake
+          sudo apt-get install -q -y gcc g++ libboost-all-dev libfmt-dev cmake
 
       - name: Build
         run: |
diff --git a/.github/workflows/test-lang-c++.yml 
b/.github/workflows/test-lang-c++.yml
index 5747a0da3..3035aa66c 100644
--- a/.github/workflows/test-lang-c++.yml
+++ b/.github/workflows/test-lang-c++.yml
@@ -39,7 +39,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Install Dependencies
-        run: sudo apt update && sudo apt-get install -qqy cppcheck 
libboost-all-dev libsnappy-dev cmake
+        run: sudo apt update && sudo apt-get install -qqy cppcheck 
libboost-all-dev libsnappy-dev libfmt-dev cmake
 
       - name: Clean
         run: ./build.sh clean
diff --git a/lang/c++/CMakeLists.txt b/lang/c++/CMakeLists.txt
index f6520329a..a3ff655d9 100644
--- a/lang/c++/CMakeLists.txt
+++ b/lang/c++/CMakeLists.txt
@@ -78,6 +78,16 @@ endif ()
 find_package (Boost 1.38 REQUIRED
     COMPONENTS filesystem iostreams program_options regex system)
 
+include(FetchContent)
+FetchContent_Declare(
+        fmt
+        GIT_REPOSITORY  https://github.com/fmtlib/fmt.git
+        GIT_TAG         10.2.1
+        GIT_PROGRESS    TRUE
+        USES_TERMINAL_DOWNLOAD TRUE
+)
+FetchContent_MakeAvailable(fmt)
+
 find_package(Snappy)
 if (SNAPPY_FOUND)
     set(SNAPPY_PKG libsnappy)
@@ -121,6 +131,7 @@ set_property (TARGET avrocpp
 
 add_library (avrocpp_s STATIC ${AVRO_SOURCE_FILES})
 target_include_directories(avrocpp_s PRIVATE ${SNAPPY_INCLUDE_DIR})
+target_link_libraries(avrocpp_s fmt::fmt-header-only)
 
 set_property (TARGET avrocpp avrocpp_s
     APPEND PROPERTY COMPILE_DEFINITIONS AVRO_SOURCE)
@@ -131,7 +142,7 @@ set_target_properties (avrocpp PROPERTIES
 set_target_properties (avrocpp_s PROPERTIES
     VERSION ${AVRO_VERSION_MAJOR}.${AVRO_VERSION_MINOR}.${AVRO_VERSION_PATCH})
 
-target_link_libraries (avrocpp ${Boost_LIBRARIES} ${SNAPPY_LIBRARIES})
+target_link_libraries (avrocpp ${Boost_LIBRARIES} ${SNAPPY_LIBRARIES} 
fmt::fmt-header-only)
 target_include_directories(avrocpp PRIVATE ${SNAPPY_INCLUDE_DIR})
 
 add_executable (precompile test/precompile.cc)
diff --git a/lang/c++/api/Exception.hh b/lang/c++/api/Exception.hh
index 691869bed..91c7a556f 100644
--- a/lang/c++/api/Exception.hh
+++ b/lang/c++/api/Exception.hh
@@ -20,19 +20,21 @@
 #define avro_Exception_hh__
 
 #include "Config.hh"
-#include <boost/format.hpp>
 #include <stdexcept>
+#include <fmt/core.h>
 
 namespace avro {
 
 /// Wrapper for std::runtime_error that provides convenience constructor
-/// for boost::format objects
+/// for formatted messages
 
 class AVRO_DECL Exception : public virtual std::runtime_error {
 public:
     explicit Exception(const std::string &msg) : std::runtime_error(msg) {}
 
-    explicit Exception(const boost::format &msg) : 
std::runtime_error(boost::str(msg)) {}
+    template <typename... Args>
+    Exception(fmt::format_string<Args...> fmt, Args&&... args)
+        : std::runtime_error(fmt::format(fmt, std::forward<Args>(args)...) ) {}
 };
 
 } // namespace avro
diff --git a/lang/c++/api/Node.hh b/lang/c++/api/Node.hh
index 0b8f2269c..f4dff1739 100644
--- a/lang/c++/api/Node.hh
+++ b/lang/c++/api/Node.hh
@@ -144,7 +144,7 @@ public:
     virtual size_t leaves() const = 0;
     virtual const NodePtr &leafAt(size_t index) const = 0;
     virtual const GenericDatum &defaultValueAt(size_t index) {
-        throw Exception(boost::format("No default value at: %1%") % index);
+        throw Exception("No default value at: {}", index);
     }
 
     void addName(const std::string &name) {
@@ -216,4 +216,11 @@ inline std::ostream &operator<<(std::ostream &os, const 
avro::Node &n) {
 }
 } // namespace std
 
+template <> struct fmt::formatter<avro::Name> : fmt::formatter<std::string> {
+    template <typename FormatContext>
+    auto format(const avro::Name &n, FormatContext &ctx) {
+        return fmt::formatter<std::string>::format(n.fullname(), ctx);
+    }
+};
+
 #endif
diff --git a/lang/c++/api/NodeImpl.hh b/lang/c++/api/NodeImpl.hh
index 8372a3887..1845c6e52 100644
--- a/lang/c++/api/NodeImpl.hh
+++ b/lang/c++/api/NodeImpl.hh
@@ -129,7 +129,7 @@ protected:
 
     void doAddName(const std::string &name) override {
         if (!nameIndex_.add(name, leafNameAttributes_.size())) {
-            throw Exception(boost::format("Cannot add duplicate name: %1%") % 
name);
+            throw Exception("Cannot add duplicate name: {}", name);
         }
         leafNameAttributes_.add(name);
     }
@@ -280,7 +280,7 @@ public:
     NodePtr getNode() const {
         NodePtr node = actualNode_.lock();
         if (!node) {
-            throw Exception(boost::format("Could not follow symbol %1%") % 
name());
+            throw Exception("Could not follow symbol {}", name());
         }
         return node;
     }
@@ -345,7 +345,7 @@ public:
     NodeEnum(const HasName &name, const LeafNames &symbols) : 
NodeImplEnum(AVRO_ENUM, name, NoLeaves(), symbols, NoAttributes(), NoSize()) {
         for (size_t i = 0; i < leafNameAttributes_.size(); ++i) {
             if (!nameIndex_.add(leafNameAttributes_.get(i), i)) {
-                throw Exception(boost::format("Cannot add duplicate enum: 
%1%") % leafNameAttributes_.get(i));
+                throw Exception("Cannot add duplicate enum: {}", 
leafNameAttributes_.get(i));
             }
         }
     }
diff --git a/lang/c++/api/Stream.hh b/lang/c++/api/Stream.hh
index fe2c97ee2..81448d26d 100644
--- a/lang/c++/api/Stream.hh
+++ b/lang/c++/api/Stream.hh
@@ -22,6 +22,7 @@
 #include <cstdint>
 #include <cstring>
 #include <memory>
+#include <vector>
 
 #include "boost/utility.hpp"
 
diff --git a/lang/c++/api/Types.hh b/lang/c++/api/Types.hh
index e3296ae0d..e7903d864 100644
--- a/lang/c++/api/Types.hh
+++ b/lang/c++/api/Types.hh
@@ -20,6 +20,7 @@
 #define avro_Types_hh__
 
 #include <iostream>
+#include <fmt/format.h>
 
 #include "Config.hh"
 
@@ -109,4 +110,11 @@ std::ostream &operator<<(std::ostream &os, const Null 
&null);
 
 } // namespace avro
 
+template <> struct fmt::formatter<avro::Type> : fmt::formatter<std::string> {
+    template <typename FormatContext>
+    auto format(avro::Type t, FormatContext &ctx) {
+        return fmt::formatter<std::string>::format(avro::toString(t), ctx);
+    }
+};
+
 #endif
diff --git a/lang/c++/api/Validator.hh b/lang/c++/api/Validator.hh
index ab5d068df..d7f6aecc1 100644
--- a/lang/c++/api/Validator.hh
+++ b/lang/c++/api/Validator.hh
@@ -88,18 +88,14 @@ public:
 
     void checkTypeExpected(Type type) {
         if (!typeIsExpected(type)) {
-            throw Exception(
-                boost::format("Type %1% does not match schema %2%")
-                % type % nextType_);
+            throw Exception("Type {} does not match schema {}", type, 
nextType_);
         }
         advance();
     }
 
     void checkFixedSizeExpected(int size) {
         if (nextSizeExpected() != size) {
-            throw Exception(
-                boost::format("Wrong size for fixed, got %1%, expected %2%")
-                % size % nextSizeExpected());
+            throw Exception("Wrong size for fixed, got {}, expected {}", size, 
nextSizeExpected());
         }
         checkTypeExpected(AVRO_FIXED);
     }
diff --git a/lang/c++/impl/BinaryDecoder.cc b/lang/c++/impl/BinaryDecoder.cc
index 248b50334..a970d6052 100644
--- a/lang/c++/impl/BinaryDecoder.cc
+++ b/lang/c++/impl/BinaryDecoder.cc
@@ -74,14 +74,13 @@ bool BinaryDecoder::decodeBool() {
     } else if (v == 1) {
         return true;
     }
-    throw Exception(boost::format("Invalid value for bool: %1%") % v);
+    throw Exception("Invalid value for bool: {}", v);
 }
 
 int32_t BinaryDecoder::decodeInt() {
     auto val = doDecodeLong();
     if (val < INT32_MIN || val > INT32_MAX) {
-        throw Exception(
-            boost::format("Value out of range for Avro int: %1%") % val);
+        throw Exception("Value out of range for Avro int: {}", val);
     }
     return static_cast<int32_t>(val);
 }
@@ -105,8 +104,7 @@ double BinaryDecoder::decodeDouble() {
 size_t BinaryDecoder::doDecodeLength() {
     ssize_t len = decodeInt();
     if (len < 0) {
-        throw Exception(
-            boost::format("Cannot have negative length: %1%") % len);
+        throw Exception("Cannot have negative length: {}", len);
     }
     return len;
 }
diff --git a/lang/c++/impl/Compiler.cc b/lang/c++/impl/Compiler.cc
index f46de055e..fb0fe32d5 100644
--- a/lang/c++/impl/Compiler.cc
+++ b/lang/c++/impl/Compiler.cc
@@ -96,7 +96,7 @@ static NodePtr makeNode(const string &t, SymbolTable &st, 
const string &ns) {
     if (it != st.end()) {
         return NodePtr(new NodeSymbolic(asSingleAttribute(n), it->second));
     }
-    throw Exception(boost::format("Unknown type: %1%") % n.fullname());
+    throw Exception("Unknown type: {}", n);
 }
 
 /** Returns "true" if the field is in the container */
@@ -112,7 +112,7 @@ json::Object::const_iterator findField(const Entity &e,
 template<typename T>
 void ensureType(const Entity &e, const string &name) {
     if (e.type() != json::type_traits<T>::type()) {
-        throw Exception(boost::format("Json field \"%1%\" is not a %2%: %3%") 
% name % json::type_traits<T>::name() % e.toString());
+        throw Exception("Json field \"{}\" is not a {}: {}", name, 
json::type_traits<T>::name(), e.toString());
     }
 }
 
@@ -158,9 +158,9 @@ struct Field {
 
 static void assertType(const Entity &e, EntityType et) {
     if (e.type() != et) {
-        throw Exception(boost::format("Unexpected type for default value: "
-                                      "Expected %1%, but found %2% in line 
%3%")
-                        % json::typeToString(et) % 
json::typeToString(e.type()) % e.line());
+        throw Exception(
+                "Unexpected type for default value: Expected {}, but found {} 
in line {}",
+                json::typeToString(et), json::typeToString(e.type()), 
e.line());
     }
 }
 
@@ -219,9 +219,9 @@ static GenericDatum makeGenericDatum(NodePtr n,
             for (size_t i = 0; i < n->leaves(); ++i) {
                 auto it = v.find(n->nameAt(i));
                 if (it == v.end()) {
-                    throw Exception(boost::format(
-                                        "No value found in default for %1%")
-                                    % n->nameAt(i));
+                    throw Exception(
+                            "No value found in default for {}",
+                            n->nameAt(i));
                 }
                 result.setFieldAt(i,
                                   makeGenericDatum(n->leafAt(i), it->second, 
st));
@@ -259,7 +259,7 @@ static GenericDatum makeGenericDatum(NodePtr n,
         case AVRO_FIXED:
             assertType(e, json::EntityType::String);
             return GenericDatum(n, GenericFixed(n, toBin(e.bytesValue())));
-        default: throw Exception(boost::format("Unknown type: %1%") % t);
+        default: throw Exception("Unknown type: {}", t);
     }
 }
 
@@ -380,7 +380,7 @@ static NodePtr makeEnumNode(const Entity &e,
     concepts::MultiAttribute<string> symbols;
     for (const auto &it : v) {
         if (it.type() != json::EntityType::String) {
-            throw Exception(boost::format("Enum symbol not a string: %1%") % 
it.toString());
+            throw Exception("Enum symbol not a string: {}", it.toString());
         }
         symbols.add(it.stringValue());
     }
@@ -395,7 +395,7 @@ static NodePtr makeFixedNode(const Entity &e,
                              const Name &name, const Object &m) {
     int v = static_cast<int>(getLongField(e, m, "size"));
     if (v <= 0) {
-        throw Exception(boost::format("Size for fixed is not positive: %1%") % 
e.toString());
+        throw Exception("Size for fixed is not positive: {}", e.toString());
     }
     NodePtr node =
         NodePtr(new NodeFixed(asSingleAttribute(name), asSingleAttribute(v)));
@@ -438,9 +438,9 @@ static Name getName(const Entity &e, const Object &m, const 
string &ns) {
         auto it = m.find("namespace");
         if (it != m.end()) {
             if (it->second.type() != json::type_traits<string>::type()) {
-                throw Exception(boost::format(
-                                    "Json field \"%1%\" is not a %2%: %3%")
-                                % "namespace" % 
json::type_traits<string>::name() % it->second.toString());
+                throw Exception(
+                        "Json field \"namespace\" is not a string: {}",
+                        it->second.toString());
             }
             result = Name(name, it->second.stringValue());
         } else {
@@ -500,8 +500,7 @@ static NodePtr makeNode(const Entity &e, const Object &m,
         return result;
     }
 
-    throw Exception(boost::format("Unknown type definition: %1%")
-                    % e.toString());
+    throw Exception("Unknown type definition: %1%", e.toString());
 }
 
 static NodePtr makeNode(const Entity &e, const Array &m,
@@ -518,13 +517,13 @@ static NodePtr makeNode(const json::Entity &e, 
SymbolTable &st, const string &ns
         case json::EntityType::String: return makeNode(e.stringValue(), st, 
ns);
         case json::EntityType::Obj: return makeNode(e, e.objectValue(), st, 
ns);
         case json::EntityType::Arr: return makeNode(e, e.arrayValue(), st, ns);
-        default: throw Exception(boost::format("Invalid Avro type: %1%") % 
e.toString());
+        default: throw Exception("Invalid Avro type: {}", e.toString());
     }
 }
 json::Object::const_iterator findField(const Entity &e, const Object &m, const 
string &fieldName) {
     auto it = m.find(fieldName);
     if (it == m.end()) {
-        throw Exception(boost::format("Missing Json field \"%1%\": %2%") % 
fieldName % e.toString());
+        throw Exception("Missing Json field \"{}\": {}", fieldName, 
e.toString());
     } else {
         return it;
     }
diff --git a/lang/c++/impl/DataFile.cc b/lang/c++/impl/DataFile.cc
index e58bc4f99..eab417f80 100644
--- a/lang/c++/impl/DataFile.cc
+++ b/lang/c++/impl/DataFile.cc
@@ -93,9 +93,9 @@ 
DataFileWriterBase::DataFileWriterBase(std::unique_ptr<OutputStream> outputStrea
 
 void DataFileWriterBase::init(const ValidSchema &schema, size_t syncInterval, 
const Codec &codec) {
     if (syncInterval < minSyncInterval || syncInterval > maxSyncInterval) {
-        throw Exception(boost::format("Invalid sync interval: %1%. "
-                                      "Should be between %2% and %3%")
-                        % syncInterval % minSyncInterval % maxSyncInterval);
+        throw Exception(
+                "Invalid sync interval: {}. Should be between {} and {}",
+                syncInterval, minSyncInterval, maxSyncInterval);
     }
     setMetadata(AVRO_CODEC_KEY, AVRO_NULL_CODEC);
 
@@ -108,7 +108,7 @@ void DataFileWriterBase::init(const ValidSchema &schema, 
size_t syncInterval, co
         setMetadata(AVRO_CODEC_KEY, AVRO_SNAPPY_CODEC);
 #endif
     } else {
-        throw Exception(boost::format("Unknown codec: %1%") % codec);
+        throw Exception("Unknown codec: {}", int(codec));
     }
     setMetadata(AVRO_SCHEMA_KEY, schema.toJson(false));
 
@@ -412,8 +412,8 @@ void DataFileReaderBase::readDataBlock() {
         uint32_t c = crc();
         if (checksum != c) {
             throw Exception(
-                boost::format("Checksum did not match for Snappy compression: 
Expected: %1%, computed: %2%") % checksum
-                % c);
+                    "Checksum did not match for Snappy compression: Expected: 
{}, computed: {}",
+                    checksum, c);
         }
         os_.reset(new boost::iostreams::filtering_istream());
         os_->push(
diff --git a/lang/c++/impl/FileStream.cc b/lang/c++/impl/FileStream.cc
index 17926af9d..ccfb44151 100644
--- a/lang/c++/impl/FileStream.cc
+++ b/lang/c++/impl/FileStream.cc
@@ -51,7 +51,7 @@ struct FileBufferCopyIn : public BufferCopyIn {
     HANDLE h_;
     explicit FileBufferCopyIn(const char *filename) : 
h_(::CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_ALWAYS, 
FILE_ATTRIBUTE_NORMAL, NULL)) {
         if (h_ == INVALID_HANDLE_VALUE) {
-            throw Exception(boost::format("Cannot open file: %1%") % 
::GetLastError());
+            throw Exception("Cannot open file: {}", ::GetLastError());
         }
     }
 
@@ -61,14 +61,14 @@ struct FileBufferCopyIn : public BufferCopyIn {
 
     void seek(size_t len) override {
         if (::SetFilePointer(h_, len, NULL, FILE_CURRENT) == 
INVALID_SET_FILE_POINTER && ::GetLastError() != NO_ERROR) {
-            throw Exception(boost::format("Cannot skip file: %1%") % 
::GetLastError());
+            throw Exception("Cannot skip file: {}", ::GetLastError());
         }
     }
 
     bool read(uint8_t *b, size_t toRead, size_t &actual) override {
         DWORD dw = 0;
         if (!::ReadFile(h_, b, toRead, &dw, NULL)) {
-            throw Exception(boost::format("Cannot read file: %1%") % 
::GetLastError());
+            throw Exception("Cannot read file: {}", ::GetLastError());
         }
         actual = static_cast<size_t>(dw);
         return actual != 0;
@@ -78,7 +78,7 @@ struct FileBufferCopyIn : public BufferCopyIn {
 
     explicit FileBufferCopyIn(const char *filename) : fd_(open(filename, 
O_RDONLY | O_BINARY)) {
         if (fd_ < 0) {
-            throw Exception(boost::format("Cannot open file: %1%") % 
::strerror(errno));
+            throw Exception("Cannot open file: {}", strerror(errno));
         }
     }
 
@@ -89,7 +89,7 @@ struct FileBufferCopyIn : public BufferCopyIn {
     void seek(size_t len) final {
         off_t r = ::lseek(fd_, len, SEEK_CUR);
         if (r == static_cast<off_t>(-1)) {
-            throw Exception(boost::format("Cannot skip file: %1%") % 
strerror(errno));
+            throw Exception("Cannot skip file: {}", strerror(errno));
         }
     }
 
@@ -234,7 +234,7 @@ struct FileBufferCopyOut : public BufferCopyOut {
     HANDLE h_;
     explicit FileBufferCopyOut(const char *filename) : 
h_(::CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
FILE_ATTRIBUTE_NORMAL, NULL)) {
         if (h_ == INVALID_HANDLE_VALUE) {
-            throw Exception(boost::format("Cannot open file: %1%") % 
::GetLastError());
+            throw Exception("Cannot open file: {}", ::GetLastError());
         }
     }
 
@@ -246,7 +246,7 @@ struct FileBufferCopyOut : public BufferCopyOut {
         while (len > 0) {
             DWORD dw = 0;
             if (!::WriteFile(h_, b, len, &dw, NULL)) {
-                throw Exception(boost::format("Cannot read file: %1%") % 
::GetLastError());
+                throw Exception("Cannot read file: {}", ::GetLastError());
             }
             b += dw;
             len -= dw;
@@ -258,7 +258,7 @@ struct FileBufferCopyOut : public BufferCopyOut {
     explicit FileBufferCopyOut(const char *filename) : fd_(::open(filename, 
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644)) {
 
         if (fd_ < 0) {
-            throw Exception(boost::format("Cannot open file: %1%") % 
::strerror(errno));
+            throw Exception("Cannot open file: {}", ::strerror(errno));
         }
     }
 
@@ -268,7 +268,7 @@ struct FileBufferCopyOut : public BufferCopyOut {
 
     void write(const uint8_t *b, size_t len) final {
         if (::write(fd_, b, len) < 0) {
-            throw Exception(boost::format("Cannot write file: %1%") % 
::strerror(errno));
+            throw Exception("Cannot write file: {}", ::strerror(errno));
         }
     }
 #endif
diff --git a/lang/c++/impl/Generic.cc b/lang/c++/impl/Generic.cc
index 6e0436ae3..1535c604b 100644
--- a/lang/c++/impl/Generic.cc
+++ b/lang/c++/impl/Generic.cc
@@ -29,7 +29,7 @@ typedef vector<uint8_t> bytes;
 
 void GenericContainer::assertType(const NodePtr &schema, Type type) {
     if (schema->type() != type) {
-        throw Exception(boost::format("Schema type %1 expected %2") % 
toString(schema->type()) % toString(type));
+        throw Exception("Schema type {} expected {}", schema->type(), type);
     }
 }
 
@@ -129,7 +129,7 @@ void GenericReader::read(GenericDatum &datum, Decoder &d, 
bool isResolving) {
             }
         } break;
         default:
-            throw Exception(boost::format("Unknown schema type %1%") % 
toString(datum.type()));
+            throw Exception("Unknown schema type {}", datum.type());
     }
 }
 
@@ -217,7 +217,7 @@ void GenericWriter::write(const GenericDatum &datum, 
Encoder &e) {
             e.mapEnd();
         } break;
         default:
-            throw Exception(boost::format("Unknown schema type %1%") % 
toString(datum.type()));
+            throw Exception("Unknown schema type {}", datum.type());
     }
 }
 
diff --git a/lang/c++/impl/GenericDatum.cc b/lang/c++/impl/GenericDatum.cc
index 7b2bf93bc..49700a927 100644
--- a/lang/c++/impl/GenericDatum.cc
+++ b/lang/c++/impl/GenericDatum.cc
@@ -83,7 +83,7 @@ void GenericDatum::init(const NodePtr &schema) {
             value_ = GenericUnion(sc);
             break;
         default:
-            throw Exception(boost::format("Unknown schema type %1%") % 
toString(type_));
+            throw Exception("Unknown schema type {}", toString(type_));
     }
 }
 
diff --git a/lang/c++/impl/LogicalType.cc b/lang/c++/impl/LogicalType.cc
index 1aa24bf20..988eed56d 100644
--- a/lang/c++/impl/LogicalType.cc
+++ b/lang/c++/impl/LogicalType.cc
@@ -33,7 +33,7 @@ void LogicalType::setPrecision(int precision) {
         throw Exception("Only logical type DECIMAL can have precision");
     }
     if (precision <= 0) {
-        throw Exception(boost::format("Precision cannot be: %1%") % precision);
+        throw Exception("Precision cannot be: {}", precision);
     }
     precision_ = precision;
 }
@@ -43,7 +43,7 @@ void LogicalType::setScale(int scale) {
         throw Exception("Only logical type DECIMAL can have scale");
     }
     if (scale < 0) {
-        throw Exception(boost::format("Scale cannot be: %1%") % scale);
+        throw Exception("Scale cannot be: {}", scale);
     }
     scale_ = scale;
 }
diff --git a/lang/c++/impl/Node.cc b/lang/c++/impl/Node.cc
index 56ad1044b..0c97a9025 100644
--- a/lang/c++/impl/Node.cc
+++ b/lang/c++/impl/Node.cc
@@ -149,11 +149,10 @@ void Node::setLogicalType(LogicalType logicalType) {
                 long maxPrecision = floor(log10(2.0) * (8.0 * fixedSize() - 
1));
                 if (logicalType.precision() > maxPrecision) {
                     throw Exception(
-                        boost::format(
-                            "DECIMAL precision %1% is too large for the "
-                            "FIXED type of size %2%, precision cannot be "
-                            "larger than %3%")
-                        % logicalType.precision() % fixedSize() % 
maxPrecision);
+                            "DECIMAL precision {} is too large for the "
+                            "FIXED type of size {}, precision cannot be "
+                            "larger than {}",
+                            logicalType.precision(), fixedSize(), 
maxPrecision);
                 }
             }
             if (logicalType.scale() > logicalType.precision()) {
diff --git a/lang/c++/impl/NodeImpl.cc b/lang/c++/impl/NodeImpl.cc
index 3d1f80a95..bcfc28947 100644
--- a/lang/c++/impl/NodeImpl.cc
+++ b/lang/c++/impl/NodeImpl.cc
@@ -482,13 +482,13 @@ NodeRecord::NodeRecord(const HasName &name, const HasDoc 
&doc, const MultiLeaves
 
     for (size_t i = 0; i < leafNameAttributes_.size(); ++i) {
         if (!nameIndex_.add(leafNameAttributes_.get(i), i)) {
-            throw Exception(boost::format("Cannot add duplicate field: %1%") % 
leafNameAttributes_.get(i));
+            throw Exception("Cannot add duplicate field: {}", 
leafNameAttributes_.get(i));
         }
 
         if (!fieldsAliases_.empty()) {
             for (const auto &alias : fieldsAliases_[i]) {
                 if (!nameIndex_.add(alias, i)) {
-                    throw Exception(boost::format("Cannot add duplicate field: 
%1%") % alias);
+                    throw Exception("Cannot add duplicate field: {}", alias);
                 }
             }
         }
diff --git a/lang/c++/impl/ValidSchema.cc b/lang/c++/impl/ValidSchema.cc
index 1356d8448..d99d7e241 100644
--- a/lang/c++/impl/ValidSchema.cc
+++ b/lang/c++/impl/ValidSchema.cc
@@ -16,7 +16,6 @@
  * limitations under the License.
  */
 
-#include <boost/format.hpp>
 #include <cctype>
 #include <sstream>
 #include <utility>
@@ -25,7 +24,6 @@
 #include "Schema.hh"
 #include "ValidSchema.hh"
 
-using boost::format;
 using std::make_pair;
 using std::ostringstream;
 using std::shared_ptr;
@@ -37,8 +35,7 @@ using SymbolMap = std::map<Name, NodePtr>;
 
 static bool validate(const NodePtr &node, SymbolMap &symbolMap) {
     if (!node->isValid()) {
-        throw Exception(format("Schema is invalid, due to bad node of type 
%1%")
-                        % node->type());
+        throw Exception("Schema is invalid, due to bad node of type {}", 
node->type());
     }
 
     if (node->hasName()) {
@@ -51,7 +48,7 @@ static bool validate(const NodePtr &node, SymbolMap 
&symbolMap) {
 
         if (node->type() == AVRO_SYMBOLIC) {
             if (!found) {
-                throw Exception(format("Symbolic name \"%1%\" is unknown") % 
node->name());
+                throw Exception("Symbolic name \"{}\" is unknown", 
node->name());
             }
 
             shared_ptr<NodeSymbolic> symNode =
diff --git a/lang/c++/impl/Validator.cc b/lang/c++/impl/Validator.cc
index 3cd4e9e49..d4cda11e2 100644
--- a/lang/c++/impl/Validator.cc
+++ b/lang/c++/impl/Validator.cc
@@ -105,9 +105,8 @@ void Validator::unionAdvance() {
             setupOperation(node->leafAt(static_cast<int>(count_)));
         } else {
             throw Exception(
-                boost::format("Union selection out of range, got %1%,"
-                              " expecting 0-%2%")
-                % count_ % (node->leaves() - 1));
+                "Union selection out of range, got {}, expecting 0-{}",
+                            count_, node->leaves() - 1);
         }
     }
 }
diff --git a/lang/c++/impl/json/JsonDom.cc b/lang/c++/impl/json/JsonDom.cc
index 5bffda255..c2696d827 100644
--- a/lang/c++/impl/json/JsonDom.cc
+++ b/lang/c++/impl/json/JsonDom.cc
@@ -25,9 +25,6 @@
 #include "JsonIO.hh"
 #include "Stream.hh"
 
-using boost::format;
-using std::string;
-
 namespace avro {
 namespace json {
 const char *typeToString(EntityType t) {
@@ -142,8 +139,7 @@ void writeEntity(JsonGenerator<JsonNullFormatter> &g, const 
Entity &n) {
 
 void Entity::ensureType(EntityType type) const {
     if (type_ != type) {
-        format msg = format("Invalid type. Expected \"%1%\" actual %2%") % 
typeToString(type) % typeToString(type_);
-        throw Exception(msg);
+        throw Exception("Invalid type. Expected \"{}\" actual {}", 
typeToString(type), typeToString(type_));
     }
 }
 
diff --git a/lang/c++/impl/json/JsonIO.cc b/lang/c++/impl/json/JsonIO.cc
index d30d1fded..dad12c554 100644
--- a/lang/c++/impl/json/JsonIO.cc
+++ b/lang/c++/impl/json/JsonIO.cc
@@ -339,7 +339,7 @@ string JsonParser::decodeString(const string &s, bool 
binary) {
             } else if (c >= 'A' && c <= 'F') {
                 n += c - 'A' + 10;
             } else {
-                throw Exception(boost::format( "Invalid hex character: %1%") % 
c);
+                throw Exception("Invalid hex character: {}", c);
             }
         }
         return n;
@@ -375,9 +375,7 @@ string JsonParser::decodeString(const string &s, bool 
binary) {
                     uint32_t n = unicodeParse();
                     if (binary) {
                         if (n > 0xff) {
-                            throw Exception(boost::format(
-                                                "Invalid byte for binary: 
%1%%2%")
-                                            % ch % string(startSeq, ++it));
+                            throw Exception("Invalid byte for binary: {}{}", 
ch, string(startSeq, ++it));
                         } else {
                             result.push_back(n);
                             continue;
@@ -386,27 +384,19 @@ string JsonParser::decodeString(const string &s, bool 
binary) {
                     if (n >= 0xd800 && n < 0xdc00) {
                         ch = readNextByte();
                         if (ch != '\\') {
-                            throw Exception(boost::format(
-                                                "Invalid unicode sequence: 
%1%")
-                                            % string(startSeq, it));
+                            throw Exception("Invalid unicode sequence: {}", 
string(startSeq, it));
                         }
                         ch = readNextByte();
                         if (ch != 'u' && ch != 'U') {
-                            throw Exception(boost::format(
-                                                "Invalid unicode sequence: 
%1%")
-                                            % string(startSeq, it));
+                            throw Exception("Invalid unicode sequence: {}", 
string(startSeq, it));
                         }
                         uint32_t m = unicodeParse();
                         if (m < 0xdc00 || m > 0xdfff) {
-                            throw Exception(boost::format(
-                                                "Invalid unicode sequence: 
%1%")
-                                            % string(startSeq, it));
+                            throw Exception("Invalid unicode sequence: {}", 
string(startSeq, it));
                         }
                         n = 0x10000 + (((n - 0xd800) << 10) | (m - 0xdc00));
                     } else if (n >= 0xdc00 && n < 0xdfff) {
-                        throw Exception(boost::format(
-                                            "Invalid unicode sequence: %1%")
-                                        % string(startSeq, it));
+                        throw Exception("Invalid unicode sequence: {}", 
string(startSeq, it));
                     }
                     if (n < 0x80) {
                         result.push_back(n);
@@ -423,9 +413,7 @@ string JsonParser::decodeString(const string &s, bool 
binary) {
                         result.push_back(((n >> 6) & 0x3f) | 0x80);
                         result.push_back((n & 0x3f) | 0x80);
                     } else {
-                        throw Exception(boost::format(
-                                            "Invalid unicode value: %1%%2%")
-                                        % n % string(startSeq, ++it));
+                        throw Exception("Invalid unicode value: {}{}", n, 
string(startSeq, ++it));
                     }
                 }
                     continue;
diff --git a/lang/c++/impl/json/JsonIO.hh b/lang/c++/impl/json/JsonIO.hh
index 447c0b0df..4e3b2c115 100644
--- a/lang/c++/impl/json/JsonIO.hh
+++ b/lang/c++/impl/json/JsonIO.hh
@@ -277,7 +277,7 @@ class AVRO_DECL JsonGenerator {
             escapeUnicode16(((c >> 10) & 0x3ff) | 0xd800);
             escapeUnicode16((c & 0x3ff) | 0xdc00);
         } else {
-            throw Exception(boost::format("Invalid code-point: %1%") % c);
+            throw Exception("Invalid code-point: {}", c);
         }
     }
     void doEncodeString(const char *b, size_t len, bool binary) {
diff --git a/lang/c++/impl/parsing/JsonCodec.cc 
b/lang/c++/impl/parsing/JsonCodec.cc
index a33fd39fd..84b366606 100644
--- a/lang/c++/impl/parsing/JsonCodec.cc
+++ b/lang/c++/impl/parsing/JsonCodec.cc
@@ -162,8 +162,7 @@ public:
             case Symbol::Kind::Field:
                 expectToken(in_, JsonParser::Token::String);
                 if (s.extra<string>() != in_.stringValue()) {
-                    throw Exception(boost::format("Incorrect field: expected 
\"%1%\" but got \"%2%\".") % 
-                        s.extra<string>() % in_.stringValue());
+                    throw Exception(R"(Incorrect field: expected "{}" but got 
"{}".)", s.extra<string>(), in_.stringValue());
                 }
                 break;
             default:
@@ -241,8 +240,7 @@ int32_t JsonDecoder<P>::decodeInt() {
     expect(JsonParser::Token::Long);
     int64_t result = in_.longValue();
     if (result < INT32_MIN || result > INT32_MAX) {
-        throw Exception(boost::format("Value out of range for Avro int: %1%")
-                        % result);
+        throw Exception("Value out of range for Avro int: {}", result);
     }
     return static_cast<int32_t>(result);
 }


Reply via email to