Commit 2da5f327 authored by Marco Clemencic's avatar Marco Clemencic
Browse files

Add support for Gaudi::Algorithm as baseclass of Functional algorithms (!897)

parents 921d005c 08d0f97a
......@@ -5,14 +5,15 @@
#include "GaudiAlg/FunctionalUtilities.h"
#include <utility>
namespace Gaudi {
namespace Functional {
namespace Gaudi::Functional {
template <typename Signature, typename Traits_ = Traits::useDefaults>
namespace details {
template <typename Signature, typename Traits_, bool isLegacy>
class Consumer;
template <typename... In, typename Traits_>
class Consumer<void( const In&... ), Traits_>
class Consumer<void( const In&... ), Traits_, true>
: public details::DataHandleMixin<void, details::filter_evtcontext<In...>, Traits_> {
public:
using details::DataHandleMixin<void, details::filter_evtcontext<In...>, Traits_>::DataHandleMixin;
......@@ -31,7 +32,33 @@ namespace Gaudi {
// ... instead, they must implement the following operator
virtual void operator()( const In&... ) const = 0;
};
} // namespace Functional
} // namespace Gaudi
template <typename... In, typename Traits_>
class Consumer<void( const In&... ), Traits_, false>
: public details::DataHandleMixin<void, details::filter_evtcontext<In...>, Traits_> {
public:
using details::DataHandleMixin<void, details::filter_evtcontext<In...>, Traits_>::DataHandleMixin;
// derived classes are NOT allowed to implement execute ...
StatusCode execute( const EventContext& ctx ) const override final {
try {
details::filter_evtcontext_t<In...>::apply( *this, ctx, this->m_inputs );
} catch ( GaudiException& e ) {
( e.code() ? this->warning() : this->error() ) << e.message() << endmsg;
return e.code();
}
return StatusCode::SUCCESS;
}
// ... instead, they must implement the following operator
virtual void operator()( const In&... ) const = 0;
};
} // namespace details
template <typename Signature, typename Traits_ = Traits::useDefaults>
using Consumer = details::Consumer<Signature, Traits_, details::isLegacy<Traits_>>;
} // namespace Gaudi::Functional
#endif
......@@ -6,14 +6,15 @@
#include <type_traits>
#include <utility>
namespace Gaudi {
namespace Functional {
namespace Gaudi ::Functional {
template <typename T, typename Traits_ = Traits::useDefaults>
namespace details {
template <typename T, typename Traits_, bool isLegacy>
class FilterPredicate;
template <typename... In, typename Traits_>
class FilterPredicate<bool( const In&... ), Traits_>
class FilterPredicate<bool( const In&... ), Traits_, true>
: public details::DataHandleMixin<void, std::tuple<In...>, Traits_> {
public:
using details::DataHandleMixin<void, std::tuple<In...>, Traits_>::DataHandleMixin;
......@@ -32,7 +33,34 @@ namespace Gaudi {
// ... instead, they must implement the following operator
virtual bool operator()( const In&... ) const = 0;
};
} // namespace Functional
} // namespace Gaudi
template <typename... In, typename Traits_>
class FilterPredicate<bool( const In&... ), Traits_, false>
: public details::DataHandleMixin<void, std::tuple<In...>, Traits_> {
public:
using details::DataHandleMixin<void, std::tuple<In...>, Traits_>::DataHandleMixin;
// derived classes are NOT allowed to implement execute ...
StatusCode execute( const EventContext& ctx ) const override final {
try {
this->execState( ctx ).setFilterPassed(
details::filter_evtcontext_t<In...>::apply( *this, ctx, this->m_inputs ) );
return StatusCode::SUCCESS;
} catch ( GaudiException& e ) {
( e.code() ? this->warning() : this->error() ) << e.message() << endmsg;
return e.code();
}
}
// ... instead, they must implement the following operator
virtual bool operator()( const In&... ) const = 0;
};
} // namespace details
template <typename Signature, typename Traits_ = Traits::useDefaults>
using FilterPredicate = details::FilterPredicate<Signature, Traits_, details::isLegacy<Traits_>>;
} // namespace Gaudi::Functional
#endif
This diff is collapsed.
......@@ -11,82 +11,77 @@
#include "GaudiKernel/DataObjectHandle.h"
#include "GaudiKernel/SerializeSTL.h"
namespace Gaudi {
namespace Functional {
// This utility is needed when the inputs of a functional algorithm may be stored in several locations
inline std::string concat_alternatives( std::initializer_list<std::string> c ) {
return boost::algorithm::join( c, ":" );
}
template <typename... Strings>
std::string concat_alternatives( const Strings&... s ) {
return concat_alternatives( std::initializer_list<std::string>{s...} );
}
inline void updateHandleLocation( IProperty& parent, const std::string& prop, const std::string& newLoc ) {
auto sc = parent.setProperty( prop, newLoc );
if ( sc.isFailure() ) throw GaudiException( "Could not set Property", prop + " -> " + newLoc, sc );
}
inline void updateHandleLocations( IProperty& parent, const std::string& prop,
const std::vector<std::string>& newLocs ) {
std::ostringstream ss;
GaudiUtils::details::ostream_joiner( ss << '[', newLocs, ", ", []( std::ostream & os, const auto& i ) -> auto& {
return os << "'" << i << "'";
} ) << ']';
auto sc = parent.setProperty( prop, ss.str() );
if ( sc.isFailure() ) throw GaudiException( "Could not set Property", prop + " -> " + ss.str(), sc );
}
[[deprecated( "please use updateHandleLocation instead" )]] inline void
updateReadHandleLocation( IProperty& parent, const std::string& prop, const std::string& newLoc ) {
return updateHandleLocation( parent, prop, newLoc );
}
namespace Traits {
// traits classes used to customize Transformer and FilterPredicate
// Define the types to to be used as baseclass, and as in- resp. output hanldes.
// In case a type is not specified in the traits struct, a default is used.
//
// The defaults are:
//
// using BaseClass = GaudiAlgorithm
// template <typename T> using InputHandle = DataObjectHandle<T>;
// template <typename T> using OutputHandle = DataObjectHandle<T>;
//
// the best way to 'compose' traits is by inheriting them one-by-one...
template <typename... Base>
struct use_ : Base... {};
// helper classes one can inherit from to specify a specific trait
template <typename Base>
struct BaseClass_t {
using BaseClass = Base;
};
template <template <typename> class Handle>
struct InputHandle_t {
template <typename T>
using InputHandle = Handle<T>;
};
template <template <typename> class Handle>
struct OutputHandle_t {
template <typename T>
using OutputHandle = Handle<T>;
};
// this uses the defaults -- and it itself is the default ;-)
using useDefaults = use_<>;
// this example uses GaudiHistoAlg as baseclass, and the default handle types for
// input and output
using useGaudiHistoAlg = use_<BaseClass_t<GaudiHistoAlg>>;
} // namespace Traits
} // namespace Functional
} // namespace Gaudi
namespace Gaudi::Functional {
// This utility is needed when the inputs of a functional algorithm may be stored in several locations
inline std::string concat_alternatives( std::initializer_list<std::string> c ) {
return boost::algorithm::join( c, ":" );
}
template <typename... Strings>
std::string concat_alternatives( const Strings&... s ) {
return concat_alternatives( std::initializer_list<std::string>{s...} );
}
[[deprecated( "please use `updateHandleLocation` instead of `Gaudi::Functional::updateHandleLocation`" )]] inline void
updateHandleLocation( IProperty& parent, const std::string& prop, const std::string& newLoc ) {
auto sc = parent.setProperty( prop, newLoc );
if ( sc.isFailure() ) throw GaudiException( "Could not set Property", prop + " -> " + newLoc, sc );
}
[[deprecated(
"please use `updateHandleLocations` instead of `Gaudi::Functional::updateHandleLocations`" )]] inline void
updateHandleLocations( IProperty& parent, const std::string& prop, const std::vector<std::string>& newLocs ) {
std::ostringstream ss;
GaudiUtils::details::ostream_joiner( ss << '[', newLocs, ", ", []( std::ostream & os, const auto& i ) -> auto& {
return os << "'" << i << "'";
} ) << ']';
auto sc = parent.setProperty( prop, ss.str() );
if ( sc.isFailure() ) throw GaudiException( "Could not set Property", prop + " -> " + ss.str(), sc );
}
namespace Traits {
// traits classes used to customize Transformer and FilterPredicate
// Define the types to to be used as baseclass, and as in- resp. output hanldes.
// In case a type is not specified in the traits struct, a default is used.
//
// The defaults are:
//
// using BaseClass = GaudiAlgorithm
// template <typename T> using InputHandle = DataObjectHandle<T>;
// template <typename T> using OutputHandle = DataObjectHandle<T>;
//
// the best way to 'compose' traits is by inheriting them one-by-one...
template <typename... Base>
struct use_ : Base... {};
// helper classes one can inherit from to specify a specific trait
template <typename Base>
struct BaseClass_t {
using BaseClass = Base;
};
template <template <typename> class Handle>
struct InputHandle_t {
template <typename T>
using InputHandle = Handle<T>;
};
template <template <typename> class Handle>
struct OutputHandle_t {
template <typename T>
using OutputHandle = Handle<T>;
};
// this uses the defaults -- and it itself is the default ;-)
using useDefaults = use_<>;
// this example uses GaudiHistoAlg as baseclass, and the default handle types for
// input and output
using useGaudiHistoAlg = use_<BaseClass_t<GaudiHistoAlg>>;
} // namespace Traits
} // namespace Gaudi::Functional
#endif
......@@ -8,26 +8,40 @@
#include "GaudiAlg/FunctionalDetails.h"
#include "GaudiAlg/FunctionalUtilities.h"
namespace Gaudi {
namespace Functional {
namespace Gaudi::Functional {
template <typename Signature, typename Traits_ = Traits::useDefaults>
class MergingTransformer;
using details::vector_of_const_;
namespace details {
using details::vector_of_const_;
template <typename Signature, typename Traits_, bool isLegacy>
class MergingTransformer;
////// Many of the same -> 1
template <typename Out, typename In, typename Traits_>
class MergingTransformer<Out( const vector_of_const_<In>& ), Traits_>
class MergingTransformer<Out( const vector_of_const_<In>& ), Traits_, true>
: public details::DataHandleMixin<std::tuple<Out>, void, Traits_> {
using base_class = details::DataHandleMixin<std::tuple<Out>, void, Traits_>;
public:
using KeyValue = std::pair<std::string, std::string>;
using KeyValues = std::pair<std::string, std::vector<std::string>>;
using KeyValue = typename base_class::KeyValue;
using KeyValues = typename base_class::KeyValues;
MergingTransformer( const std::string& name, ISvcLocator* locator, const KeyValues& inputs,
const KeyValue& output );
const KeyValue& output )
: base_class( name, locator, output )
, m_inputLocations{this, inputs.first, inputs.second,
[=]( Gaudi::Details::PropertyBase& ) {
this->m_inputs = details::make_vector_of_handles<decltype( this->m_inputs )>(
this, m_inputLocations );
if ( std::is_pointer<In>::value ) { // handle constructor does not (yet) allow to set
// optional flag... so do it
// explicitly here...
std::for_each( this->m_inputs.begin(), this->m_inputs.end(),
[]( auto& h ) { h.setOptional( true ); } );
}
},
Gaudi::Details::Property::ImmediatelyInvokeHandler{true}} {}
// accessor to input Locations
const std::string& inputLocation( unsigned int n ) const { return m_inputLocations.value()[n]; }
......@@ -40,13 +54,12 @@ namespace Gaudi {
std::transform( m_inputs.begin(), m_inputs.end(), std::back_inserter( ins ),
details::details2::get_from_handle<In>{} );
try {
using details::as_const;
details::put( std::get<0>( this->m_outputs ), as_const( *this )( as_const( ins ) ) );
details::put( std::get<0>( this->m_outputs ), std::as_const( *this )( std::as_const( ins ) ) );
return StatusCode::SUCCESS;
} catch ( GaudiException& e ) {
( e.code() ? this->warning() : this->error() ) << e.message() << endmsg;
return e.code();
}
return StatusCode::SUCCESS;
}
virtual Out operator()( const vector_of_const_<In>& inputs ) const = 0;
......@@ -62,24 +75,66 @@ namespace Gaudi {
};
template <typename Out, typename In, typename Traits_>
MergingTransformer<Out( const vector_of_const_<In>& ), Traits_>::MergingTransformer( const std::string& name,
ISvcLocator* pSvcLocator,
const KeyValues& inputs,
const KeyValue& output )
: base_class( name, pSvcLocator, output )
, m_inputLocations{this, inputs.first, inputs.second,
[=]( Gaudi::Details::PropertyBase& ) {
this->m_inputs =
details::make_vector_of_handles<decltype( this->m_inputs )>( this, m_inputLocations );
if ( std::is_pointer<In>::value ) { // handle constructor does not (yet) allow to set
// optional flag... so do it
// explicitly here...
std::for_each( this->m_inputs.begin(), this->m_inputs.end(),
[]( auto& h ) { h.setOptional( true ); } );
}
},
Gaudi::Details::Property::ImmediatelyInvokeHandler{true}} {}
} // namespace Functional
} // namespace Gaudi
class MergingTransformer<Out( const vector_of_const_<In>& ), Traits_, false>
: public details::DataHandleMixin<std::tuple<Out>, void, Traits_> {
using base_class = details::DataHandleMixin<std::tuple<Out>, void, Traits_>;
public:
using KeyValue = typename base_class::KeyValue;
using KeyValues = typename base_class::KeyValues;
MergingTransformer( const std::string& name, ISvcLocator* locator, const KeyValues& inputs,
const KeyValue& output )
: base_class( name, locator, output )
, m_inputLocations{this, inputs.first, inputs.second,
[=]( Gaudi::Details::PropertyBase& ) {
this->m_inputs = details::make_vector_of_handles<decltype( this->m_inputs )>(
this, m_inputLocations );
if ( std::is_pointer<In>::value ) { // handle constructor does not (yet) allow to set
// optional flag... so do it
// explicitly here...
std::for_each( this->m_inputs.begin(), this->m_inputs.end(),
[]( auto& h ) { h.setOptional( true ); } );
}
},
Gaudi::Details::Property::ImmediatelyInvokeHandler{true}} {}
// accessor to input Locations
const std::string& inputLocation( unsigned int n ) const { return m_inputLocations.value()[n]; }
unsigned int inputLocationSize() const { return m_inputLocations.value().size(); }
// derived classes can NOT implement execute
StatusCode execute( const EventContext& ) const override final {
vector_of_const_<In> ins;
ins.reserve( m_inputs.size() );
std::transform( m_inputs.begin(), m_inputs.end(), std::back_inserter( ins ),
details::details2::get_from_handle<In>{} );
try {
details::put( std::get<0>( this->m_outputs ), std::as_const( *this )( std::as_const( ins ) ) );
return StatusCode::SUCCESS;
} catch ( GaudiException& e ) {
( e.code() ? this->warning() : this->error() ) << e.message() << endmsg;
return e.code();
}
}
virtual Out operator()( const vector_of_const_<In>& inputs ) const = 0;
private:
// if In is a pointer, it signals optional (as opposed to mandatory) input
template <typename T>
using InputHandle_t = details::InputHandle_t<Traits_, typename std::remove_pointer<T>::type>;
std::vector<InputHandle_t<In>> m_inputs; // and make the handles properties instead...
Gaudi::Property<std::vector<std::string>> m_inputLocations; // TODO/FIXME: remove this duplication...
// TODO/FIXME: replace vector of string property + call-back with a
// vector<handle> property ... as soon as declareProperty can deal with that.
};
} // namespace details
template <typename Signature, typename Traits_ = Traits::useDefaults>
using MergingTransformer = details::MergingTransformer<Signature, Traits_, details::isLegacy<Traits_>>;
} // namespace Gaudi::Functional
#endif
......@@ -5,14 +5,16 @@
#include "GaudiAlg/FunctionalUtilities.h"
#include <utility>
namespace Gaudi {
namespace Functional {
namespace Gaudi::Functional {
template <typename Signature, typename Traits_ = Traits::useDefaults>
namespace details {
template <typename Signature, typename Traits_, bool isLegacy>
class Producer;
template <typename... Out, typename Traits_>
class Producer<std::tuple<Out...>(), Traits_> : public details::DataHandleMixin<std::tuple<Out...>, void, Traits_> {
class Producer<std::tuple<Out...>(), Traits_, true>
: public details::DataHandleMixin<std::tuple<Out...>, void, Traits_> {
public:
using details::DataHandleMixin<std::tuple<Out...>, void, Traits_>::DataHandleMixin;
......@@ -25,14 +27,43 @@ namespace Gaudi {
[&ohandle...]( auto&&... data ) {
( details::put( ohandle, std::forward<decltype( data )>( data ) ), ... );
},
details::as_const( *this )() );
std::as_const( *this )() );
},
this->m_outputs );
return StatusCode::SUCCESS;
} catch ( GaudiException& e ) {
( e.code() ? this->warning() : this->error() ) << e.message() << endmsg;
return e.code();
}
}
// ... instead, they must implement the following operator
virtual std::tuple<Out...> operator()() const = 0;
};
template <typename... Out, typename Traits_>
class Producer<std::tuple<Out...>(), Traits_, false>
: public details::DataHandleMixin<std::tuple<Out...>, void, Traits_> {
public:
using details::DataHandleMixin<std::tuple<Out...>, void, Traits_>::DataHandleMixin;
// derived classes are NOT allowed to implement execute ...
StatusCode execute( const EventContext& ) const override final {
try {
std::apply(
[&]( auto&... ohandle ) {
std::apply(
[&ohandle...]( auto&&... data ) {
( details::put( ohandle, std::forward<decltype( data )>( data ) ), ... );
},
std::as_const( *this )() );
},
this->m_outputs );
return StatusCode::SUCCESS;
} catch ( GaudiException& e ) {
( e.code() ? this->warning() : this->error() ) << e.message() << endmsg;
return e.code();
}
return StatusCode::SUCCESS;
}
// ... instead, they must implement the following operator
......@@ -40,24 +71,47 @@ namespace Gaudi {
};
template <typename Out, typename Traits_>
class Producer<Out(), Traits_> : public details::DataHandleMixin<std::tuple<Out>, void, Traits_> {
class Producer<Out(), Traits_, true> : public details::DataHandleMixin<std::tuple<Out>, void, Traits_> {
public:
using details::DataHandleMixin<std::tuple<Out>, void, Traits_>::DataHandleMixin;
// derived classes are NOT allowed to implement execute ...
StatusCode execute() override final {
try {
details::put( std::get<0>( this->m_outputs ), details::as_const( *this )() );
details::put( std::get<0>( this->m_outputs ), std::as_const( *this )() );
return StatusCode::SUCCESS;
} catch ( GaudiException& e ) {
( e.code() ? this->warning() : this->error() ) << e.message() << endmsg;
return e.code();
}
return StatusCode::SUCCESS;
}
// ... instead, they must implement the following operator
virtual Out operator()() const = 0;
};
} // namespace Functional
} // namespace Gaudi
template <typename Out, typename Traits_>
class Producer<Out(), Traits_, false> : public details::DataHandleMixin<std::tuple<Out>, void, Traits_> {
public:
using details::DataHandleMixin<std::tuple<Out>, void, Traits_>::DataHandleMixin;
// derived classes are NOT allowed to implement execute ...
StatusCode execute( const EventContext& ) const override final {
try {
details::put( std::get<0>( this->m_outputs ), std::as_const( *this )() );
return StatusCode::SUCCESS;
} catch ( GaudiException& e ) {
( e.code() ? this->warning() : this->error() ) << e.message() << endmsg;
return e.code();
}
}
// ... instead, they must implement the following operator
virtual Out operator()() const = 0;
};
} // namespace details
template <typename Signature, typename Traits_ = Traits::useDefaults>
using Producer = details::Producer<Signature, Traits_, details::isLegacy<Traits_>>;
} // namespace Gaudi::Functional
#endif
......@@ -8,30 +8,44 @@
#include "GaudiAlg/FunctionalDetails.h"
#include "GaudiAlg/FunctionalUtilities.h"
namespace Gaudi {
namespace Functional {
namespace Gaudi::Functional {
template <typename Signature, typename Traits_ = Traits::useDefaults>
class SplittingTransformer;
template <typename Container>
using vector_of_ = std::vector<Container>;
template <typename Container>
using vector_of_optional_ = std::vector<boost::optional<Container>>;
namespace details {
template <typename Container>
using vector_of_ = std::vector<Container>;
template <typename Container>
using vector_of_optional_ = std::vector<boost::optional<Container>>;
template <typename Signature, typename Traits_, bool isLegacy>
class SplittingTransformer;
////// N -> Many of the same one (value of Many not known at compile time, but known at configuration time)
template <typename Out, typename... In, typename Traits_>
class SplittingTransformer<vector_of_<Out>( const In&... ), Traits_>
class SplittingTransformer<vector_of_<Out>( const In&... ), Traits_, true>
: public details::DataHandleMixin<void, std::tuple<In...>, Traits_> {
using base_class = details::DataHandleMixin<void, std::tuple<In...>, Traits_>;
public:
constexpr static std::size_t N = sizeof...( In );
using KeyValue = std::pair<std::string, std::string>;
using KeyValues = std::pair<std::string, std::vector<std::string>>;
constexpr static std::size_t N = base_class::N_in;
using KeyValue = typename base_class::KeyValue;
using KeyValues = typename base_class::KeyValues;
SplittingTransformer( const std::string& name, ISvcLocator* locator, const std::array<KeyValue, N>& inputs,
const KeyValues& output );