Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it possible to share maps between both directions #21

Merged
merged 20 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
493540c
Remove const from lcio key types
tmadlener Jun 30, 2023
c2dc53d
Move mapLookup to utils header with some additional functionality
tmadlener Jun 30, 2023
71e56d2
Make EDM4hep -> LCIO conversion use mapLookupFrom
tmadlener Jun 30, 2023
fcff727
Introduce type-alises for the two used map types
tmadlener Jun 30, 2023
a6e108b
Switch LCIO to EDM4hep conversion to generic functionality
tmadlener Jun 30, 2023
ba56986
Actually make getKey and getMapped usable with maps and vectors
tmadlener Jul 6, 2023
51ef89f
Add references and remove hand-rolled find
tmadlener Sep 13, 2023
f39108f
Switch EDM4hep to LCIO conversion to generic functionality
tmadlener Jul 5, 2023
f3c85ea
Rename TypeMapT to ObjectMapT for less confusion
tmadlener Sep 12, 2023
1fa9044
Make EDM4hep to LCIO conversion a template library
tmadlener Sep 13, 2023
f2bc3b1
Make LCIO to EDM4hep conversoin a template library
tmadlener Sep 13, 2023
47786b8
Restore original behavior to not break marlinwrapper
tmadlener Sep 13, 2023
d2f2664
Fix docstring
tmadlener Sep 13, 2023
2f70c7d
Make object map names consistent in both directions
tmadlener Sep 14, 2023
70de768
Make enable_if conditions less restrictive
tmadlener Sep 14, 2023
09568d5
Make it possible to use a global map for relation resolving
tmadlener Sep 14, 2023
12f72b3
Remove specifying unnecessary default types for template parameters
tmadlener Sep 27, 2023
809cef9
Improve log output for subset collection conversion
tmadlener Sep 27, 2023
547fa39
Make the association coll creation work with all map types
tmadlener Sep 27, 2023
ec2f31f
Pass separate lookup maps for resolving MC and reco particle
tmadlener Sep 27, 2023
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
10 changes: 9 additions & 1 deletion k4EDM4hep2LcioConv/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,17 @@ target_link_libraries(k4EDM4hep2LcioConv PUBLIC
${LCIO_LIBRARIES}
EDM4HEP::edm4hep)

set(public_headers
include/${PROJECT_NAME}/k4EDM4hep2LcioConv.h
include/${PROJECT_NAME}/k4EDM4hep2LcioConv.ipp
include/${PROJECT_NAME}/k4Lcio2EDM4hepConv.h
include/${PROJECT_NAME}/k4Lcio2EDM4hepConv.ipp
include/${PROJECT_NAME}/MappingUtils.h
)

set_target_properties(${PROJECT_NAME}
PROPERTIES
PUBLIC_HEADER "include/${PROJECT_NAME}/k4EDM4hep2LcioConv.h;include/${PROJECT_NAME}/k4Lcio2EDM4hepConv.h"
PUBLIC_HEADER "${public_headers}"
)

