Invocation: complete rework of the FeedManifold
This completes a deep and very challenging series of refactorings with the goal to introduce support for **Parameters** into the Render invocation code. A secondary goal was to re-assess the prototype code written thus far and thereby to establish a standard processing scheme. With these rearrangements, the `FeedManifold` is poised to act as **central link** between the Render-Node invocation code and the actual Media-Processing code in a Library Plug-in Up to this point, the existing code from the Prototype is still compilable, yet broken. The __next step__ will be to harness the possible simplifications and enable the actual invocation to work on arbitrary combinations of buffers and parameters, enabled by the **compile-time use-case classification** now provided by `FeedManifold`
This commit is contained in:
parent
72703f70c9
commit
0ccc2d0b89
3 changed files with 717 additions and 168 deletions
|
|
@ -80,7 +80,6 @@
|
|||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
//#include "steam/engine/proc-node.hpp"
|
||||
#include "steam/engine/buffhandle.hpp"
|
||||
#include "lib/uninitialised-storage.hpp"
|
||||
#include "lib/meta/function.hpp"
|
||||
|
|
@ -91,8 +90,6 @@
|
|||
#include "lib/test/test-helper.hpp"
|
||||
//#include "lib/several.hpp"
|
||||
|
||||
//#include <utility>
|
||||
//#include <array>
|
||||
#include <tuple>
|
||||
|
||||
|
||||
|
|
@ -101,14 +98,10 @@
|
|||
namespace steam {
|
||||
namespace engine {
|
||||
|
||||
// using std::pair;
|
||||
// using std::vector;
|
||||
|
||||
namespace {// Introspection helpers....
|
||||
|
||||
using lib::meta::_Fun;
|
||||
using lib::meta::enable_if;
|
||||
using lib::meta::disable_if_self;
|
||||
using lib::meta::is_UnaryFun;
|
||||
using lib::meta::is_BinaryFun;
|
||||
using lib::meta::is_TernaryFun;
|
||||
|
|
@ -117,14 +110,14 @@ namespace engine {
|
|||
using lib::meta::ElmTypes;
|
||||
using lib::meta::NullType;
|
||||
using lib::meta::Tagged;
|
||||
using lib::meta::TySeq;
|
||||
using std::declval;
|
||||
using std::is_pointer;
|
||||
using std::is_reference;
|
||||
using std::remove_reference_t;
|
||||
using std::is_convertible;
|
||||
using std::is_constructible;
|
||||
using std::is_copy_constructible;
|
||||
using std::remove_pointer_t;
|
||||
using std::tuple_element_t;
|
||||
using std::void_t;
|
||||
using std::add_pointer_t;
|
||||
using std::__and_;
|
||||
using std::__not_;
|
||||
|
||||
|
|
@ -267,13 +260,17 @@ namespace engine {
|
|||
|
||||
template<class PF>
|
||||
using Res = typename _Fun<PF>::Ret;
|
||||
template<class PF>
|
||||
using SigP = add_pointer_t<typename _Fun<PF>::Sig>;
|
||||
|
||||
template<class PF>
|
||||
using isSuitable = std::is_constructible<Param, Res<PF>>;
|
||||
using isSuitable = is_constructible<Param, Res<PF>>;
|
||||
|
||||
template<class PF>
|
||||
using isConfigurable = std::is_constructible<bool, PF&>;
|
||||
|
||||
using isConfigurable = __and_<is_constructible<bool, PF&>
|
||||
,__not_<is_convertible<PF&, SigP<PF>>>
|
||||
>; // non-capture-λ are convertible via function-ptr to bool
|
||||
// yet we want to detect a real built-in bool-conversion.
|
||||
template<class PF>
|
||||
static bool
|
||||
isActivated (PF const& paramFun)
|
||||
|
|
@ -547,6 +544,18 @@ namespace engine {
|
|||
* for each Node invocation.
|
||||
* @tparam FUN type of the data processing-functor
|
||||
* @tparam PAM type of an optional parameter-setup functor (defaults to deactivated)
|
||||
*
|
||||
* # Usage
|
||||
* The Prototype is typically first built solely from a processing-functor.
|
||||
* It can even be constructed as type only, by `FeedManifold<FUN>::Prototype`.
|
||||
* In this form, any parameter handling will be _disabled._ However, by adding a
|
||||
* parameter-functor with the **cross-builder-API**, a _new instance_ of the prototype
|
||||
* is created _as a replacement_ of the old one (note: we move the processing functor).
|
||||
* This adds a parameter-functor to the configuration, which will then be invoked
|
||||
* _whenever a new FeedManifold instance_ [is created](\ref #createFeed); the result of
|
||||
* this parameter-functor invocation should be a parameter value, which can be passed
|
||||
* into the constructor of FeedManifold, together with a copy of the proc-functor.
|
||||
* @see NodeBase_test::verify_FeedPrototype()
|
||||
*/
|
||||
template<class FUN, class PAM>
|
||||
class FeedPrototype
|
||||
|
|
@ -574,21 +583,32 @@ namespace engine {
|
|||
static constexpr bool hasParamFun() { return _Trait::template isParamFun<PAM>(); }
|
||||
static constexpr bool canActivate() { return _Trait::template canActivate<PAM>(); }
|
||||
|
||||
/**
|
||||
* build suitable Feed(Manifold) for processing Node invocation
|
||||
/** @return runtime test: there is actually usable parameter-functor to invoke? */
|
||||
bool isActivated() const { return _Trait::isActivated(paramFun_); }
|
||||
|
||||
|
||||
|
||||
/************************************************************//**
|
||||
* build suitable Feed(Manifold) for processing a Node invocation
|
||||
*/
|
||||
Feed
|
||||
createFeed (TurnoutSystem& turnoutSys)
|
||||
{
|
||||
if constexpr (hasParamFun())
|
||||
if (_Trait::isActivated(paramFun_))
|
||||
if (isActivated())
|
||||
return Feed(paramFun_(turnoutSys), procFun_);
|
||||
return Feed{procFun_};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======= cross-builder API ======= */
|
||||
|
||||
using ProcFun = FUN;
|
||||
using ParamFun = PAM;
|
||||
|
||||
template<typename PFX>
|
||||
using Adapted = FeedPrototype<FUN, PFX>;
|
||||
using Adapted = FeedPrototype<FUN,PFX>;
|
||||
|
||||
/**
|
||||
* Cross-Builder to add configuration with a given parameter-functor.
|
||||
|
|
@ -601,11 +621,37 @@ namespace engine {
|
|||
*/
|
||||
template<typename PFX>
|
||||
auto
|
||||
moveAdapted (PFX otherParamFun)
|
||||
moveAdapted (PFX otherParamFun =PFX{})
|
||||
{
|
||||
using OtherParamFun = std::decay_t<PFX>;
|
||||
return Adapted<OtherParamFun>{move(procFun_), move(otherParamFun)};
|
||||
}
|
||||
|
||||
|
||||
/** build a clone-copy of this prototype, holding the same functors
|
||||
* @note possible only if both proc-functor and param-functor are copyable
|
||||
*/
|
||||
enable_if<__and_<is_copy_constructible<FUN>
|
||||
,is_copy_constructible<PAM>>, FeedPrototype>
|
||||
clone() const
|
||||
{
|
||||
return FeedPrototype{FUN(procFun_), PAM(paramFun_)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the current parameter-functor setup by assigning some value.
|
||||
* @param paramFunDef something that is assignable to \a PAM
|
||||
* @note possible only if the param-functor accepts this kind of assignment;
|
||||
* especially when \a PAM was defined to be a `std::function`, then
|
||||
* the param-functor can not only be reconfigured, but also disabled.
|
||||
*/
|
||||
template<typename PFX =PAM, typename = enable_if<std::is_assignable<PAM,PFX>>>
|
||||
FeedPrototype&&
|
||||
assignParamFun (PFX&& paramFunDef =PAM{})
|
||||
{
|
||||
paramFun_ = forward<PFX> (paramFunDef);
|
||||
return move(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -332,6 +332,9 @@ namespace test {
|
|||
BufferProvider& provider = DiagnosticBufferProvider::build();
|
||||
BuffHandle buff = provider.lockBufferFor<Buffer> (-55);
|
||||
|
||||
|
||||
//_______________________________________
|
||||
// Case-1: Prototype without param-functor
|
||||
auto fun_singleParamOut = [](short param, Buffer* buff) { *buff = param-1; };
|
||||
using M1 = FeedManifold<decltype(fun_singleParamOut)>;
|
||||
using P1 = M1::Prototype;
|
||||
|
|
@ -357,8 +360,8 @@ namespace test {
|
|||
|
||||
|
||||
|
||||
//_____________________________________
|
||||
// Reconfigure to attach a param-functor
|
||||
//_____________________________________________
|
||||
// Case-2: Reconfigure to attach a param-functor
|
||||
long rr{11}; // ▽▽▽▽ Note: side-effect
|
||||
auto fun_paramSimple = [&](TurnoutSystem&){ return rr += 1+rani(100); };
|
||||
using P1x = P1::Adapted<decltype(fun_paramSimple)>;
|
||||
|
|
@ -395,6 +398,74 @@ namespace test {
|
|||
CHECK (calcResult == r1 - 1); // FeedManifold on the stack, since invocations are
|
||||
m1.invoke(); // performed concurrently, each with its own set of
|
||||
CHECK (calcResult == 0 - 1); // buffers and parameters.
|
||||
|
||||
|
||||
|
||||
//_______________________________
|
||||
// Case-3: Integrate std::function
|
||||
using ParamSig = short(TurnoutSystem&);
|
||||
using ParamFunction = std::function<ParamSig>;
|
||||
// a Prototype to hold such a function...
|
||||
using P1F = P1::Adapted<ParamFunction>;
|
||||
CHECK ( P1F::hasParam());
|
||||
CHECK ( P1F::hasParamFun());
|
||||
CHECK ( P1F::canActivate());
|
||||
|
||||
P1F p1f = p1x.clone() // if (and only if) the embedded functors allow clone-copy
|
||||
.moveAdapted<ParamFunction>(); // then we can fork-off and then adapt a cloned prototype
|
||||
|
||||
// Need to distinguish between static capability and runtime state...
|
||||
CHECK (not p1 .canActivate()); // Case-1 had no param functor installed...
|
||||
CHECK (not p1 .isActivated()); // and thus also can not invoke such a functor at runtime
|
||||
CHECK (not p1x.canActivate()); // Case-2 has a fixed param-λ, which can not be activated/deactivated
|
||||
CHECK ( p1x.isActivated()); // yet at runtime this functor is always active and callable
|
||||
CHECK ( p1f.canActivate()); // Case-3 was defined to hold a std::function, which thus can be toggled
|
||||
CHECK (not p1f.isActivated()); // yet in current runtime configuration, the function is empty
|
||||
|
||||
// create a FeedManifold instance from this prototype
|
||||
M1 m1f1 = p1f.createFeed(turSys); // no param-functor invoked,
|
||||
CHECK (m1f1.param == short{}); // so this FeedManifold will use the default-constructed parameter
|
||||
|
||||
// but since std::function is assignable, we can activate it...
|
||||
CHECK (not p1f.isActivated());
|
||||
p1f.assignParamFun ([](TurnoutSystem&){ return 47; });
|
||||
CHECK ( p1f.isActivated());
|
||||
M1 m1f2 = p1f.createFeed(turSys); // ◁————————— param-functor invoked here
|
||||
CHECK (m1f2.param == 47); // ...surprise: we got number 47...
|
||||
p1f.assignParamFun();
|
||||
CHECK (not p1f.isActivated()); // can /deactivate/ it again...
|
||||
M1 m1f3 = p1f.createFeed(turSys); // so no param-functor invoked here
|
||||
CHECK (m1f3.param == short{});
|
||||
|
||||
// done with buffer
|
||||
buff.release();
|
||||
|
||||
|
||||
|
||||
//_____________________________________
|
||||
// Addendum: type conversion intricacies
|
||||
auto lambdaSimple = [ ](TurnoutSystem&){ return short(47); };
|
||||
auto lambdaCapture = [&](TurnoutSystem&){ return short(47); };
|
||||
using LambdaSimple = decltype(lambdaSimple);
|
||||
using LambdaCapture = decltype(lambdaCapture);
|
||||
CHECK ( (std::is_constructible<bool,ParamFunction>::value));
|
||||
CHECK ( (std::is_constructible<bool,LambdaSimple >::value));
|
||||
CHECK (not (std::is_constructible<bool,LambdaCapture>::value));
|
||||
// Surprise! a non-capture-λ turns out to be bool convertible,
|
||||
// which however is also true for std::function,
|
||||
// yet for quite different reasons: While the latter has a
|
||||
// built-in conversion operator to detect /inactive/ state,
|
||||
// the simple λ decays to a function pointer, which makes it
|
||||
// usable as implementation for plain-C callback functions.
|
||||
using FunPtr = short(*)(TurnoutSystem&);
|
||||
CHECK (not (std::is_convertible<ParamFunction,FunPtr>::value));
|
||||
CHECK ( (std::is_convertible<LambdaSimple ,FunPtr>::value));
|
||||
CHECK (not (std::is_convertible<LambdaCapture,FunPtr>::value));
|
||||
// ..which allows to distinguish these cases..
|
||||
//
|
||||
CHECK (true == _ParamFun<P1::ProcFun>::isConfigurable<ParamFunction>::value);
|
||||
CHECK (false == _ParamFun<P1::ProcFun>::isConfigurable<LambdaSimple >::value);
|
||||
CHECK (false == _ParamFun<P1::ProcFun>::isConfigurable<LambdaCapture>::value);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue