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

[WIP] Add a generic variant based type that can be used for interface-like functionality #215

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a947be0
Make Obj destructor default if possible
tmadlener Sep 10, 2021
2768191
Make assignment operators use copy-and-swap idiom
tmadlener Sep 10, 2021
05fc52b
Fully qualify OneToOneRelations in setReferences
tmadlener Sep 10, 2021
7fa814f
Create cmake lists file with generated files
tmadlener Oct 13, 2020
e1b1d96
Make templates and yaml file changes retrigger cmake
tmadlener Jun 18, 2021
2fe07ce
Make changes to the code generator retrigger generation
tmadlener Aug 11, 2021
639846c
Make macro abort cmake if datamodel cannot be generated
tmadlener Aug 17, 2021
ab17a8d
[wip] First version of generic wrapper
tmadlener Aug 13, 2021
201cebd
[wip] Make the validator accept interface types
tmadlener Aug 16, 2021
a580007
[wip] Make the variant accessible by inheriting classes
tmadlener Aug 17, 2021
2d17291
[wip] Add more utility functionality to wrapper
tmadlener Aug 17, 2021
8500614
[wip] Make everything compile again
tmadlener Aug 18, 2021
1663f67
[wip] make non-const wrappers have setter functions
tmadlener Aug 19, 2021
a758d1f
[wip] Make wrappers work with Const types as well
tmadlener Sep 6, 2021
765301e
[wip] Add simple tests for reading and writing
tmadlener Sep 6, 2021
9902148
[wip] Make CollectionBase return string_view value type names
tmadlener Sep 6, 2021
5a270e1
[wip] Cleanup enable_if machinery
tmadlener Sep 8, 2021
b5de4e4
[wip] Remove the possibility to use the wrapper for changing constness
tmadlener Sep 8, 2021
9eb4cff
Uncomment static_assert since it works in any case
tmadlener Sep 8, 2021
b5f9b16
Restore access rights to avoid leaking the Obj*
tmadlener Sep 10, 2021
c15f4b3
Make naming for enable_if helpes more consistent
tmadlener Sep 10, 2021
6e83818
[wip] fix templates to put Wrapped class into namespace
tmadlener Sep 10, 2021
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,6 @@ spack*
# Tooling
/.clangd/
/compile_commands.json

# Generated files
*podio_generated_files.cmake
32 changes: 21 additions & 11 deletions cmake/podioMacros.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -134,27 +134,37 @@ function(PODIO_GENERATE_DATAMODEL datamodel YAML_FILE RETURN_HEADERS RETURN_SOUR
# At least build the ROOT selection.xml by default for now
SET(ARG_IO_BACKEND_HANDLERS "ROOT")
ENDIF()

# Make sure that we re run the generation process everytime either the
# templates or the yaml file changes.
include(${podio_PYTHON_DIR}/templates/CMakeLists.txt)
set_property(
DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
${YAML_FILE}
${PODIO_TEMPLATES}
${podio_PYTHON_DIR}/podio_class_generator.py
${podio_PYTHON_DIR}/generator_utils.py
${podio_PYTHON_DIR}/podio_config_reader.py
)

message(STATUS "Creating '${datamodel}' datamodel")
# we need to boostrap the data model, so this has to be executed in the cmake run
execute_process(
COMMAND ${CMAKE_COMMAND} -E echo "Creating \"${datamodel}\" data model"
COMMAND python ${podio_PYTHON_DIR}/podio_class_generator.py ${YAML_FILE} ${ARG_OUTPUT_FOLDER} ${datamodel} ${ARG_IO_BACKEND_HANDLERS}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE podio_generate_command_retval
)

file(GLOB headers ${ARG_OUTPUT_FOLDER}/${datamodel}/*.h)
file(GLOB sources ${ARG_OUTPUT_FOLDER}/src/*.cc)
IF(NOT ${podio_generate_command_retval} EQUAL 0)
message(FATAL_ERROR "Could not generate datamodel '${datamodel}'. Check your definition in '${YAML_FILE}'")
ENDIF()

# Get the generated headers and source files
include(${ARG_OUTPUT_FOLDER}/podio_generated_files.cmake)

set (${RETURN_HEADERS} ${headers} PARENT_SCOPE)
set (${RETURN_SOURCES} ${sources} PARENT_SCOPE)

add_custom_target(create_${datamodel}
COMMENT "Re-Creating \"${datamodel}\" data model"
DEPENDS ${YAML_FILE}
BYPRODUCTS ${sources} ${headers}
COMMAND python ${podio_PYTHON_DIR}/podio_class_generator.py --quiet ${YAML_FILE} ${ARG_OUTPUT_FOLDER} ${datamodel} ${ARG_IO_BACKEND_HANDLERS}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

endfunction()


Expand Down
5 changes: 2 additions & 3 deletions include/podio/CollectionBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
#include "podio/ObjectID.h"
#include "podio/CollectionBuffers.h"

#include <string>
#include <utility>
#include <vector>

#include <string_view>


namespace podio {
Expand Down Expand Up @@ -41,7 +40,7 @@ namespace podio {
virtual size_t size() const = 0;

/// fully qualified type name of elements - with namespace
virtual std::string getValueTypeName() const = 0;
virtual std::string_view getValueTypeName() const = 0;

/// destructor
virtual ~CollectionBase() = default;
Expand Down
222 changes: 222 additions & 0 deletions include/podio/GenericWrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
#ifndef PODIO_GENERICWRAPPER_H
#define PODIO_GENERICWRAPPER_H

#include "podio/ObjectID.h"

#include <type_traits>
#include <variant>
#include <iostream>

namespace podio {
namespace detail {

#if __cplusplus > 201704L
// Only available in c++20
using std::remove_cvref_t;
#else
// Simple enough to roll our own quickly
template<class T>
struct remove_cvref {
using type = std::remove_cv_t<std::remove_reference_t<T>>;
};

template<class T>
using remove_cvref_t = typename remove_cvref<T>::type;
#endif

// Helper bool to check if the first type is any of the passed other types after
// stripping all const, volatile and references from the first type
template<typename T, typename ...Ts>
constexpr bool isAnyOf = (std::is_same_v<remove_cvref_t<T>, Ts> || ...);
// I.e. the following works
static_assert(isAnyOf<const int&, int>);

// Helper struct to select functions/overloads depending on whether the first
// type is actually one of the other passed types (after removing any const and
// volatile qualifiers). See also isAnyOf
template<typename T, typename ...Ts>
using EnableIfAnyOf = std::enable_if_t<isAnyOf<T, Ts...>>;

// Helper type to select functions/overloads depending on whether the condition
// C is treu and the type T is one of the other passed types (after removing any
// const and volatile qualifiers from T). See also isAnyOf
template <bool C, typename T, typename... Ts>
using EnableIfAnyOfAnd = std::enable_if_t<C && isAnyOf<T, Ts...>>;

// Helper struct to determine whether a type as an ObjPtrT subtype
// Mainly used for slightly nicer error messages
template<typename T, typename=std::void_t<>>
struct HasObjPtr : std::false_type {};

template<typename T>
struct HasObjPtr<T, std::void_t<typename T::ObjPtrT>> : std::true_type {};

template<typename T>
constexpr bool hasObjPtr = HasObjPtr<T>::value;

template<typename ...Ts>
constexpr bool allHaveObjPtrT = (hasObjPtr<Ts> && ...);

// Helper type for getting the type of the Obj pointer that is stored in the
// class.
// NOTE: Not SFINAE friendly by design!
template<typename T>
struct GetObjPtr {
using type = typename T::ObjPtrT;
};

template<typename T>
using GetObjPtrT = typename GetObjPtr<T>::type;

// Helper type for getting the Const type from a mutable class (which is used to
// instantiate the templates)
// NOTE: no checks here, since we assume that that is covered by the checks for
// the public ObjPtrT subtype above
template<typename T>
struct GetConstType {
using type = typename T::ConstT;
};

template<typename T>
using GetConstT = typename GetConstType<T>::type;

} // namespace detail


/**
* GenericWrapper class that can wrap any number of DataTypes as long as they
* have a public ObjPtrT typedef/subtype. The wrapper internally stores the Obj*
* of the passed value type into a std::variant of all the passed types. It also
* defines some commonly used functions that all data types provide. The
* intended use case is to inherit form this GenericWrapper and then implement
* necessary additional functionality in the inheriting class that can then be
* used just as any other podio generated class.
*
* NOTE: The first bool template parameter steers whether a call to getValue can
* return only Const values or if it can also return mutable values.
*/
template<bool Mutable, typename ...WrappedTypes>
class GenericWrapper {
static_assert(detail::allHaveObjPtrT<WrappedTypes...>, "All WrappedTypes must have a public ObjPtrT subtype");
/// Private type of the variant that is used internally
using VariantT = typename std::variant<detail::GetObjPtrT<WrappedTypes>...>;

/// Private helper type for enabling functions that should work with "Const"
/// and the default classes
template<typename T>
using EnableIfValueType = detail::EnableIfAnyOf<T,
WrappedTypes...,
detail::GetConstT<WrappedTypes>...>;

/// Private helper type for enabling functions that whould work for ObjPtrT
/// template arguments
template<typename T>
using EnableIfObjPtrType = detail::EnableIfAnyOf<T,
detail::GetObjPtrT<WrappedTypes>...>;


public:
/// Public helper type for enabling one "default" constructor in the using
/// classes that takes values or Obj pointers
template<typename T>
using EnableWrapper = detail::EnableIfAnyOf<T,
WrappedTypes...,
detail::GetConstT<WrappedTypes>...,
detail::GetObjPtrT<WrappedTypes>...
>;

template<typename T,
typename = EnableIfValueType<T>>
GenericWrapper(T value) : m_obj(value.m_obj) {
value.m_obj->acquire(); // TODO: go through std::visit as well here?
}

template<typename ObjT,
typename = EnableIfObjPtrType<ObjT>>
GenericWrapper(ObjT* obj) : m_obj(obj) {
obj->acquire(); // TODO: go through std::visit as well here?
}

~GenericWrapper() {
releaseObj();
}

// No default constructor as the "empty" wrapper is really not something that
// makes sense in this case
GenericWrapper() = delete;

GenericWrapper(GenericWrapper const& other) : m_obj(other.m_obj) {
acquireObj();
}

GenericWrapper(VariantT const& variant) : m_obj(variant) {
acquireObj();
}

GenericWrapper& operator=(GenericWrapper const& other) {
releaseObj();
m_obj = other.m_obj;
acquireObj();
return *this;
}

void unlink() {
std::visit([](auto&& obj) { obj = nullptr; }, m_obj);
}

const podio::ObjectID getObjectID() const {
return std::visit([](auto&& obj) { return obj->id; }, m_obj);
}

unsigned int id() const {
return std::visit([](auto&& obj) {
const auto objId = obj->id;
return objId.collectionID * 10000000 + objId.index;
}, m_obj);
}

template<typename U,
typename = EnableIfValueType<U>>
bool isCurrentType() const {
return std::holds_alternative<detail::GetObjPtrT<U>>(m_obj);
}

/// Get the wrapped value as user data type.
/// This overload with access to mutable types only exists for Mutable == true
/// classes
template<typename U,
typename = detail::EnableIfAnyOfAnd<Mutable,
U,
WrappedTypes...,
detail::GetConstT<WrappedTypes>...>>
U getValue() {
const auto obj = std::get<detail::GetObjPtrT<U>>(m_obj);
return U(obj);
}

/// Access to the const wrapped user data type
template<typename U,
typename = detail::EnableIfAnyOf<U, detail::GetConstT<WrappedTypes>...>>
U getValue() const {
const auto obj = std::get<detail::GetObjPtrT<U>>(m_obj);
return U(obj);
}

protected:
VariantT m_obj;

private:
void acquireObj() {
std::visit([](auto&& obj) { obj->acquire(); }, m_obj);
}
void releaseObj() {
std::visit([](auto&& obj) {
if(obj) obj->release();
}, m_obj);
}
};


}

#endif
1 change: 1 addition & 0 deletions python/generator_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ def __init__(self, name, **kwargs):
self.is_builtin = False
self.is_builtin_array = False
self.is_array = False
self.interface_types = []
# ensure that this will break somewhere if requested but not set
self.namespace, self.bare_type = None, None
self.array_namespace, self.array_bare_type = None, None
Expand Down
Loading