Invocation: pass the actual processing function
...need to pass a binding for the actual processing function in a way that it acts as a ''prototype'' — since the `Feed`, i.e. the ''Invocation Adapter'' must be generated for each invocation anew within the current stack frame (so to avoid spurious heap allocations)
This commit is contained in:
parent
9490192ef8
commit
a02873a015
4 changed files with 193 additions and 156 deletions
|
|
@ -307,7 +307,7 @@ namespace engine {
|
|||
}
|
||||
|
||||
|
||||
/****************************************************//**
|
||||
/*************************************************************//**
|
||||
* Terminal: complete the Port wiring and return to the node level.
|
||||
*/
|
||||
NodeBuilder<POL>
|
||||
|
|
|
|||
|
|
@ -274,159 +274,19 @@ namespace engine {
|
|||
}
|
||||
|
||||
|
||||
struct WeavingPatternBase ///////OOO seems to be obsolete...??
|
||||
//////////////////////////////OOO non-copyable? move-only??
|
||||
{
|
||||
using Feed = FeedManifold<0>;
|
||||
|
||||
Feed mount() { return Feed{}; }
|
||||
void pull (Feed&, TurnoutSystem&) { /* NOP */ }
|
||||
void shed (Feed&) { /* NOP */ }
|
||||
void weft (Feed&) { /* NOP */ }
|
||||
void fix (Feed&) { /* NOP */ }
|
||||
};
|
||||
|
||||
namespace {// Introspection helpers....
|
||||
|
||||
using lib::meta::_Fun;
|
||||
using lib::meta::is_BinaryFun;
|
||||
using std::remove_reference_t;
|
||||
|
||||
/** Helper to pick up the parameter dimensions from the processing function
|
||||
* @remark this is the rather simple yet common case that media processing
|
||||
* is done by a function, which takes an array of input and output
|
||||
* buffer pointers with a common type; this simple case is used
|
||||
* 7/2024 for prototyping and validate the design.
|
||||
* @tparam FUN a _function-like_ object, expected to accept two arguments,
|
||||
* which both are arrays of buffer pointers (input, output).
|
||||
*/
|
||||
template<class FUN>
|
||||
struct _ProcFun
|
||||
{
|
||||
static_assert(_Fun<FUN>() , "something funktion-like required");
|
||||
static_assert(is_BinaryFun<FUN>() , "function with two arguments expected");
|
||||
|
||||
using ArgI = remove_reference_t<typename _Fun<FUN>::Args::List::Head>;
|
||||
using ArgO = remove_reference_t<typename _Fun<FUN>::Args::List::Tail::Head>;
|
||||
|
||||
template<class ARG>
|
||||
struct MatchBuffArray
|
||||
{
|
||||
static_assert(not sizeof(ARG), "processing function expected to take array-of-buffer-pointers");
|
||||
};
|
||||
template<class BUF, size_t N>
|
||||
struct MatchBuffArray<std::array<BUF*,N>>
|
||||
{
|
||||
using Buff = BUF;
|
||||
enum{ SIZ = N };
|
||||
};
|
||||
|
||||
using BuffI = typename MatchBuffArray<ArgI>::Buff;
|
||||
using BuffO = typename MatchBuffArray<ArgO>::Buff;
|
||||
|
||||
enum{ FAN_I = MatchBuffArray<ArgI>::SIZ
|
||||
, FAN_O = MatchBuffArray<ArgO>::SIZ
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Pick a suitable size for the FeedManifold to accommodate the given function.
|
||||
* @remark only returning one of a small selection of sizes, to avoid
|
||||
* excessive generation of template instances.
|
||||
* @todo 10/24 this is a premature safety guard;
|
||||
* need to assess if there is actually a problem
|
||||
* (chances are that the optimiser absorbs most of the combinatoric complexity,
|
||||
* or that, to the contrary, other proliferation mechanisms cause more harm)
|
||||
* Standard implementation for a _Weaving Pattern_ to connect
|
||||
* the input and output data feeds (buffers) into a processing function.
|
||||
* @tparam CONF a configuration / policy base class
|
||||
* @note assumptions made regarding the overall structure
|
||||
* - CONF::Feed defines an _invocation adapter_ for the processing function
|
||||
* - CONF::buildFeed is a functor to (repeatedly) build `Feed` instances
|
||||
* - this adapter in turn embeds a `FeedManifold<N>` to hold
|
||||
* + an array of input buffer pointers
|
||||
* + an array of output buffer pointers
|
||||
* + `CONF::MAX_SIZ` limits both arrays
|
||||
*/
|
||||
template<class FUN>
|
||||
inline constexpr uint
|
||||
manifoldSiz()
|
||||
{
|
||||
using _F = _ProcFun<FUN>;
|
||||
auto bound = std::max (_F::FAN_I, _F::FAN_O);
|
||||
static_assert (bound <= 10,
|
||||
"Limitation of template instances exceeded");
|
||||
return bound < 3? bound
|
||||
: bound < 6? 5
|
||||
: 10;
|
||||
}
|
||||
}//(End)Introspection helpers.
|
||||
|
||||
|
||||
/**
|
||||
* Adapter to handle a simple yet common setup for media processing
|
||||
* - somehow we can invoke processing as a simple function
|
||||
* - this function takes two arrays: the input- and output buffers
|
||||
* @remark this setup is useful for testing, and as documentation example;
|
||||
* actually the FeedManifold is mixed in as baseclass, and the
|
||||
* buffer pointers are retrieved from the BuffHandles.
|
||||
* @tparam MAN a FeedManifold, providing arrays of BuffHandles
|
||||
* @tparam FUN the processing function
|
||||
*/
|
||||
template<class MAN, class FUN>
|
||||
struct SimpleFunctionInvocationAdapter
|
||||
: MAN
|
||||
{
|
||||
using BuffI = typename _ProcFun<FUN>::BuffI;
|
||||
using BuffO = typename _ProcFun<FUN>::BuffO;
|
||||
|
||||
enum{ N = MAN::inBuff::size()
|
||||
, FAN_I = _ProcFun<FUN>::FAN_I
|
||||
, FAN_O = _ProcFun<FUN>::FAN_O
|
||||
};
|
||||
|
||||
static_assert(FAN_I <= N and FAN_O <= N);
|
||||
|
||||
using ArrayI = std::array<BuffI*, FAN_I>;
|
||||
using ArrayO = std::array<BuffO*, FAN_O>;
|
||||
|
||||
|
||||
FUN process;
|
||||
|
||||
ArrayI inParam;
|
||||
ArrayO outParam;
|
||||
|
||||
template<typename...INIT>
|
||||
SimpleFunctionInvocationAdapter (INIT&& ...funSetup)
|
||||
: FUN{forward<INIT> (funSetup)...}
|
||||
{ }
|
||||
|
||||
|
||||
void
|
||||
connect (uint fanIn, uint fanOut)
|
||||
{
|
||||
REQUIRE (fanIn >= FAN_I and fanOut >= FAN_O);
|
||||
for (uint i=0; i<FAN_I; ++i)
|
||||
inParam[i] = & MAN::inBuff[i].template accessAs<BuffI>();
|
||||
for (uint i=0; i<FAN_O; ++i)
|
||||
outParam[i] = & MAN::outBuff[i].template accessAs<BuffO>();
|
||||
}
|
||||
|
||||
void
|
||||
invoke()
|
||||
{
|
||||
process (inParam, outParam);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Example base configuration for a Weaving-Pattern chain:
|
||||
* - use a simple processing function
|
||||
* - pass an input/output buffer array to this function
|
||||
* - map all »slots« directly without any re-ordering
|
||||
* - use a sufficiently sized FeedManifold as storage scheme
|
||||
*/
|
||||
template<uint N, class FUN>
|
||||
struct Conf_DirectFunctionInvocation
|
||||
: util::MoveOnly
|
||||
{
|
||||
using Manifold = FeedManifold<N>;
|
||||
using Feed = SimpleFunctionInvocationAdapter<Manifold, FUN>;
|
||||
enum{ MAX_SIZ = N };
|
||||
};
|
||||
|
||||
|
||||
template<class CONF>
|
||||
struct SimpleWeavingPattern
|
||||
: CONF
|
||||
|
|
@ -441,8 +301,9 @@ namespace engine {
|
|||
uint resultSlot{0};
|
||||
|
||||
//////////////////////////////////////////OOO builder must set-up those descriptors
|
||||
SimpleWeavingPattern(Several<PortRef>&& pr, Several<BuffDescr> dr)
|
||||
: CONF{}
|
||||
template<typename...ARGS>
|
||||
SimpleWeavingPattern(Several<PortRef>&& pr, Several<BuffDescr> dr, ARGS&& ...args)
|
||||
: CONF{forward<ARGS>(args)...}
|
||||
, leadPort{move(pr)}
|
||||
, outTypes{move(dr)}
|
||||
{ }
|
||||
|
|
@ -451,7 +312,7 @@ namespace engine {
|
|||
Feed
|
||||
mount()
|
||||
{
|
||||
return Feed{}; //////////////////////OOO cant work this way ... need to pass-in a processing functor for the ctor
|
||||
return CONF::buildFeed();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -61,6 +61,152 @@ namespace engine {
|
|||
using std::forward;
|
||||
using lib::Several;
|
||||
|
||||
|
||||
namespace {// Introspection helpers....
|
||||
|
||||
using lib::meta::_Fun;
|
||||
using lib::meta::is_BinaryFun;
|
||||
using std::remove_reference_t;
|
||||
|
||||
/** Helper to pick up the parameter dimensions from the processing function
|
||||
* @remark this is the rather simple yet common case that media processing
|
||||
* is done by a function, which takes an array of input and output
|
||||
* buffer pointers with a common type; this simple case is used
|
||||
* 7/2024 for prototyping and validate the design.
|
||||
* @tparam FUN a _function-like_ object, expected to accept two arguments,
|
||||
* which both are arrays of buffer pointers (input, output).
|
||||
*/
|
||||
template<class FUN>
|
||||
struct _ProcFun
|
||||
{
|
||||
static_assert(_Fun<FUN>() , "something funktion-like required");
|
||||
static_assert(is_BinaryFun<FUN>() , "function with two arguments expected");
|
||||
|
||||
using ArgI = remove_reference_t<typename _Fun<FUN>::Args::List::Head>;
|
||||
using ArgO = remove_reference_t<typename _Fun<FUN>::Args::List::Tail::Head>;
|
||||
|
||||
template<class ARG>
|
||||
struct MatchBuffArray
|
||||
{
|
||||
static_assert(not sizeof(ARG), "processing function expected to take array-of-buffer-pointers");
|
||||
};
|
||||
template<class BUF, size_t N>
|
||||
struct MatchBuffArray<std::array<BUF*,N>>
|
||||
{
|
||||
using Buff = BUF;
|
||||
enum{ SIZ = N };
|
||||
};
|
||||
|
||||
using BuffI = typename MatchBuffArray<ArgI>::Buff;
|
||||
using BuffO = typename MatchBuffArray<ArgO>::Buff;
|
||||
|
||||
enum{ FAN_I = MatchBuffArray<ArgI>::SIZ
|
||||
, FAN_O = MatchBuffArray<ArgO>::SIZ
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Pick a suitable size for the FeedManifold to accommodate the given function.
|
||||
* @remark only returning one of a small selection of sizes, to avoid
|
||||
* excessive generation of template instances.
|
||||
* @todo 10/24 this is a premature safety guard;
|
||||
* need to assess if there is actually a problem
|
||||
* (chances are that the optimiser absorbs most of the combinatoric complexity,
|
||||
* or that, to the contrary, other proliferation mechanisms cause more harm)
|
||||
*/
|
||||
template<class FUN>
|
||||
inline constexpr uint
|
||||
manifoldSiz()
|
||||
{
|
||||
using _F = _ProcFun<FUN>;
|
||||
auto bound = std::max (_F::FAN_I, _F::FAN_O);
|
||||
static_assert (bound <= 10,
|
||||
"Limitation of template instances exceeded");
|
||||
return bound < 3? bound
|
||||
: bound < 6? 5
|
||||
: 10;
|
||||
}
|
||||
}//(End)Introspection helpers.
|
||||
|
||||
|
||||
/**
|
||||
* Adapter to handle a simple yet common setup for media processing
|
||||
* - somehow we can invoke processing as a simple function
|
||||
* - this function takes two arrays: the input- and output buffers
|
||||
* @remark this setup is useful for testing, and as documentation example;
|
||||
* actually the FeedManifold is mixed in as baseclass, and the
|
||||
* buffer pointers are retrieved from the BuffHandles.
|
||||
* @tparam MAN a FeedManifold, providing arrays of BuffHandles
|
||||
* @tparam FUN the processing function
|
||||
*/
|
||||
template<class MAN, class FUN>
|
||||
struct SimpleFunctionInvocationAdapter
|
||||
: MAN
|
||||
{
|
||||
using BuffI = typename _ProcFun<FUN>::BuffI;
|
||||
using BuffO = typename _ProcFun<FUN>::BuffO;
|
||||
|
||||
enum{ N = MAN::inBuff::size()
|
||||
, FAN_I = _ProcFun<FUN>::FAN_I
|
||||
, FAN_O = _ProcFun<FUN>::FAN_O
|
||||
};
|
||||
|
||||
static_assert(FAN_I <= N and FAN_O <= N);
|
||||
|
||||
using ArrayI = std::array<BuffI*, FAN_I>;
|
||||
using ArrayO = std::array<BuffO*, FAN_O>;
|
||||
|
||||
|
||||
FUN process;
|
||||
|
||||
ArrayI inParam;
|
||||
ArrayO outParam;
|
||||
|
||||
template<typename...INIT>
|
||||
SimpleFunctionInvocationAdapter (INIT&& ...funSetup)
|
||||
: FUN{forward<INIT> (funSetup)...}
|
||||
{ }
|
||||
|
||||
|
||||
void
|
||||
connect (uint fanIn, uint fanOut)
|
||||
{
|
||||
REQUIRE (fanIn >= FAN_I and fanOut >= FAN_O);
|
||||
for (uint i=0; i<FAN_I; ++i)
|
||||
inParam[i] = & MAN::inBuff[i].template accessAs<BuffI>();
|
||||
for (uint i=0; i<FAN_O; ++i)
|
||||
outParam[i] = & MAN::outBuff[i].template accessAs<BuffO>();
|
||||
}
|
||||
|
||||
void
|
||||
invoke()
|
||||
{
|
||||
process (inParam, outParam);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Example base configuration for a Weaving-Pattern chain:
|
||||
* - use a simple processing function
|
||||
* - pass an input/output buffer array to this function
|
||||
* - map all »slots« directly without any re-ordering
|
||||
* - use a sufficiently sized FeedManifold as storage scheme
|
||||
*/
|
||||
template<uint N, class FUN>
|
||||
struct Conf_DirectFunctionInvocation
|
||||
: util::MoveOnly
|
||||
{
|
||||
using Manifold = FeedManifold<N>;
|
||||
using Feed = SimpleFunctionInvocationAdapter<Manifold, FUN>;
|
||||
enum{ MAX_SIZ = N };
|
||||
|
||||
std::function<Feed()> buildFeed;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template<class POL, class I, class E=I>
|
||||
using DataBuilder = lib::SeveralBuilder<I,E, POL::template Policy>;
|
||||
|
||||
|
|
@ -92,6 +238,12 @@ namespace engine {
|
|||
};
|
||||
ServiceCtx ctx; //////////////////////////////////////////OOO need to wire that top-down through all builders!
|
||||
|
||||
FUN fun_;
|
||||
|
||||
WeavingBuilder(FUN&& init)
|
||||
: fun_{move(init)}
|
||||
{ }
|
||||
|
||||
WeavingBuilder
|
||||
attachToLeadPort(ProcNode& lead, uint portNr)
|
||||
{
|
||||
|
|
@ -134,7 +286,7 @@ namespace engine {
|
|||
|
||||
using Product = Turnout<SimpleDirectInvoke<N,FUN>>;
|
||||
///////////////////////////////OOO need a way to prepare SeveralBuilder-instances for leadPort and outDescr --> see NodeBuilder
|
||||
return Product{leadPort.build(), outTypes.build()};
|
||||
return Product{leadPort.build(), outTypes.build(), move(fun_)};
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -89326,7 +89326,31 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1728517256203" ID="ID_480014054" MODIFIED="1728517267790" TEXT="Verdrahtung auf die eigentliche Processing-Function"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1728517256203" ID="ID_480014054" MODIFIED="1728611066435" TEXT="Verdrahtung auf die eigentliche Processing-Function">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1728611071455" ID="ID_1880473287" MODIFIED="1728611098134" TEXT="hier muß i.A. tatsächlich ein Init-Argument durchgereicht werden">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1728611119523" ID="ID_1204170210" MODIFIED="1728611253369" TEXT="spezieller twist: wir brauchen das in jedem mount()-Aufruf erneut">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1728611175507" ID="ID_689801971" MODIFIED="1728613964611" TEXT="Konsequenz ⟹ muß im Turnout als Wert gespeichert sein">
|
||||
<arrowlink COLOR="#3f5766" DESTINATION="ID_1904438142" ENDARROW="Default" ENDINCLINATION="-504;0;" ID="Arrow_ID_341886103" STARTARROW="None" STARTINCLINATION="96;5;"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1728611321023" ID="ID_405951729" MODIFIED="1728611366416" TEXT="�� das paßt nicht gut mit der Idee mit dem CONF-Basisparameter zusammen">
|
||||
<node CREATED="1728611387739" ID="ID_1318656545" MODIFIED="1728613923465" TEXT="denn dieser geht bereits in den WeavingBuilder"/>
|
||||
<node CREATED="1728612653405" ID="ID_944062020" MODIFIED="1728612669318" TEXT="Init-Wert muß dann durch den Turnout durchgereicht werden"/>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1728612939774" ID="ID_718599233" MODIFIED="1728612975781" TEXT="läuft auf die Frage hinaus: wo liegt der InvocationAdapter?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1728613727051" ID="ID_1724797637" MODIFIED="1728613734871" TEXT="er selber liegt auf dem Stack"/>
|
||||
<node CREATED="1728613735866" ID="ID_357104726" MODIFIED="1728613755285" TEXT="dort wird er von der WeavingPattern::mount()-Operation erzeugt"/>
|
||||
<node CREATED="1728613756312" ID="ID_1145093586" MODIFIED="1728613768699" TEXT="aber ein Prototyp muß im Turnout selber liegen"/>
|
||||
<node CREATED="1728613769979" ID="ID_1904438142" MODIFIED="1728613948652" TEXT="⟹ wegen der Flexibilität in die CONF-Policy">
|
||||
<linktarget COLOR="#3f5766" DESTINATION="ID_1904438142" ENDARROW="Default" ENDINCLINATION="-504;0;" ID="Arrow_ID_341886103" SOURCE="ID_689801971" STARTARROW="None" STARTINCLINATION="96;5;"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1728517306077" ID="ID_781615859" MODIFIED="1728517400020" TEXT="Verdrahtung Service-Context / DI für runtime-services">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue