diff --git a/README/ReleaseNotes/v642/index.md b/README/ReleaseNotes/v642/index.md index 293187bde9380..7a16b865e42e2 100644 --- a/README/ReleaseNotes/v642/index.md +++ b/README/ReleaseNotes/v642/index.md @@ -69,6 +69,7 @@ Users are encouraged to export their models to ONNX and use the retained ONNX pa * Support for the AIX operating system has been removed from the codebase. This support has not been tested since the late v5 releases and the LLVM JIT is not yet supporting AIX. * The headers Htypes.h and Gtypes.h that were deprecated in ROOT 6.20 will now emit warnings and will be fully removed in ROOT 6.44. * The header GLConstants.h is no longer part of ROOT installed headers. +* The `ROOT::Math::ParamFunctionBase`, `ROOT::Math::ParamFunctorHandler` and `ROOT::Math::ParamMemFunHandler` classes in `Math/ParamFunctor.h` are removed, together with the `ParamFunctor::GetImpl()` and `ParamFunctor::SetFunction()` methods that exposed them. They implemented the type erasure that `ParamFunctor` now gets from `std::function`, mirroring what was already done for `ROOT::Math::Functor`. Constructing and calling a `ParamFunctor` is unchanged, except that the constructor from an object and one of its member functions now takes a plain pointer to the object instead of anything dereferenceable, so smart pointers are no longer accepted there. ## Build System diff --git a/math/mathcore/inc/Math/ParamFunctor.h b/math/mathcore/inc/Math/ParamFunctor.h index 789192f0b5600..aa6ae02553747 100644 --- a/math/mathcore/inc/Math/ParamFunctor.h +++ b/math/mathcore/inc/Math/ParamFunctor.h @@ -9,256 +9,20 @@ **********************************************************************/ // Header file for Functor classes. -// design is inspired by the Loki Functor #ifndef ROOT_Math_ParamFunctor #define ROOT_Math_ParamFunctor -// #ifndef ROOT_Math_IFunction -// #include "Math/IFunction.h" -// #endif - -// #ifndef Root_Math_StaticCheck -// #include "Math/StaticCheck.h" -// #endif - -//#include - #include "RtypesCore.h" + #include -#include +#include +#include namespace ROOT { namespace Math { -/** - * \defgroup ParamFunctor_int N-D parametric functions - * \brief Multi-dimensional parametric functions - * \ingroup Math - */ - -/** class defining the signature for multi-dim parametric functions - - @ingroup ParamFunctor_int - */ -template -class ParamFunctionBase { - public: - virtual ~ParamFunctionBase() {} - virtual T operator() (const T * x, const double *p) = 0; - virtual T operator() (T * x, double *p) = 0; - virtual ParamFunctionBase * Clone() const = 0; -}; - - - -/** - ParamFunctor Handler class is responsible for wrapping any other functor and pointer to - free C functions. - It can be created from any function implementing the correct signature - corresponding to the requested type - - @ingroup ParamFunctor_int - -*/ - -template -class ParamFunctorHandler : public ParentFunctor::Impl { - - typedef typename ParentFunctor::EvalType EvalType; - typedef typename ParentFunctor::Impl Base; - -public: - - // constructor - ParamFunctorHandler(const Func & fun) : fFunc(fun) {} - - - virtual ~ParamFunctorHandler() {} - - - // for 1D functions - inline EvalType operator() (EvalType x, double *p) { - return fFunc(x,p); - } -// inline double operator() (double x, const double *p) const { -// return fFunc(x,p); -// } - // for multi-dimensional functions -// inline double operator() (const double * x, const double *p) const { -// return fFunc(x,p); -// } - inline EvalType operator() (EvalType * x, double *p) override { - return FuncEvaluator::Eval(fFunc,x,p); - } - - inline EvalType operator() (const EvalType * x, const double *p) override { - return FuncEvaluator::EvalConst(fFunc,x,p); - } - - // clone (use same pointer) - ParamFunctorHandler * Clone() const override { - return new ParamFunctorHandler(fFunc); - } - - -private : - - Func fFunc; - - // structure to distinguish pointer types - template struct FuncEvaluator { - inline static T Eval( F & f, T *x, double * p) { - return f(x, p); - } - - inline static T EvalConst( F & f, const T *x, const double * p) { - return f((T*)x, (double*)p); - } - }; - - template struct FuncEvaluator { - inline static T Eval( F * f, T *x, double * p) { - return (*f)(x, p); - } - - inline static T EvalConst( F * f, const T *x, const double * p) { - return (*f)((T*)x, (double*)p); - - } - }; - - template struct FuncEvaluator { - inline static T Eval( const F * f, T *x, double * p) { - return (*f)(x, p); - } - - inline static T EvalConst( const F * f, const T *x, const double * p) { - return (*f)((T*)x, (double*)p); - } - }; - - // need maybe also volatile ? -}; - - -#if defined(__ROOTCLING__) || defined(G__DICTIONARY) -// needed since Cling initialize it with TRootIOCtor -//class TRootIOCtor; -template -class ParamFunctorHandler : public ParentFunctor::Impl -{ -public: - - ParamFunctorHandler(TRootIOCtor *) {} - - double operator() (double *, double * ) { return 0; } - - double operator() (const double *, const double * ) { return 0; } - // clone (use same pointer) - ParamFunctorHandler * Clone() const { - return 0; - } - -}; -#endif - - -/** - ParamFunctor Handler to Wrap pointers to member functions - - @ingroup ParamFunctor_int -*/ -template -class ParamMemFunHandler : public ParentFunctor::Impl -{ - typedef typename ParentFunctor::Impl Base; - - -public: - - /// constructor from a pointer to the class and a pointer to the function - ParamMemFunHandler(const PointerToObj& pObj, PointerToMemFn pMemFn) - : fObj(pObj), fMemFn(pMemFn) - {} - - virtual ~ParamMemFunHandler() {} - -// inline double operator() (double x, const double * p) const { -// return ((*fObj).*fMemFn)(x,p); -// } - - inline double operator() (double x, double * p) { - return ((*fObj).*fMemFn)(x,p); - } - -// inline double operator() (const double * x, const double * p) const { -// return ((*fObj).*fMemFn)(x,p); -// } - - inline double operator() (double * x, double * p) override { - return MemFuncEvaluator::Eval(fObj,fMemFn,x,p); - } - - inline double operator() (const double * x, const double * p) override { - return MemFuncEvaluator::EvalConst(fObj,fMemFn,x,p); - } - - // clone (use same pointer) - ParamMemFunHandler * Clone() const override { - return new ParamMemFunHandler(fObj, fMemFn); - } - -private: - - // structure to distinguish pointer types - template struct MemFuncEvaluator { - inline static T Eval(PObj & pobj, F & f, T *x, double * p) { - return ((*pobj).*f)(x, p); - } - - inline static T EvalConst(PObj & pobj, F & f, const T *x, const double * p) { - return ((*pobj).*f)((T*)x, (double*)p); - } - }; - - - // // these are needed ?? - // template struct MemFuncEvaluator { - // inline static T Eval(PObj & pobj, F * f, T *x, double * p) { - // return ((*pobj).*f)f(x, p); - // } - - // inline static T EvalConst(PObj & pobj, F * f, const T *x, const double * p) { - // return ((*pobj).*f)((T*)x, (double*)p); - - // } - // }; - - // template struct FuncEvaluator { - // inline static T Eval(PObj &, const F * f, T *x, double * p) { - // return ((*pobj).*f)f(x, p); - // } - - // inline static T EvalConst(PObj & pobj, const F * f, const T *x, const double * p) { - // return ((*pobj).*f)((T*)x, (double*)p); - // } - // }; - -private : - ParamMemFunHandler(const ParamMemFunHandler&) = delete; // Not implemented - ParamMemFunHandler& operator=(const ParamMemFunHandler&) = delete; // Not implemented - - PointerToObj fObj; - PointerToMemFn fMemFn; - -}; - - - - /** Param Functor class for Multidimensional functions. It is used to wrap in a very simple and convenient way @@ -270,127 +34,69 @@ private : */ - -template -class ParamFunctorTempl { - +template +class ParamFunctorTempl { public: + using EvalType = T; - typedef T EvalType; - typedef ParamFunctionBase Impl; - - - /** - Default constructor - */ - ParamFunctorTempl () : fImpl(nullptr) {} - - - /** - construct from a pointer to member function (multi-dim type) - */ - template - ParamFunctorTempl(const PtrObj& p, MemFn memFn) - : fImpl(new ParamMemFunHandler, PtrObj, MemFn>(p, memFn)) - {} - - - - /** - construct from another generic Functor of multi-dimension - */ - template - explicit ParamFunctorTempl( const Func & f) : - fImpl(new ParamFunctorHandler,Func>(f) ) - {} + /// The signature every wrapped callable is normalized to. + using Signature = T(const T *, const double *); + ParamFunctorTempl() = default; - - // specialization used in TF1 - typedef T (* FreeFunc ) (T * , double *); - ParamFunctorTempl(FreeFunc f) : - fImpl(new ParamFunctorHandler,FreeFunc>(f) ) - { - } - - // specialization used in TF1 - ParamFunctorTempl(const std::function &func) : - fImpl(new ParamFunctorHandler, const std::function>(func)) + /// Construct from a pointer to a class object and a pointer to one of its member + /// functions, like `Foo::EvalPar(const double *x, const double *p)`. + template + ParamFunctorTempl(Obj *p, MemFn memFn) + : fFunc{[p, memFn](const T *x, const double *par) { + return (p->*memFn)(const_cast(x), const_cast(par)); + }} { } - /** - Destructor (no operations) - */ - virtual ~ParamFunctorTempl () { - if (fImpl) delete fImpl; - } - - /** - Copy constructor - */ - ParamFunctorTempl(const ParamFunctorTempl & rhs) : - fImpl(nullptr) + /// Construct from any callable object, or from a pointer to one. + /// + /// The callable is normalized to the `T (const T *, const double *)` signature: anything + /// `std::function` accepts as-is is handed to it directly, a pointer to a callable object is + /// called through without taking ownership of it, and callables that insist on non-const + /// pointers (the classic `T (T *x, double *p)` signature) get their arguments cast for them. + /// + /// The two exclusions keep this greedy forwarding reference away from overloads that a + /// non-const lvalue would otherwise bind to it by exact match: the implicit copy constructor, + /// which would end up wrapping a functor in itself, and the `std::function` conversion below, + /// which is implicit for the Python interface and would become ill-formed via this `explicit` one. + template , ParamFunctorTempl> && + !std::is_same_v, std::function>>> + explicit ParamFunctorTempl(Func &&f) { -// if (rhs.fImpl.get() != 0) -// fImpl = std::unique_ptr( (rhs.fImpl)->Clone() ); - if (rhs.fImpl) fImpl = rhs.fImpl->Clone(); - } - - /** - Assignment operator - */ - ParamFunctorTempl & operator = (const ParamFunctorTempl & rhs) { -// ParamFunctor copy(rhs); - // swap unique_ptr by hand -// Impl * p = fImpl.release(); -// fImpl.reset(copy.fImpl.release()); -// copy.fImpl.reset(p); - - if(this != &rhs) { - if (fImpl) delete fImpl; - fImpl = nullptr; - if (rhs.fImpl) - fImpl = rhs.fImpl->Clone(); + using F = std::decay_t; + if constexpr (std::is_constructible_v, Func>) { + fFunc = std::function{std::forward(f)}; + } else if constexpr (std::is_pointer_v && std::is_class_v>) { + fFunc = [f](const T *x, const double *p) { return (*f)(const_cast(x), const_cast(p)); }; + } else { + fFunc = [f = std::forward(f)](const T *x, const double *p) mutable { + return f(const_cast(x), const_cast(p)); + }; } - return *this; - } - - void * GetImpl() { return (void *) fImpl; } - - - T operator() ( T * x, double * p) { - return (*fImpl)(x,p); - } - - T operator() (const T * x, const double * p) { - return (*fImpl)(x,p); - } - - - bool Empty() const { return !fImpl; } - - - void SetFunction(Impl * f) { - fImpl = f; } -private : + /// Implicit conversion, relied on by the Python interface when passing a callable to TF1. + ParamFunctorTempl(std::function f) : fFunc{std::move(f)} {} + T operator()(const T *x, const double *p) const { return fFunc(x, p); } - //std::unique_ptr fImpl; - Impl * fImpl; - + bool Empty() const { return !fFunc; } +private: + std::function fFunc; }; - using ParamFunctor = ParamFunctorTempl; - } // end namespace Math +} // end namespace Math } // end namespace ROOT - #endif /* ROOT_Math_ParamFunctor */