Skip to content

Commit aeb8148

Browse files
tmadlenerjmcarcell
andauthored
Add a c++ implementation for podio-dump (#620)
* Move sortAlphabetically to public header * Implement podio-dump-tool in c++ and wrap it in thin script Drop-in replacement of podio-dump python implementation which gets moved to podio-dump.py because it supports dumping the pre-release legacy files * Fix test env and paths for roundtrip tests * Keep podio-dump.py in set of installed programs for now * Fix minor differences in output * Use ranges where possible * Remove unnecessary flushing of stdout * Switch to println where possible * Rename legacy tool to podio-dump-legacy * Make more things done at compile time * Enable more tests for sanitizers * Fix pre-commit issues * Simplify parse function Co-authored-by: Juan Miguel Carceller <22276694+jmcarcell@users.noreply.github.com> * Use ranges and views in some places * Disable some tests still for TSan and UBSan * Range-ify more of the implementation Co-authored-by: Juan Miguel Carceller <22276694+jmcarcell@users.noreply.github.com> * Introduce alias namespace for better readability * Improve error message --------- Co-authored-by: Juan Miguel Carceller <22276694+jmcarcell@users.noreply.github.com>
1 parent d50647f commit aeb8148

15 files changed

Lines changed: 694 additions & 238 deletions

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@ if(BUILD_TESTING)
190190
include(cmake/podioTest.cmake)
191191
add_subdirectory(tests)
192192
endif()
193+
194+
find_package(fmt 9 REQUIRED)
195+
193196
add_subdirectory(tools)
194197
add_subdirectory(python)
195198

cmake/podioTest.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ function(PODIO_SET_TEST_ENV test)
1212
IO_HANDLERS=${IO_HANDLERS}
1313
PODIO_USE_CLANG_FORMAT=${PODIO_USE_CLANG_FORMAT}
1414
PODIO_BASE=${PROJECT_SOURCE_DIR}
15+
PODIO_BUILD_BASE=${PROJECT_BINARY_DIR}
1516
ENABLE_SIO=${ENABLE_SIO}
1617
PODIO_BUILD_BASE=${PROJECT_BINARY_DIR}
1718
LSAN_OPTIONS=suppressions=${PROJECT_SOURCE_DIR}/tests/root_io/leak_sanitizer_suppressions.txt
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#ifndef PODIO_UTILITIES_MISCHELPERS_H
2+
#define PODIO_UTILITIES_MISCHELPERS_H
3+
4+
#include <algorithm>
5+
#include <string>
6+
#include <vector>
7+
8+
namespace podio::utils {
9+
10+
/// Sort the input vector of strings alphabetically, case insensitive.
11+
///
12+
/// @param strings The strings that should be sorted alphabetically
13+
///
14+
/// @returns A vector of strings sorted alphabetically, case insensitive
15+
inline std::vector<std::string> sortAlphabeticaly(std::vector<std::string> strings) {
16+
// Obviously there is no tolower(std::string) in c++, so this is slightly more
17+
// involved and we make use of the fact that lexicographical_compare works on
18+
// ranges and the fact that we can feed it a dedicated comparison function,
19+
// where we convert the strings to lower case char-by-char. The alternative is
20+
// to make string copies inside the first lambda, transform them to lowercase
21+
// and then use operator< of std::string, which would be effectively
22+
// hand-writing what is happening below.
23+
std::ranges::sort(strings, [](const auto& lhs, const auto& rhs) {
24+
return std::lexicographical_compare(
25+
lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),
26+
[](const auto& cl, const auto& cr) { return std::tolower(cl) < std::tolower(cr); });
27+
});
28+
return strings;
29+
}
30+
} // namespace podio::utils
31+
32+
#endif // PODIO_UTILITIES_MISCHELPERS_H

src/RNTupleWriter.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ void RNTupleWriter::writeFrame(const podio::Frame& frame, const std::string& cat
5858
const bool new_category = (catInfo.writer == nullptr);
5959
if (new_category) {
6060
// This is the minimal information that we need for now
61-
catInfo.names = root_utils::sortAlphabeticaly(collsToWrite);
61+
catInfo.names = podio::utils::sortAlphabeticaly(collsToWrite);
6262
}
6363

6464
std::vector<root_utils::StoreCollection> collections;

src/ROOTWriter.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ void ROOTWriter::writeFrame(const podio::Frame& frame, const std::string& catego
3333
// been initialized
3434
if (catInfo.tree == nullptr) {
3535
catInfo.idTable = frame.getCollectionIDTableForWrite();
36-
catInfo.collsToWrite = root_utils::sortAlphabeticaly(collsToWrite);
36+
catInfo.collsToWrite = podio::utils::sortAlphabeticaly(collsToWrite);
3737
catInfo.tree = new TTree(category.c_str(), (category + " data tree").c_str());
3838
catInfo.tree->SetDirectory(m_file.get());
3939
}

src/rootUtils.h

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define PODIO_ROOT_UTILS_H // NOLINT(llvm-header-guard): internal headers confuse clang-tidy
33

44
#include "podio/CollectionIDTable.h"
5+
#include "podio/utilities/MiscHelpers.h"
56
#include "podio/utilities/RootHelpers.h"
67
#include "podio/utilities/TypeHelpers.h"
78

@@ -280,25 +281,6 @@ inline auto reconstructCollectionInfo(TTree* eventTree, podio::CollectionIDTable
280281
return collInfo;
281282
}
282283

283-
/**
284-
* Sort the input vector of strings alphabetically, case insensitive.
285-
*/
286-
inline std::vector<std::string> sortAlphabeticaly(std::vector<std::string> strings) {
287-
// Obviously there is no tolower(std::string) in c++, so this is slightly more
288-
// involved and we make use of the fact that lexicographical_compare works on
289-
// ranges and the fact that we can feed it a dedicated comparison function,
290-
// where we convert the strings to lower case char-by-char. The alternative is
291-
// to make string copies inside the first lambda, transform them to lowercase
292-
// and then use operator< of std::string, which would be effectively
293-
// hand-writing what is happening below.
294-
std::ranges::sort(strings, [](const auto& lhs, const auto& rhs) {
295-
return std::lexicographical_compare(
296-
lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),
297-
[](const auto& cl, const auto& cr) { return std::tolower(cl) < std::tolower(cr); });
298-
});
299-
return strings;
300-
}
301-
302284
/**
303285
* Check whether existingColls and candidateColls both contain the same
304286
* collection names. Returns false if the two vectors differ in content. Inputs

tests/CTestCustom.cmake

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,16 @@ if ((NOT "@FORCE_RUN_ALL_TESTS@" STREQUAL "ON") AND (NOT "@USE_SANITIZER@" STREQ
4040

4141
pyunittest
4242

43-
podio-dump-help
4443
podio-dump-root
4544
podio-dump-detailed-root
4645
podio-dump-legacy_root_v00-16-06
4746
podio-dump-legacy_root-detailed_v00-16-06
4847

49-
podio-dump-sio
50-
podio-dump-detailed-sio
5148
podio-dump-legacy_sio_v00-16-06
5249
podio-dump-legacy_sio-detailed_v00-16-06
5350

54-
podio-dump-rntuple
55-
podio-dump-detailed-rntuple
56-
5751
datamodel_def_store_roundtrip_root
5852
datamodel_def_store_roundtrip_root_extension
59-
datamodel_def_store_roundtrip_sio
60-
datamodel_def_store_roundtrip_sio_extension
61-
datamodel_def_store_roundtrip_rntuple
62-
datamodel_def_store_roundtrip_rntuple_extension
63-
6453

6554
write_old_data_root
6655
read_new_data_root
@@ -100,6 +89,12 @@ if ((NOT "@FORCE_RUN_ALL_TESTS@" STREQUAL "ON") AND (NOT "@USE_SANITIZER@" STREQ
10089
read_rntuple
10190
read_interface_rntuple
10291
selected_colls_roundtrip_rntuple
92+
93+
podio-dump-rntuple
94+
podio-dump-detailed-rntuple
95+
96+
datamodel_def_store_roundtrip_rntuple
97+
datamodel_def_store_roundtrip_rntuple_extension
10398
)
10499
endif()
105100

@@ -112,6 +107,12 @@ if ((NOT "@FORCE_RUN_ALL_TESTS@" STREQUAL "ON") AND (NOT "@USE_SANITIZER@" STREQ
112107
write_interface_rntuple
113108
read_interface_rntuple
114109
selected_colls_roundtrip_rntuple
110+
111+
podio-dump-rntuple
112+
podio-dump-detailed-rntuple
113+
114+
datamodel_def_store_roundtrip_rntuple
115+
datamodel_def_store_roundtrip_rntuple_extension
115116
)
116117

117118
endif()

tests/scripts/dumpModelRoundTrip.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ OUTPUT_FOLDER=${INPUT_FILE}.dumped_${EDM_NAME}
2121
mkdir -p ${OUTPUT_FOLDER}
2222

2323
# Dump the model to a yaml file
24-
${PODIO_BASE}/tools/podio-dump --dump-edm ${EDM_NAME} ${INPUT_FILE} > ${DUMPED_MODEL}
24+
${PODIO_BUILD_BASE}/tools/podio-dump --dump-edm ${EDM_NAME} ${INPUT_FILE} > ${DUMPED_MODEL}
2525

2626
# Regenerate the code via the class generator and the freshly dumped model
2727
${PODIO_BASE}/python/podio_class_generator.py \

tools/CMakeLists.txt

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1+
add_executable(podio-dump-tool src/podio-dump-tool.cpp)
2+
target_link_libraries(podio-dump-tool PRIVATE podio::podio podio::podioIO fmt::fmt)
3+
4+
install(TARGETS podio-dump-tool EXPORT podioTargets DESTINATION ${CMAKE_INSTALL_BINDIR})
5+
16
install(PROGRAMS ${CMAKE_CURRENT_LIST_DIR}/podio-dump DESTINATION ${CMAKE_INSTALL_BINDIR})
7+
install(PROGRAMS ${CMAKE_CURRENT_LIST_DIR}/podio-dump-legacy DESTINATION ${CMAKE_INSTALL_BINDIR})
8+
install(PROGRAMS ${CMAKE_CURRENT_LIST_DIR}/json-to-yaml DESTINATION ${CMAKE_INSTALL_BINDIR})
29
install(PROGRAMS ${CMAKE_CURRENT_LIST_DIR}/podio-vis DESTINATION ${CMAKE_INSTALL_BINDIR})
310
if(ENABLE_RNTUPLE)
411
install(PROGRAMS ${CMAKE_CURRENT_LIST_DIR}/podio-ttree-to-rntuple DESTINATION ${CMAKE_INSTALL_BINDIR})
@@ -37,18 +44,22 @@ endif()
3744

3845
# Add a very basic tests here to make sure that podio-dump at least runs
3946
if(BUILD_TESTING)
47+
# Copy these two files into the build tree to be able to test things
48+
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/json-to-yaml ${CMAKE_CURRENT_BINARY_DIR}/json-to-yaml COPYONLY)
49+
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/podio-dump ${CMAKE_CURRENT_BINARY_DIR}/podio-dump COPYONLY)
50+
4051
# Helper function for easily creating "tests" that simply execute podio-dump
4152
# with different arguments. Not crashing is considered success.
4253
#
4354
# Args:
4455
# name the name of the test
4556
# depends_on the target name of the test that produces the required input file
4657
function(CREATE_DUMP_TEST name depends_on)
47-
add_test(NAME ${name} COMMAND ./podio-dump ${ARGN})
58+
add_test(NAME ${name} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/podio-dump ${ARGN})
4859
PODIO_SET_TEST_ENV(${name})
4960

5061
set_tests_properties(${name} PROPERTIES
51-
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
62+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
5263
)
5364
if (depends_on)
5465
set_tests_properties(${name} PROPERTIES
@@ -69,7 +80,7 @@ if(BUILD_TESTING)
6980
set(_name podio-dump-legacy_${name}_${version})
7081
ExternalData_Add_Test(legacy_test_cases
7182
NAME ${_name}
72-
COMMAND ./podio-dump ${ARGN} DATA{${PROJECT_SOURCE_DIR}/tests/input_files/${input_file}}
83+
COMMAND ./podio-dump-legacy ${ARGN} DATA{${PROJECT_SOURCE_DIR}/tests/input_files/${input_file}}
7384
)
7485
PODIO_SET_TEST_ENV(${_name})
7586

tools/json-to-yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env python3
2+
"""Tiny script to ingest a json string and dump it as a yaml string"""
3+
4+
import sys
5+
import json
6+
import yaml
7+
8+
9+
def main():
10+
"""Main, read json from stdin and dump yaml to stdout"""
11+
input_data = sys.stdin.read()
12+
model_def = json.loads(input_data)
13+
print(yaml.dump(model_def, sort_keys=False, default_flow_style=False))
14+
15+
16+
if __name__ == "__main__":
17+
main()

0 commit comments

Comments
 (0)