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:
Fischlurch 2024-10-11 03:33:05 +02:00
parent 9490192ef8
commit a02873a015
4 changed files with 193 additions and 156 deletions

View file

@ -307,7 +307,7 @@ namespace engine {
}
/****************************************************//**
/*************************************************************//**
* Terminal: complete the Port wiring and return to the node level.
*/
NodeBuilder<POL>

View file

@ -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

View file

@ -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:

View file

@ -89326,7 +89326,31 @@ Date:&#160;&#160;&#160;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&#xdf; i.A. tats&#xe4;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 &#x27f9; mu&#xdf; 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="&#xd83d;&#xddf2; das pa&#xdf;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&#xdf; dann durch den Turnout durchgereicht werden"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1728612939774" ID="ID_718599233" MODIFIED="1728612975781" TEXT="l&#xe4;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&#xdf; im Turnout selber liegen"/>
<node CREATED="1728613769979" ID="ID_1904438142" MODIFIED="1728613948652" TEXT="&#x27f9; wegen der Flexibilit&#xe4;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&#xfc;r runtime-services">
<richcontent TYPE="NOTE"><html>
<head/>