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

Use C++ concepts to deduce data product attributes #45756

Merged
merged 1 commit into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 23 additions & 6 deletions DataFormats/Common/interface/Wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,38 +108,55 @@ namespace edm {

template <typename T>
inline bool Wrapper<T>::isMergeable_() const {
return detail::getHasMergeFunction<T>()();
if constexpr (requires(T& a, T const& b) { a.mergeProduct(b); }) {
return true;
}
return false;
}

template <typename T>
inline bool Wrapper<T>::mergeProduct_(WrapperBase const* newProduct) {
Wrapper<T> const* wrappedNewProduct = dynamic_cast<Wrapper<T> const*>(newProduct);
assert(wrappedNewProduct != nullptr);
return detail::doMergeProduct<T>()(obj, wrappedNewProduct->obj);
if constexpr (requires(T& a, T const& b) { a.mergeProduct(b); }) {
return obj.mergeProduct(wrappedNewProduct->obj);
}
return true;
}

template <typename T>
inline bool Wrapper<T>::hasIsProductEqual_() const {
return detail::getHasIsProductEqual<T>()();
if constexpr (requires(T& a, T const& b) { a.isProductEqual(b); }) {
return true;
}
return false;
}

template <typename T>
inline bool Wrapper<T>::isProductEqual_(WrapperBase const* newProduct) const {
Wrapper<T> const* wrappedNewProduct = dynamic_cast<Wrapper<T> const*>(newProduct);
assert(wrappedNewProduct != nullptr);
return detail::doIsProductEqual<T>()(obj, wrappedNewProduct->obj);
if constexpr (requires(T& a, T const& b) { a.isProductEqual(b); }) {
return obj.isProductEqual(wrappedNewProduct->obj);
}
return true;
}

template <typename T>
inline bool Wrapper<T>::hasSwap_() const {
return detail::getHasSwapFunction<T>()();
if constexpr (requires(T& a, T& b) { a.swap(b); }) {
return true;
}
return false;
}

template <typename T>
inline void Wrapper<T>::swapProduct_(WrapperBase* newProduct) {
Wrapper<T>* wrappedNewProduct = dynamic_cast<Wrapper<T>*>(newProduct);
assert(wrappedNewProduct != nullptr);
detail::doSwapProduct<T>()(obj, wrappedNewProduct->obj);
if constexpr (requires(T& a, T& b) { a.swap(b); }) {
obj.swap(wrappedNewProduct->obj);
}
}

namespace soa {
Expand Down
113 changes: 0 additions & 113 deletions DataFormats/Common/interface/WrapperDetail.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,119 +86,6 @@ namespace edm {
std::type_info const& operator()() { return typeid(T); }
};

// bool isMergeable_() will return true if T::mergeProduct(T const&) is declared and false otherwise
// bool mergeProduct_(WrapperBase const*) will merge products if T::mergeProduct(T const&) is defined
// Definitions for the following struct and function templates are not needed; we only require the declarations.
template <typename T, bool (T::*)(T const&)>
struct mergeProduct_function;
template <typename T>
static yes_tag has_mergeProduct(mergeProduct_function<T, &T::mergeProduct>* dummy);
template <typename T>
static no_tag has_mergeProduct(...);

template <typename T>
struct has_mergeProduct_function {
static constexpr bool value = std::is_same<decltype(has_mergeProduct<T>(nullptr)), yes_tag>::value;
};

template <typename T, bool = has_mergeProduct_function<T>::value>
struct getHasMergeFunction;
template <typename T>
struct getHasMergeFunction<T, true> {
bool operator()() { return true; }
};
template <typename T>
struct getHasMergeFunction<T, false> {
bool operator()() { return false; }
};
template <typename T, bool = has_mergeProduct_function<T>::value>
struct doMergeProduct;
template <typename T>
struct doMergeProduct<T, true> {
bool operator()(T& thisProduct, T const& newProduct) { return thisProduct.mergeProduct(newProduct); }
};
template <typename T>
struct doMergeProduct<T, false> {
bool operator()(T& thisProduct, T const& newProduct) {
return true; // Should never be called
}
};

// bool hasIsProductEqual_() will return true if T::isProductEqual(T const&) const is declared and false otherwise
// bool isProductEqual _(WrapperBase const*) will call T::isProductEqual(T const&) if it is defined
// Definitions for the following struct and function templates are not needed; we only require the declarations.
template <typename T, bool (T::*)(T const&) const>
struct isProductEqual_function;
template <typename T>
static yes_tag has_isProductEqual(isProductEqual_function<T, &T::isProductEqual>* dummy);
template <typename T>
static no_tag has_isProductEqual(...);

template <typename T>
struct has_isProductEqual_function {
static constexpr bool value = std::is_same<decltype(has_isProductEqual<T>(nullptr)), yes_tag>::value;
};

template <typename T, bool = has_isProductEqual_function<T>::value>
struct getHasIsProductEqual;
template <typename T>
struct getHasIsProductEqual<T, true> {
bool operator()() { return true; }
};
template <typename T>
struct getHasIsProductEqual<T, false> {
bool operator()() { return false; }
};
template <typename T, bool = has_isProductEqual_function<T>::value>
struct doIsProductEqual;
template <typename T>
struct doIsProductEqual<T, true> {
bool operator()(T const& thisProduct, T const& newProduct) { return thisProduct.isProductEqual(newProduct); }
};
template <typename T>
struct doIsProductEqual<T, false> {
bool operator()(T const& thisProduct, T const& newProduct) {
return true; // Should never be called
}
};

// bool hasSwap_() will return true if T::swap(T&) is declared and false otherwise
// void swapProduct_() will call T::swap(T&) if it is defined otherwise it does nothing
// Definitions for the following struct and function templates are not needed; we only require the declarations.
template <typename T, void (T::*)(T&)>
struct swap_function;
template <typename T>
static yes_tag has_swap(swap_function<T, &T::swap>* dummy);
template <typename T>
static no_tag has_swap(...);

template <typename T>
struct has_swap_function {
static constexpr bool value = std::is_same<decltype(has_swap<T>(nullptr)), yes_tag>::value;
};

template <typename T, bool = has_swap_function<T>::value>
struct getHasSwapFunction;
template <typename T>
struct getHasSwapFunction<T, true> {
bool operator()() { return true; }
};
template <typename T>
struct getHasSwapFunction<T, false> {
bool operator()() { return false; }
};
template <typename T, bool = has_swap_function<T>::value>
struct doSwapProduct;
template <typename T>
struct doSwapProduct<T, true> {
void operator()(T& thisProduct, T& newProduct) { thisProduct.swap(newProduct); }
};
template <typename T>
struct doSwapProduct<T, false> {
void operator()(T&, T&) {
return; // Should never be called
}
};
} // namespace detail
} // namespace edm
#endif
6 changes: 2 additions & 4 deletions FWCore/Framework/interface/Event.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,8 +374,7 @@ namespace edm {
OrphanHandle<PROD> Event::putImpl(EDPutToken::value_type index, std::unique_ptr<PROD> product) {
// The following will call post_insert if T has such a function,
// and do nothing if T has no such function.
std::conditional_t<detail::has_postinsert<PROD>::value, DoPostInsert<PROD>, DoNotPostInsert<PROD>> maybe_inserter;
maybe_inserter(product.get());
detail::do_post_insert_if_available(*product.get());

assert(index < putProducts().size());

Expand Down Expand Up @@ -456,8 +455,7 @@ namespace edm {

// The following will call post_insert if T has such a function,
// and do nothing if T has no such function.
std::conditional_t<detail::has_postinsert<PROD>::value, DoPostInsert<PROD>, DoNotPostInsert<PROD>> maybe_inserter;
maybe_inserter(&(wp->bareProduct()));
detail::do_post_insert_if_available(wp->bareProduct());

PROD const* prod = wp->product();

Expand Down
6 changes: 2 additions & 4 deletions FWCore/Framework/interface/LuminosityBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,7 @@ namespace edm {
void LuminosityBlock::putImpl(EDPutToken::value_type index, std::unique_ptr<PROD> product) {
// The following will call post_insert if T has such a function,
// and do nothing if T has no such function.
std::conditional_t<detail::has_postinsert<PROD>::value, DoPostInsert<PROD>, DoNotPostInsert<PROD>> maybe_inserter;
maybe_inserter(product.get());
detail::do_post_insert_if_available(*product.get());

assert(index < putProducts().size());

Expand Down Expand Up @@ -271,8 +270,7 @@ namespace edm {

// The following will call post_insert if T has such a function,
// and do nothing if T has no such function.
std::conditional_t<detail::has_postinsert<PROD>::value, DoPostInsert<PROD>, DoNotPostInsert<PROD>> maybe_inserter;
maybe_inserter(&(wp->bareProduct()));
detail::do_post_insert_if_available(wp->bareProduct());

putProducts()[index] = std::move(wp);
}
Expand Down
56 changes: 13 additions & 43 deletions FWCore/Framework/interface/PrincipalGetAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,13 @@ namespace edm {
unsigned int processBlockIndex(std::string const& processName) const;

private:
template <typename T>
static constexpr bool hasMergeProductFunction() {
if constexpr (requires(T& a, T const& b) { a.mergeProduct(b); }) {
return true;
}
return false;
}
// Is this an Event, a LuminosityBlock, or a Run.
BranchType const& branchType() const;

Expand Down Expand Up @@ -254,59 +261,22 @@ namespace edm {
// PrincipalGetAdapter::put member template.
//

// has_postinsert is a metafunction of one argument, the type T. As
// with many metafunctions, it is implemented as a class with a data
// member 'value', which contains the value 'returned' by the
// metafunction.
//
// has_postinsert<T>::value is 'true' if T has the post_insert
// member function (with the right signature), and 'false' if T has
// no such member function.

namespace detail {
using no_tag = std::false_type; // type indicating FALSE
using yes_tag = std::true_type; // type indicating TRUE

// Definitions forthe following struct and function templates are
// not needed; we only require the declarations.
template <typename T, void (T::*)()>
struct postinsert_function;
template <typename T>
no_tag has_postinsert_helper(...);
template <typename T>
yes_tag has_postinsert_helper(postinsert_function<T, &T::post_insert>* p);

template <typename T>
struct has_postinsert {
static constexpr bool value = std::is_same<decltype(has_postinsert_helper<T>(nullptr)), yes_tag>::value &&
!std::is_base_of<DoNotSortUponInsertion, T>::value;
};

void do_post_insert_if_available(T& iProduct) {
if constexpr (not std::derived_from<T, DoNotSortUponInsertion> and requires(T& p) { p.post_insert(); }) {
iProduct.post_insert();
}
}
} // namespace detail

//------------------------------------------------------------

// The following function objects are used by Event::put, under the
// control of a metafunction if, to either call the given object's
// post_insert function (if it has one), or to do nothing (if it
// does not have a post_insert function).
template <typename T>
struct DoPostInsert {
void operator()(T* p) const { p->post_insert(); }
};

template <typename T>
struct DoNotPostInsert {
void operator()(T*) const {}
};

// Implementation of PrincipalGetAdapter member templates. See PrincipalGetAdapter.cc for the
// implementation of non-template members.
//

template <typename PROD>
inline bool PrincipalGetAdapter::checkIfComplete() const {
return isComplete() || !detail::has_mergeProduct_function<PROD>::value;
return isComplete() || !hasMergeProductFunction<PROD>();
}

} // namespace edm
Expand Down
6 changes: 2 additions & 4 deletions FWCore/Framework/interface/ProcessBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,7 @@ namespace edm {
void ProcessBlock::putImpl(EDPutToken::value_type index, std::unique_ptr<PROD> product) {
// The following will call post_insert if T has such a function,
// and do nothing if T has no such function.
std::conditional_t<detail::has_postinsert<PROD>::value, DoPostInsert<PROD>, DoNotPostInsert<PROD>> maybe_inserter;
maybe_inserter(product.get());
detail::do_post_insert_if_available(*product.get());

assert(index < putProducts().size());

Expand All @@ -208,8 +207,7 @@ namespace edm {

// The following will call post_insert if T has such a function,
// and do nothing if T has no such function.
std::conditional_t<detail::has_postinsert<PROD>::value, DoPostInsert<PROD>, DoNotPostInsert<PROD>> maybe_inserter;
maybe_inserter(&(wp->bareProduct()));
detail::do_post_insert_if_available(wp->bareProduct());

putProducts()[index] = std::move(wp);
}
Expand Down
6 changes: 2 additions & 4 deletions FWCore/Framework/interface/Run.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,7 @@ namespace edm {
void Run::putImpl(EDPutToken::value_type index, std::unique_ptr<PROD> product) {
// The following will call post_insert if T has such a function,
// and do nothing if T has no such function.
std::conditional_t<detail::has_postinsert<PROD>::value, DoPostInsert<PROD>, DoNotPostInsert<PROD>> maybe_inserter;
maybe_inserter(product.get());
detail::do_post_insert_if_available(*product.get());

assert(index < putProducts().size());

Expand Down Expand Up @@ -270,8 +269,7 @@ namespace edm {

// The following will call post_insert if T has such a function,
// and do nothing if T has no such function.
std::conditional_t<detail::has_postinsert<PROD>::value, DoPostInsert<PROD>, DoNotPostInsert<PROD>> maybe_inserter;
maybe_inserter(&(wp->bareProduct()));
detail::do_post_insert_if_available(wp->bareProduct());

putProducts()[index] = std::move(wp);
}
Expand Down