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/error.hpp"
|
||||||
#include "lib/nocopy.hpp"
|
#include "lib/nocopy.hpp"
|
||||||
//#include "steam/engine/proc-node.hpp"
|
|
||||||
#include "steam/engine/buffhandle.hpp"
|
#include "steam/engine/buffhandle.hpp"
|
||||||
#include "lib/uninitialised-storage.hpp"
|
#include "lib/uninitialised-storage.hpp"
|
||||||
#include "lib/meta/function.hpp"
|
#include "lib/meta/function.hpp"
|
||||||
|
|
@ -91,8 +90,6 @@
|
||||||
#include "lib/test/test-helper.hpp"
|
#include "lib/test/test-helper.hpp"
|
||||||
//#include "lib/several.hpp"
|
//#include "lib/several.hpp"
|
||||||
|
|
||||||
//#include <utility>
|
|
||||||
//#include <array>
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -101,14 +98,10 @@
|
||||||
namespace steam {
|
namespace steam {
|
||||||
namespace engine {
|
namespace engine {
|
||||||
|
|
||||||
// using std::pair;
|
|
||||||
// using std::vector;
|
|
||||||
|
|
||||||
namespace {// Introspection helpers....
|
namespace {// Introspection helpers....
|
||||||
|
|
||||||
using lib::meta::_Fun;
|
using lib::meta::_Fun;
|
||||||
using lib::meta::enable_if;
|
using lib::meta::enable_if;
|
||||||
using lib::meta::disable_if_self;
|
|
||||||
using lib::meta::is_UnaryFun;
|
using lib::meta::is_UnaryFun;
|
||||||
using lib::meta::is_BinaryFun;
|
using lib::meta::is_BinaryFun;
|
||||||
using lib::meta::is_TernaryFun;
|
using lib::meta::is_TernaryFun;
|
||||||
|
|
@ -117,14 +110,14 @@ namespace engine {
|
||||||
using lib::meta::ElmTypes;
|
using lib::meta::ElmTypes;
|
||||||
using lib::meta::NullType;
|
using lib::meta::NullType;
|
||||||
using lib::meta::Tagged;
|
using lib::meta::Tagged;
|
||||||
using lib::meta::TySeq;
|
|
||||||
using std::declval;
|
|
||||||
using std::is_pointer;
|
using std::is_pointer;
|
||||||
using std::is_reference;
|
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::remove_pointer_t;
|
||||||
using std::tuple_element_t;
|
using std::tuple_element_t;
|
||||||
using std::void_t;
|
using std::add_pointer_t;
|
||||||
using std::__and_;
|
using std::__and_;
|
||||||
using std::__not_;
|
using std::__not_;
|
||||||
|
|
||||||
|
|
@ -267,13 +260,17 @@ namespace engine {
|
||||||
|
|
||||||
template<class PF>
|
template<class PF>
|
||||||
using Res = typename _Fun<PF>::Ret;
|
using Res = typename _Fun<PF>::Ret;
|
||||||
|
template<class PF>
|
||||||
|
using SigP = add_pointer_t<typename _Fun<PF>::Sig>;
|
||||||
|
|
||||||
template<class PF>
|
template<class PF>
|
||||||
using isSuitable = std::is_constructible<Param, Res<PF>>;
|
using isSuitable = is_constructible<Param, Res<PF>>;
|
||||||
|
|
||||||
template<class 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>
|
template<class PF>
|
||||||
static bool
|
static bool
|
||||||
isActivated (PF const& paramFun)
|
isActivated (PF const& paramFun)
|
||||||
|
|
@ -547,6 +544,18 @@ namespace engine {
|
||||||
* for each Node invocation.
|
* for each Node invocation.
|
||||||
* @tparam FUN type of the data processing-functor
|
* @tparam FUN type of the data processing-functor
|
||||||
* @tparam PAM type of an optional parameter-setup functor (defaults to deactivated)
|
* @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>
|
template<class FUN, class PAM>
|
||||||
class FeedPrototype
|
class FeedPrototype
|
||||||
|
|
@ -574,21 +583,32 @@ namespace engine {
|
||||||
static constexpr bool hasParamFun() { return _Trait::template isParamFun<PAM>(); }
|
static constexpr bool hasParamFun() { return _Trait::template isParamFun<PAM>(); }
|
||||||
static constexpr bool canActivate() { return _Trait::template canActivate<PAM>(); }
|
static constexpr bool canActivate() { return _Trait::template canActivate<PAM>(); }
|
||||||
|
|
||||||
/**
|
/** @return runtime test: there is actually usable parameter-functor to invoke? */
|
||||||
* build suitable Feed(Manifold) for processing Node invocation
|
bool isActivated() const { return _Trait::isActivated(paramFun_); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************//**
|
||||||
|
* build suitable Feed(Manifold) for processing a Node invocation
|
||||||
*/
|
*/
|
||||||
Feed
|
Feed
|
||||||
createFeed (TurnoutSystem& turnoutSys)
|
createFeed (TurnoutSystem& turnoutSys)
|
||||||
{
|
{
|
||||||
if constexpr (hasParamFun())
|
if constexpr (hasParamFun())
|
||||||
if (_Trait::isActivated(paramFun_))
|
if (isActivated())
|
||||||
return Feed(paramFun_(turnoutSys), procFun_);
|
return Feed(paramFun_(turnoutSys), procFun_);
|
||||||
return Feed{procFun_};
|
return Feed{procFun_};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ======= cross-builder API ======= */
|
||||||
|
|
||||||
|
using ProcFun = FUN;
|
||||||
|
using ParamFun = PAM;
|
||||||
|
|
||||||
template<typename PFX>
|
template<typename PFX>
|
||||||
using Adapted = FeedPrototype<FUN, PFX>;
|
using Adapted = FeedPrototype<FUN,PFX>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cross-Builder to add configuration with a given parameter-functor.
|
* Cross-Builder to add configuration with a given parameter-functor.
|
||||||
|
|
@ -601,11 +621,37 @@ namespace engine {
|
||||||
*/
|
*/
|
||||||
template<typename PFX>
|
template<typename PFX>
|
||||||
auto
|
auto
|
||||||
moveAdapted (PFX otherParamFun)
|
moveAdapted (PFX otherParamFun =PFX{})
|
||||||
{
|
{
|
||||||
using OtherParamFun = std::decay_t<PFX>;
|
using OtherParamFun = std::decay_t<PFX>;
|
||||||
return Adapted<OtherParamFun>{move(procFun_), move(otherParamFun)};
|
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();
|
BufferProvider& provider = DiagnosticBufferProvider::build();
|
||||||
BuffHandle buff = provider.lockBufferFor<Buffer> (-55);
|
BuffHandle buff = provider.lockBufferFor<Buffer> (-55);
|
||||||
|
|
||||||
|
|
||||||
|
//_______________________________________
|
||||||
|
// Case-1: Prototype without param-functor
|
||||||
auto fun_singleParamOut = [](short param, Buffer* buff) { *buff = param-1; };
|
auto fun_singleParamOut = [](short param, Buffer* buff) { *buff = param-1; };
|
||||||
using M1 = FeedManifold<decltype(fun_singleParamOut)>;
|
using M1 = FeedManifold<decltype(fun_singleParamOut)>;
|
||||||
using P1 = M1::Prototype;
|
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
|
long rr{11}; // ▽▽▽▽ Note: side-effect
|
||||||
auto fun_paramSimple = [&](TurnoutSystem&){ return rr += 1+rani(100); };
|
auto fun_paramSimple = [&](TurnoutSystem&){ return rr += 1+rani(100); };
|
||||||
using P1x = P1::Adapted<decltype(fun_paramSimple)>;
|
using P1x = P1::Adapted<decltype(fun_paramSimple)>;
|
||||||
|
|
@ -395,6 +398,74 @@ namespace test {
|
||||||
CHECK (calcResult == r1 - 1); // FeedManifold on the stack, since invocations are
|
CHECK (calcResult == r1 - 1); // FeedManifold on the stack, since invocations are
|
||||||
m1.invoke(); // performed concurrently, each with its own set of
|
m1.invoke(); // performed concurrently, each with its own set of
|
||||||
CHECK (calcResult == 0 - 1); // buffers and parameters.
|
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