install(TARGETS k4EDM4hep2LcioConv
Expand Down
255 changes: 255 additions & 0 deletions k4EDM4hep2LcioConv/include/k4EDM4hep2LcioConv/MappingUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
#ifndef K4EDM4HEP2LCIOCONV_MAPPINGUTILS_H
#define K4EDM4HEP2LCIOCONV_MAPPINGUTILS_H

#include <optional>
#include <algorithm>
#include <vector>
#include <tuple>
#include <unordered_map>
#include <type_traits>

#if __has_include("experimental/type_traits.h")
tmadlener marked this conversation as resolved.
Show resolved Hide resolved
#include <experimental/type_traits>
namespace det {
using namespace std::experimental;
}
#else
// Implement the minimal feature set we need
namespace det {
namespace detail {
template<typename DefT, typename AlwaysVoidT, template<typename...> typename Op, typename... Args>
struct detector {
using value_t = std::false_type;
using type = DefT;
};

template<typename DefT, template<typename...> typename Op, typename... Args>
struct detector<DefT, std::void_t<Op<Args...>>, Op, Args...> {
using value_t = std::true_type;
using type = Op<Args...>;
};
} // namespace detail

struct nonesuch {
~nonesuch() = delete;
nonesuch(const nonesuch&) = delete;
void operator=(const nonesuch&) = delete;
};

template<template<typename...> typename Op, typename... Args>
using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;

template<template<typename...> typename Op, typename... Args>
static constexpr bool is_detected_v = is_detected<Op, Args...>::value;
} // namespace det
#endif

namespace k4EDM4hep2LcioConv {

namespace detail {
/// Very minimal detector for whether T is a map or not. We simply assume that
/// if it has a key_type we can also call find on T.
template<typename T>
using has_key_t = typename T::key_type;

template<typename T>
constexpr static bool is_map_v = det::is_detected_v<has_key_t, T>;

/// Helper struct to determine the key and mapped types for map-like types or
/// maps
template<typename T, typename IsMap = std::bool_constant<is_map_v<T>>>
struct map_t_helper {
};

template<typename T>
struct map_t_helper<T, std::bool_constant<true>> {
using key_type = typename T::key_type;
using mapped_type = typename T::mapped_type;
};

template<typename T>
struct map_t_helper<T, std::bool_constant<false>> {
using key_type = typename std::tuple_element<0, typename T::value_type>::type;
using mapped_type = typename std::tuple_element<1, typename T::value_type>::type;
};

template<typename T>
using key_t = typename map_t_helper<T>::key_type;

template<typename T>
using mapped_t = typename map_t_helper<T>::mapped_type;

template<typename T>
using has_object_type = typename T::object_type;

/// Detector for whether a type T is a Mutable user facing type.
template<typename T>
constexpr static bool is_mutable_v = det::is_detected_v<has_object_type, T>;

/// Helper struct to determine the Mutable type for a user facing type
/// NOTE: Not SFINAE safe for anything that is not a podio generated class
template<typename T, typename IsMutable = std::bool_constant<is_mutable_v<T>>>
struct mutable_t_helper {
};

template<typename T>
struct mutable_t_helper<T, std::bool_constant<true>> {
using type = T;
};

template<typename T>
struct mutable_t_helper<T, std::bool_constant<false>> {
using type = typename T::mutable_type;
};

template<typename T>
using mutable_t = typename mutable_t_helper<T>::type;

/// bool constant to determine whether type T is a valid type to be used as
/// a key in the generic mapping functionality defined below. In this case
/// it checks for type equality or makes sure that KeyT is a base of T or
/// vice versa. This is designed specifically for the uses cases here, where
/// the LCIO types (pointers) are used as key types.
template<typename T, typename KeyT>
constexpr static bool is_valid_key_type_v =
std::is_same_v<T, KeyT> || std::is_base_of_v<std::remove_pointer_t<T>, std::remove_pointer_t<KeyT>> ||
std::is_base_of_v<std::remove_pointer_t<KeyT>, std::remove_pointer_t<T>>;

/// Detector and corresponding bool constant to detect whether two types are
/// equality comparable. c++20 would have a concept for this.
template<typename T, typename U>
using has_operator_eq = decltype(std::declval<T>() == std::declval<U>());

template<typename T, typename U>
constexpr static bool is_eq_comparable = det::is_detected_v<has_operator_eq, T, U>;

/// bool constant to determine whether a type T is a valid type to be used
/// as a mapped type in the generic mapping functionality defined below. In
/// this case this it checks T is equality copmarable with MappedT
template<typename T, typename MappedT>
constexpr static bool is_valid_mapped_type_v = is_eq_comparable<T, MappedT>;

/**
* Find the mapped-to object in a map provided a key object
*
* NOTE: This will use a potentially more efficient lookup for actual map
* types (i.e. MapT::find). In that case it will have the time complexity of
* that. In case of a "map-like" (e.g. vector<tuple<K, V>>) it will be O(N).
*/
template<typename FromT, typename MapT, typename = std::enable_if_t<is_valid_key_type_v<FromT, key_t<MapT>>>>
auto mapLookupTo(FromT keyObj, const MapT& map) -> std::optional<mapped_t<MapT>>
{
if constexpr (is_map_v<MapT>) {
if (const auto& it = map.find(keyObj); it != map.end()) {
return it->second;
}
}
else {
if (const auto& it = std::find_if(
map.begin(), map.end(), [&keyObj](const auto& mapElem) { return std::get<0>(mapElem) == keyObj; });
it != map.end()) {
return std::get<1>(*it);
}
}

return std::nullopt;
}

/**
* Find the mapped-from (or key object) in a "map" provided a mapped-to object
*
* NOTE: This will always loop over potentially all elements in the provided
* map, so it is definitely O(N) regardless of the provided map type
*/
template<typename ToT, typename MapT, typename = std::enable_if_t<is_valid_mapped_type_v<ToT, mapped_t<MapT>>>>
auto mapLookupFrom(ToT mappedObj, const MapT& map) -> std::optional<key_t<MapT>>
{
// In this case we cannot use a potential find method for an actual map, but
// looping over the map and doing the actual comparison will work
if (const auto& it = std::find_if(
map.begin(), map.end(), [&mappedObj](const auto& mapElem) { return std::get<1>(mapElem) == mappedObj; });
it != map.end()) {
return std::get<0>(*it);
}

return std::nullopt;
}

enum class InsertMode { Unchecked, Checked };

/**
* Insert a key-value pair into a "map"
*
* The InsertMode argument can be use to check whether the Key already
* exists in the map before inserting. This is only useful for maps using a
* vector as backing, since the usual emplace already does this check and
* does not insert if a key already exists
*/
template<typename MapT, typename KeyT = key_t<MapT>, typename MappedT = mapped_t<MapT>>
auto mapInsert(KeyT&& key, MappedT&& mapped, MapT& map, InsertMode insertMode = InsertMode::Unchecked)
{
if constexpr (is_map_v<MapT>) {
return map.emplace(std::forward<KeyT>(key), std::forward<MappedT>(mapped));
}
else {
if (insertMode == InsertMode::Checked) {
if (auto existing = mapLookupTo(key, map)) {
// Explicitly casting to the actual key type here to make return
// type deductoin work even in cases where we have a Map<Base*, V>
// but the KeyT has been deduced as Derived*
return std::make_pair(std::make_tuple(key_t<MapT>(key), existing.value()), false);
}
}
return std::make_pair(map.emplace_back(std::forward<KeyT>(key), std::forward<MappedT>(mapped)), true);
}
}

/// Helper type alias that can be used to detect whether a T can be used
/// with std::get directly or whether it has to be dereferenced first
template<typename T>
using std_get_usable = decltype(std::get<0>(std::declval<T>()));

/**
* Helper function to get the Key from an Iterator (e.g. returned by
* mapInsert). This is necessary because map::emplace returns an iterator,
* while vector::emplace_back returns a reference to the inserted element.
* Simply providing two overloads here does the trick.
*/
template<typename It>
auto getKey(const It& it)
{
if constexpr (det::is_detected_v<std_get_usable, It>) {
return std::get<0>(it);
}
else {
return std::get<0>(*it);
}
}

/**
* Helper function to get the Value from an Iterator (e.g. returned by
* mapInsert). This is necessary because map::emplace returns an iterator,
* while vector::emplace_back returns a reference to the inserted element.
* Simply providing two overloads here does the trick.
*/
template<typename It>
auto getMapped(const It& it)
{
if constexpr (det::is_detected_v<std_get_usable, It>) {
return std::get<1>(it);
}
else {
return std::get<1>(*it);
}
}
} // namespace detail

template<typename K, typename V>
using MapT = std::unordered_map<K, V>;

template<typename K, typename V>
using VecMapT = std::vector<std::tuple<K, V>>;

} // namespace k4EDM4hep2LcioConv

#endif // K4EDM4HEP2LCIOCONV_MAPPINGUTILS_H
Loading
Loading