Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5d60791
Add compatibility with fmtlib formatting for collections
tmadlener Feb 12, 2026
52fbb9d
Add fmt compatibility for ObjectID
tmadlener Feb 12, 2026
2a2bc9b
Add fmtlib compatibility for Links
tmadlener Feb 12, 2026
698a74e
Add fmt compatibility for generated objects
tmadlener Feb 12, 2026
44dd932
Add fmt compatibility for generated components
tmadlener Feb 12, 2026
a6d084b
Add fmt compatibility for interface types
tmadlener Feb 13, 2026
ac81cc2
Add fmt support for ObjectId
tmadlener Feb 12, 2026
9fb2833
Add fmt support for Links
tmadlener Feb 12, 2026
0cf8ce1
Add fmt support for LinkCollections
tmadlener Feb 12, 2026
8b3b3b4
Add fmt support for Version
tmadlener Feb 12, 2026
5b8078d
Make UserDataCollection formattable as range
tmadlener Feb 13, 2026
58f3ab1
Ensure Links and LinkCollections remain formattable
tmadlener Feb 13, 2026
848f148
Add proper fmt support for user facing handles
tmadlener Feb 13, 2026
76a7f68
Add fmt support for collections
tmadlener Feb 13, 2026
e8d4be8
Implement CollectionBase::print in terms of format
tmadlener Feb 13, 2026
f315e89
Implement GenericParamters::print in terms of format
tmadlener Feb 13, 2026
ce8bee8
Remove unnecessary template parameter specification
tmadlener Feb 13, 2026
87571a8
Add format support for GenericParameters
tmadlener Feb 13, 2026
163cead
Make sure fmt headers can be found by ROOT interactively
tmadlener Feb 13, 2026
6833c2c
Add brief format specifier to Link for more concise formatting
tmadlener Feb 13, 2026
a8db92e
Add brief format specifier to LinkCollection
tmadlener Feb 13, 2026
062a60f
First version of customization hook
tmadlener Feb 13, 2026
ea428e7
First version of tests that use custom formatting hooks
tmadlener Feb 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/python/__version__.py.in
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE
DESTINATION ${CMAKE_INSTALL_DOCDIR})

find_package(fmt 9 REQUIRED)

#--- project specific subdirectories -------------------------------------------
add_subdirectory(src)

Expand All @@ -188,8 +190,6 @@ if(BUILD_TESTING)
add_subdirectory(tests)
endif()

find_package(fmt 9 REQUIRED)

add_subdirectory(tools)
add_subdirectory(python)

Expand Down
1 change: 1 addition & 0 deletions cmake/podioConfig.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ if(NOT "@REQUIRE_PYTHON_VERSION@" STREQUAL "")
else()
find_dependency(Python3 COMPONENTS Interpreter Development)
endif()
find_dependency(fmt @fmt_VERSION@)

SET(ENABLE_SIO @ENABLE_SIO@)
if(ENABLE_SIO)
Expand Down
2 changes: 1 addition & 1 deletion cmake/podioTest.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function(PODIO_SET_TEST_ENV test)
LD_LIBRARY_PATH=${PROJECT_BINARY_DIR}/src:$<TARGET_FILE_DIR:ROOT::Tree>:$<$<TARGET_EXISTS:SIO::sio>:$<TARGET_FILE_DIR:SIO::sio>>:$ENV{LD_LIBRARY_PATH}
PYTHONPATH=${PROJECT_SOURCE_DIR}/python:$ENV{PYTHONPATH}
PODIO_SIOBLOCK_PATH=${PROJECT_BINARY_DIR}/tests
ROOT_INCLUDE_PATH=${PROJECT_SOURCE_DIR}/tests:${PROJECT_SOURCE_DIR}/include:$ENV{ROOT_INCLUDE_PATH}
ROOT_INCLUDE_PATH=${PROJECT_SOURCE_DIR}/tests:${PROJECT_SOURCE_DIR}/include:$ENV{ROOT_INCLUDE_PATH}:$<TARGET_FILE_DIR:fmt::fmt>/../include
SKIP_SIO_TESTS=$<NOT:$<BOOL:${ENABLE_SIO}>>
IO_HANDLERS=${IO_HANDLERS}
PODIO_USE_CLANG_FORMAT=${PODIO_USE_CLANG_FORMAT}
Expand Down
16 changes: 16 additions & 0 deletions include/podio/GenericParameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,20 @@ void GenericParameters::loadFrom(VecLike<std::string> keys, VecLike<std::vector<
}

} // namespace podio

#include <fmt/core.h>

template <>
struct fmt::formatter<podio::GenericParameters> {
constexpr auto parse(fmt::format_parse_context& ctx) {
auto it = ctx.begin();
if (it != ctx.end() && *it != '}') {
fmt::throw_format_error("Invalid format. GenericParameters does not support specifiers");
}
return it;
}

fmt::format_context::iterator format(const podio::GenericParameters& params, fmt::format_context& ctx) const;
};

