Skip to content

Commit

Permalink
Add templated get/set functionality for GenericParameters and depreca…
Browse files Browse the repository at this point in the history
…te non-templated versions (#262)

* Add templated access functionality to GenericParameters

Makes it easier to add new supported data types if ever necessary.
Additionally, gives this a more generic API that should make future
usage in the Frame easier.

These changes include a bunch of type helpers which now live in their
own header, which could also be useful in the future (or until we make
real use of concepts)

* Add documentation

* Move all usage of detail::GetVectorType into one place

* Mark non-templated access functionality as deprecated

* clang-tidy: Fix accidental copy in range-based for loop

* clang-tidy: Use auto to avoid duplicating typename

* Unify getValue interface / implementation and add a few tests

* clang-tidy: Silence overzealous warning in tests

* Remove no longer necessary code

* Add test cases for resetting values

* Reserve necessary capacity up front
  • Loading branch information
tmadlener authored May 23, 2022
1 parent 16ce1ee commit 469b739
Show file tree
Hide file tree
Showing 7 changed files with 403 additions and 146 deletions.
208 changes: 173 additions & 35 deletions include/podio/GenericParameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,58 @@
#ifndef PODIO_GENERICPARAMETERS_H
#define PODIO_GENERICPARAMETERS_H 1

#include "podio/utilities/TypeHelpers.h"

#include <algorithm>
#include <map>
#include <string>
#include <vector>

#define DEPRECATED_ACCESS [[deprecated("Use templated access functionality")]]

namespace podio {

/// The types which are supported in the GenericParameters
using SupportedGenericDataTypes = std::tuple<int, float, std::string>;

/// Static bool for determining if a type T is a supported GenericParamter type
template <typename T>
static constexpr bool isSupportedGenericDataType = detail::isAnyOrVectorOf<T, SupportedGenericDataTypes>;

/// Alias template to be used for enabling / disabling template overloads that
/// should only be present for actually supported data types
template <typename T>
using EnableIfValidGenericDataType = typename std::enable_if_t<isSupportedGenericDataType<T>>;

namespace detail {
/// Helper struct to determine how to return different types from the
/// GenericParamters to avoid unnecessary copies but also to prohibit carrying
/// around const references to ints or floats
template <typename T>
struct GenericDataReturnTypeHelper {
using type = T;
};

/// Specialization for std::string. Those will always be returned by const ref
template <>
struct GenericDataReturnTypeHelper<std::string> {
using type = const std::string&;
};

/// Specialization for std::vector. Those will always be returned by const ref
template <typename T>
struct GenericDataReturnTypeHelper<std::vector<T>> {
using type = const std::vector<T>&;
};
} // namespace detail

/// Alias template for determining the appropriate return type for the passed in
/// type
template <typename T>
using GenericDataReturnType = typename detail::GenericDataReturnTypeHelper<T>::type;

// These should be trivial to remove once the deprecated non-templated access
// functionality is actually removed
typedef std::vector<int> IntVec;
typedef std::vector<float> FloatVec;
typedef std::vector<std::string> StringVec;
Expand All @@ -33,80 +79,96 @@ class GenericParameters {
using StringMap = MapType<std::string>;

public:
/// Get the value that is stored under the given key, by const reference or by
/// value depending on the desired type
template <typename T, typename = EnableIfValidGenericDataType<T>>
GenericDataReturnType<T> getValue(const std::string&) const;

/// Store (a copy of) the passed value under the given key
template <typename T, typename = EnableIfValidGenericDataType<T>>
void setValue(const std::string& key, T value);

/// Overload for catching const char* setting for string values
void setValue(const std::string& key, std::string value) {
setValue<std::string>(key, std::move(value));
}

/// Overload for catching initializer list setting for vector values
template <typename T, typename = std::enable_if_t<detail::isInTuple<T, SupportedGenericDataTypes>>>
void setValue(const std::string& key, std::initializer_list<T>&& values) {
setValue<std::vector<T>>(key, std::move(values));
}

/// Get the number of elements stored under the given key for a type
template <typename T, typename = EnableIfValidGenericDataType<T>>
size_t getN(const std::string& key) const;

/// Get all available keys for a given type
template <typename T, typename = EnableIfValidGenericDataType<T>>
std::vector<std::string> getKeys() const;

/** Returns the first integer value for the given key.
*/
int getIntVal(const std::string& key) const;
DEPRECATED_ACCESS int getIntVal(const std::string& key) const;

/** Returns the first float value for the given key.
*/
float getFloatVal(const std::string& key) const;
DEPRECATED_ACCESS float getFloatVal(const std::string& key) const;

/** Returns the first string value for the given key.
*/
const std::string& getStringVal(const std::string& key) const;
DEPRECATED_ACCESS const std::string& getStringVal(const std::string& key) const;

/** Adds all integer values for the given key to values.
* Returns a reference to values for convenience.
*/
IntVec& getIntVals(const std::string& key, IntVec& values) const;
DEPRECATED_ACCESS IntVec& getIntVals(const std::string& key, IntVec& values) const;

/** Adds all float values for the given key to values.
* Returns a reference to values for convenience.
*/
FloatVec& getFloatVals(const std::string& key, FloatVec& values) const;
DEPRECATED_ACCESS FloatVec& getFloatVals(const std::string& key, FloatVec& values) const;

/** Adds all float values for the given key to values.
* Returns a reference to values for convenience.
*/
StringVec& getStringVals(const std::string& key, StringVec& values) const;
DEPRECATED_ACCESS StringVec& getStringVals(const std::string& key, StringVec& values) const;

/** Returns a list of all keys of integer parameters.
*/
const StringVec& getIntKeys(StringVec& keys) const;
DEPRECATED_ACCESS const StringVec& getIntKeys(StringVec& keys) const;

/** Returns a list of all keys of float parameters.
*/
const StringVec& getFloatKeys(StringVec& keys) const;
DEPRECATED_ACCESS const StringVec& getFloatKeys(StringVec& keys) const;

/** Returns a list of all keys of string parameters.
*/
const StringVec& getStringKeys(StringVec& keys) const;
DEPRECATED_ACCESS const StringVec& getStringKeys(StringVec& keys) const;

/** The number of integer values stored for this key.
*/
int getNInt(const std::string& key) const;
DEPRECATED_ACCESS int getNInt(const std::string& key) const;

/** The number of float values stored for this key.
*/
int getNFloat(const std::string& key) const;
DEPRECATED_ACCESS int getNFloat(const std::string& key) const;

/** The number of string values stored for this key.
*/
int getNString(const std::string& key) const;

/** Set integer value for the given key.
*/
void setValue(const std::string& key, int value);

/** Set float value for the given key.
*/
void setValue(const std::string& key, float value);

/** Set string value for the given key.
*/
void setValue(const std::string& key, const std::string& value);
DEPRECATED_ACCESS int getNString(const std::string& key) const;

/** Set integer values for the given key.
*/
void setValues(const std::string& key, const IntVec& values);
DEPRECATED_ACCESS void setValues(const std::string& key, const IntVec& values);

/** Set float values for the given key.
*/
void setValues(const std::string& key, const FloatVec& values);
DEPRECATED_ACCESS void setValues(const std::string& key, const FloatVec& values);

/** Set string values for the given key.
*/
void setValues(const std::string& key, const StringVec& values);
DEPRECATED_ACCESS void setValues(const std::string& key, const StringVec& values);

/// erase all elements
void clear() {
Expand All @@ -124,37 +186,113 @@ class GenericParameters {
* Get the internal int map (necessary for serialization with SIO)
*/
const IntMap& getIntMap() const {
return _intMap;
return getMap<int>();
}
IntMap& getIntMap() {
return _intMap;
return getMap<int>();
}

/**
* Get the internal float map (necessary for serialization with SIO)
*/
const FloatMap& getFloatMap() const {
return _floatMap;
return getMap<float>();
}
FloatMap& getFloatMap() {
return _floatMap;
return getMap<float>();
}

/**
* Get the internal string map (necessary for serialization with SIO)
*/
const StringMap& getStringMap() const {
return _stringMap;
return getMap<std::string>();
}
StringMap& getStringMap() {
return _stringMap;
return getMap<std::string>();
}

protected:
private:
/// Get a reference to the internal map for a given type (necessary for SIO)
template <typename T>
const MapType<detail::GetVectorType<T>>& getMap() const {
if constexpr (std::is_same_v<detail::GetVectorType<T>, int>) {
return _intMap;
} else if constexpr (std::is_same_v<detail::GetVectorType<T>, float>) {
return _floatMap;
} else {
return _stringMap;
}
}

/// Get a reference to the internal map for a given type (necessary for SIO)
template <typename T>
MapType<detail::GetVectorType<T>>& getMap() {
if constexpr (std::is_same_v<detail::GetVectorType<T>, int>) {
return _intMap;
} else if constexpr (std::is_same_v<detail::GetVectorType<T>, float>) {
return _floatMap;
} else {
return _stringMap;
}
}

private:
IntMap _intMap{};
FloatMap _floatMap{};
StringMap _stringMap{};
};

template <typename T, typename>
GenericDataReturnType<T> GenericParameters::getValue(const std::string& key) const {
const auto& map = getMap<T>();
const auto it = map.find(key);
// If there is no entry to the key, we just return an empty default
// TODO: make this case detectable from the outside
if (it == map.end()) {
static const auto empty = T{};
return empty;
}

// We have to check whether the return type is a vector or a single value
if constexpr (detail::isVector<T>) {
return it->second;
} else {
const auto& iv = it->second;
return iv[0];
}
}

template <typename T, typename>
void GenericParameters::setValue(const std::string& key, T value) {
auto& map = getMap<T>();
if constexpr (detail::isVector<T>) {
map.insert_or_assign(key, std::move(value));
} else {
// Wrap the value into a vector with exactly one entry and store that
std::vector<T> v = {value};
map.insert_or_assign(key, std::move(v));
}
}

template <typename T, typename>
size_t GenericParameters::getN(const std::string& key) const {
const auto& map = getMap<T>();
if (const auto it = map.find(key); it != map.end()) {
return it->second.size();
}
return 0;
}

template <typename T, typename>
std::vector<std::string> GenericParameters::getKeys() const {
std::vector<std::string> keys;
const auto& map = getMap<T>();
keys.reserve(map.size());
std::transform(map.begin(), map.end(), std::back_inserter(keys), [](const auto& pair) { return pair.first; });

return keys;
}

}; // class
} // namespace podio
#endif
34 changes: 6 additions & 28 deletions include/podio/UserDataCollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define PODIO_USERDATACOLLECTION_H

#include "podio/CollectionBase.h"
#include "podio/utilities/TypeHelpers.h"

#include <map>
#include <string>
Expand All @@ -17,40 +18,17 @@

namespace podio {

namespace detail {

// some templates to ensure valid and supported user types
// as suggested by T.Madlener, DESY

/** tuple of basic types supported in user vector
*/
using SupportedUserDataTypes =
std::tuple<float, double, int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t>;

/**
* Helper function to check whether a type T is in an std::tuple<Ts...>
*/
template <typename T, typename... Ts>
constexpr bool inTuple(std::tuple<Ts...>) {
return (std::is_same_v<T, Ts> || ...);
}

/**
* Compile time helper function to check whether the given type is in the list
* of supported types
*/
template <typename T>
constexpr bool isSupported() {
return inTuple<T>(SupportedUserDataTypes{});
}
} // namespace detail
/** tuple of basic types supported in user vector
*/
using SupportedUserDataTypes =
std::tuple<float, double, int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t>;

/**
* Alias template to be used to enable template specializations only for the types listed in the
* SupportedUserDataTypes list
*/
template <typename T>
using EnableIfSupportedUserType = std::enable_if_t<detail::isSupported<T>()>;
using EnableIfSupportedUserType = std::enable_if_t<detail::isInTuple<T, SupportedUserDataTypes>>;

/** helper template to provide readable type names for basic types with macro PODIO_ADD_USER_TYPE(type)
*/
Expand Down
Loading

0 comments on commit 469b739

Please sign in to comment.