Skip to content

Commit

Permalink
[AVRO-3967] Replace boost::format with fmt library (#2832)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
mkmkme authored Apr 29, 2024
1 parent f40c222 commit 19501fc
Show file tree
Hide file tree
Showing 24 changed files with 103 additions and 104 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-lang-c++-ARM.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-lang-c++.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 12 additions & 1 deletion lang/c++/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
8 changes: 5 additions & 3 deletions lang/c++/api/Exception.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion lang/c++/api/Node.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
6 changes: 3 additions & 3 deletions lang/c++/api/NodeImpl.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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));
}
}
}
Expand Down
1 change: 1 addition & 0 deletions lang/c++/api/Stream.hh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <cstdint>
#include <cstring>
#include <memory>
#include <vector>

#include "boost/utility.hpp"

Expand Down
8 changes: 8 additions & 0 deletions lang/c++/api/Types.hh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#define avro_Types_hh__

#include <iostream>
#include <fmt/format.h>

#include "Config.hh"

Expand Down Expand Up @@ -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
8 changes: 2 additions & 6 deletions lang/c++/api/Validator.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
8 changes: 3 additions & 5 deletions lang/c++/impl/BinaryDecoder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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;
}
Expand Down
35 changes: 17 additions & 18 deletions lang/c++/impl/Compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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());
}
}

Expand Down Expand Up @@ -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());
}
}

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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());
}
Expand All @@ -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)));
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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;
}
Expand Down
12 changes: 6 additions & 6 deletions lang/c++/impl/DataFile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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));

Expand Down Expand Up @@ -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(
Expand Down
Loading

0 comments on commit 19501fc

Please sign in to comment.