#endif
33 changes: 25 additions & 8 deletions include/podio/ObjectID.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#ifndef PODIO_OBJECTID_H
#define PODIO_OBJECTID_H

#include <fmt/core.h>

#include <cstdint>
#include <functional>
#include <iomanip>
#include <iterator>
#include <ostream>

#if defined(PODIO_JSON_OUTPUT) && !defined(__CLING__)
Expand Down Expand Up @@ -35,13 +37,6 @@ class ObjectID {
}
};

inline std::ostream& operator<<(std::ostream& os, const podio::ObjectID& id) {
const auto oldFlags = os.flags();
os << std::hex << std::setw(8) << id.collectionID;
os.flags(oldFlags);
return os << "|" << id.index;
}

#if defined(PODIO_JSON_OUTPUT) && !defined(__CLING__)
inline void to_json(nlohmann::json& j, const podio::ObjectID& id) {
j = nlohmann::json{{"collectionID", id.collectionID}, {"index", id.index}};
Expand All @@ -60,4 +55,26 @@ struct std::hash<podio::ObjectID> {
}
};

template <>
struct fmt::formatter<podio::ObjectID> {
constexpr auto parse(fmt::format_parse_context& ctx) {
auto it = ctx.begin();
if (it != ctx.end() && *it != '}') {
fmt::throw_format_error("Invalid format. ObjectId does not support specifiers");
}
return it;
}

auto format(const podio::ObjectID& obj, fmt::format_context& ctx) const {
return fmt::format_to(ctx.out(), "{:8x}|{}", obj.collectionID, obj.index);
}
};

namespace podio {
inline std::ostream& operator<<(std::ostream& os, const podio::ObjectID& id) {
fmt::format_to(std::ostreambuf_iterator(os), "{}", id);
return os;
}
} // namespace podio

#endif
16 changes: 7 additions & 9 deletions include/podio/UserDataCollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
#include "podio/detail/Pythonizations.h"
#include "podio/utilities/TypeHelpers.h"

#include <fmt/ostream.h>
#include <fmt/ranges.h>

#include <iterator>

#define PODIO_ADD_USER_TYPE(type) \
template <> \
consteval const char* userDataTypeName<type>() { \
Expand Down Expand Up @@ -219,14 +224,7 @@ class UserDataCollection : public CollectionBase {

/// Print this collection to the passed stream
void print(std::ostream& os = std::cout, bool flush = true) const override {
os << "[";
if (!_vec.empty()) {
os << _vec[0];
for (size_t i = 1; i < _vec.size(); ++i) {
os << ", " << _vec[i];
}
}
os << "]";
os << fmt::format("{}", _vec);

if (flush) {
os.flush(); // Necessary for python
Expand Down Expand Up @@ -321,7 +319,7 @@ using UserDataCollectionTypes = decltype(std::apply(

template <SupportedUserDataType BasicType>
std::ostream& operator<<(std::ostream& o, const podio::UserDataCollection<BasicType>& coll) {
coll.print(o);
fmt::format_to(std::ostreambuf_iterator(o), "{}", coll);
return o;
}

Expand Down
70 changes: 58 additions & 12 deletions include/podio/detail/Link.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
#include "nlohmann/json.hpp"
#endif

#include "podio/utilities/FormatHelpers.h"

#include <fmt/core.h>
#include <fmt/ranges.h>

#include <functional>
#include <ostream>
#include <type_traits>
Expand Down Expand Up @@ -348,18 +353,6 @@ class LinkT {
podio::utils::MaybeSharedPtr<LinkObjT> m_obj{nullptr};
};

template <typename FromT, typename ToT>
std::ostream& operator<<(std::ostream& os, const Link<FromT, ToT>& link) {
if (!link.isAvailable()) {
return os << "[not available]";
}

return os << " id: " << link.id() << '\n'
<< " weight: " << link.getWeight() << '\n'
<< " from: " << link.getFrom().id() << '\n'
<< " to: " << link.getTo().id() << '\n';
}

#if defined(PODIO_JSON_OUTPUT) && !defined(__CLING__)
template <typename FromT, typename ToT>
void to_json(nlohmann::json& j, const podio::LinkT<FromT, ToT, false>& link) {
Expand All @@ -382,4 +375,57 @@ struct std::hash<podio::LinkT<FromT, ToT, Mutable>> {
}
};

template <typename FromT, typename ToT, bool Mutable>
struct fmt::formatter<podio::LinkT<FromT, ToT, Mutable>> {
char presentation = 'd'; // 'd' for default/detailed, 'b' for brief

constexpr auto parse(fmt::format_parse_context& ctx) {
auto it = ctx.begin();
auto end = ctx.end();

if (it != end && *it != '}') {
presentation = *it++;
if (presentation != 'b' && presentation != 'd' && presentation != 'u') {
fmt::throw_format_error("Invalid format specifier for Link. Use 'b' for brief, 'd' for detailed, or 'u' for user-defined");
}
}

if (it != end && *it != '}') {
fmt::throw_format_error("Invalid format specifier for Link");
}

return it;
}

auto format(const podio::LinkT<FromT, ToT, Mutable>& link, fmt::format_context& ctx) const {
if (presentation == 'u') {
return podio::detail::dispatchCustomFormat(link, ctx);
}
if (!link.isAvailable()) {
return fmt::format_to(ctx.out(), "[not available]");
}
if (presentation == 'b') {
return fmt::format_to(ctx.out(), "{} | {} {} {}", link.id(), link.getFrom().id(), link.getTo().id(),
link.getWeight());
}

return fmt::format_to(ctx.out(), " id: {}\n weight: {}\n from: {}\n to: {}\n", link.id(), link.getWeight(),
link.getFrom().id(), link.getTo().id());
}
};

// Disable fmt's tuple formatter for LinkT to avoid ambiguity with the custom
// formatter above. This is necessary because opting tuple_size and
// tuple_element makes LinkT behave like a tuple to the compiler
template <typename FromT, typename ToT, bool Mutable, typename Char>
struct fmt::is_tuple_formattable<podio::LinkT<FromT, ToT, Mutable>, Char> : std::false_type {};

namespace podio {
template <typename FromT, typename ToT, bool Mutable>
std::ostream& operator<<(std::ostream& os, const LinkT<FromT, ToT, Mutable>& link) {
fmt::format_to(std::ostreambuf_iterator(os), "{}", link);
return os;
}
} // namespace podio

#endif // PODIO_DETAIL_LINK_H
79 changes: 59 additions & 20 deletions include/podio/detail/LinkCollectionImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
#include "nlohmann/json.hpp"
#endif

#include <iomanip>
#include <fmt/core.h>
#include <fmt/ranges.h>

#include <memory>
#include <mutex>
#include <ostream>
Expand Down Expand Up @@ -210,7 +212,7 @@ class LinkCollection : public podio::CollectionBase {
}

void print(std::ostream& os = std::cout, bool flush = true) const override {
os << *this;
os << fmt::format("{}", *this);
if (flush) {
os.flush();
}
Expand Down Expand Up @@ -375,24 +377,6 @@ class LinkCollection : public podio::CollectionBase {
mutable CollectionDataT m_storage{};
};

template <typename FromT, typename ToT>
std::ostream& operator<<(std::ostream& o, const LinkCollection<FromT, ToT>& v) {
const auto old_flags = o.flags();
o << " id: weight:" << '\n';
for (const auto&& el : v) {
o << std::scientific << std::showpos << std::setw(12) << el.id() << " " << std::setw(12) << " " << el.getWeight()
<< '\n';

o << " from : ";
o << el.getFrom().id() << std::endl;
o << " to : ";
o << el.getTo().id() << std::endl;
}

o.flags(old_flags);
return o;
}

namespace detail {
template <typename FromT, typename ToT>
podio::CollectionReadBuffers createLinkBuffers(bool subsetColl) {
Expand Down Expand Up @@ -460,4 +444,59 @@ void to_json(nlohmann::json& j, const podio::LinkCollection<FromT, ToT>& collect

} // namespace podio

template <typename FromT, typename ToT>
struct fmt::formatter<podio::LinkCollection<FromT, ToT>> {
char presentation = 'd'; // 'd' for default/detailed, 'b' for brief

constexpr auto parse(fmt::format_parse_context& ctx) {
auto it = ctx.begin();
auto end = ctx.end();

if (it != end && *it != '}') {
presentation = *it++;
if (presentation != 'b' && presentation != 'd' && presentation != 'u') {
fmt::throw_format_error(
"Unsupported format specifier for LinkCollection. Use 'b' for brief, 'd' for detailed, or 'u' for user-defined");
}
}

if (it != end && *it != '}') {
fmt::throw_format_error("Invalid format specifier for LinkCollection");
}

return it;
}

auto format(const podio::LinkCollection<FromT, ToT>& coll, fmt::format_context& ctx) const {
if (presentation == 'u') {
return podio::detail::dispatchCustomFormat(coll, ctx);
}
auto out = ctx.out();

if (presentation == 'b') {
return fmt::format_to(out, "{} (id: {:8x}, size: {})", coll.getTypeName(), coll.getID(), coll.size());
}

out = fmt::format_to(out, " id: weight:\n");
for (const auto&& elem : coll) {
out = fmt::format_to(out, "{} {:+12e}\n", elem.id(), elem.getWeight());
out = fmt::format_to(out, " from : {}\n to : {}\n", elem.getFrom().id(), elem.getTo().id());
}
return out;
}
};

// Disable fmt's range formatter for LinkCollection to avoid ambiguity with the
// custom formatter above
template <typename FromT, typename ToT>
struct fmt::is_range<podio::LinkCollection<FromT, ToT>, char> : std::false_type {};

namespace podio {
template <typename FromT, typename ToT>
std::ostream& operator<<(std::ostream& o, const LinkCollection<FromT, ToT>& v) {
fmt::format_to(std::ostreambuf_iterator(o), "{}", v);
return o;
}
} // namespace podio

#endif // PODIO_DETAIL_LINKCOLLECTIONIMPL_H
Loading
Loading