Certain graphs design may require the instantiation of a port based on a template parameter. For example if you want to use the same templated graph with or without a port that will be connected to a cascade in or a cascade out kernel port, then you will need to use this conditional port feature.
The syntax to conditionally instantiate a port or a sub-graph is as
follows:
typename std::conditional<SEL,T1,T2>::type Object
Where,
-
T1
is aninput_port
,output_port
,kernel
, or user-defined graph. -
T2
is a dummy type asstd::tuple<>
orint
. -
Object
is the name of the object that will be instantiated.
The syntax to conditionally instantiate an array of ports or sub-graphs is as follows:
typename std::array<T3,N>::type Object
Where,
-
T3
is aninput_port
,output_port
,kernel
, or user-defined graph. -
N
is the number of items in the array. -
Object
is the name of the object that will be instantiated.
Here is an example of conditional port
instantiation:
#include "adf.h"
using namespace adf;
void k0(input_stream<int32> *, output_stream<int32> *);
void k0_cascin(input_stream<int32> *, output_stream<int32> *, input_stream<acc48>*);
void k0_cascout(input_stream<int32> *, output_stream<int32> *, output_stream<acc48>*);
void k0_cascin_cascout(input_stream<int32> *, output_stream<int32> *, input_stream<acc48>*, output_stream<acc48>*);
template<bool HAS_CASCADE_IN, bool HAS_CASCADE_OUT>
struct SubGraph: public graph
{
input_port strmIn;
input_port strmOut;
kernel k1;
typename std::conditional<HAS_CASCADE_IN, input_port, int>::type cascIn;
typename std::conditional<HAS_CASCADE_OUT, output_port, int>::type cascOut;
SubGraph()
{
if constexpr (HAS_CASCADE_IN && HAS_CASCADE_OUT)
{
k1 = kernel::create(k0_cascin_cascout);
}
else if constexpr (HAS_CASCADE_IN)
{
k1 = kernel::create(k0_cascin);
}
else if constexpr (HAS_CASCADE_OUT)
{
k1 = kernel::create(k0_cascout);
}
else
{
k1 = kernel::create(k0);
}
connect(strmIn, k1.in[0]);
connect(k1.out[0], strmOut);
if constexpr (HAS_CASCADE_IN)
{
connect(cascIn, k1.in[1]);
}
if constexpr (HAS_CASCADE_OUT)
{
connect(k1.out[1], cascOut);
}
source(k1) = "kernels.cc";
runtime<ratio>(k1) = 0.6;
}
};
In this example, the four functions have different ports. One of these functions
is instantiated in the graph depending on the template parameters. All functions have one
input stream and one output stream, however, the cascade input and output ports are optional.
The two template parameters are used in the std::conditional
to create, or not, the cascade ports at the sub-graph level. They are also used to specify
which function is instantiated in the sub-graph, and to connect the kernel ports to the right
sub-graph port.Here is another example where a complete sub-graph is instantiated and not
depending on some template
parameter:
#include "adf.h"
using namespace adf;
template<int ID>
void f0(input_stream<int32> *, output_stream<int32> *);
struct Sub0: public graph
{
input_port _in0;
input_port _out0;
kernel _k0;
Sub0()
{
_k0 = kernel::create(f0<0>);
connect(_in0, _k0.in[0]);
connect(_k0.out[0], _out0);
runtime<ratio>(_k0) = 0.9;
source(_k0) = "k0.cpp";
}
};
struct Sub1: public graph
{
input_port _in0;
input_port _out0;
kernel _k0;
Sub1()
{
_k0 = kernel::create(f0<1>);
connect(_in0, _k0.in[0]);
connect(_k0.out[0], _out0);
runtime<ratio>(_k0) = 0.8;
source(_k0) = "k0.cpp";
}
};
template<int ID>
struct MyGraph: public graph
{
input_plio _plioI;
output_plio _plioO;
constexpr static bool hasSub0() {return ID & 0x1;}
constexpr static bool hasSub1() {return ID & 0x2;}
typename std::conditional<hasSub0(), Sub0, int >::type _sub0;
typename std::conditional<hasSub1(), Sub1, int >::type _sub1;
MyGraph()
{
_plioI = input_plio::create("plio_I"+std::to_string(ID), plio_32_bits, "input"+std::to_string(ID)+".txt");
_plioO = output_plio::create("plio_O"+std::to_string(ID), plio_64_bits, "output"+std::to_string(ID)+".txt");
if constexpr (hasSub0() && hasSub1())
{
connect(_plioI.out[0], _sub0._in0);
connect(_sub0._out0, _sub1._in0);
connect(_sub1._out0, _plioO.in[0]);
}
else if constexpr (hasSub0())
{
connect(_plioI.out[0], _sub0._in0);
connect(_sub0._out0, _plioO.in[0]);
}
else if constexpr (hasSub1())
{
connect(_plioI.out[0], _sub1._in0);
connect(_sub1._out0, _plioO.in[0]);
}
}
};
Depending on the template parameter ID
, the
graph MyGraph
will contain either one or both of the
sub-graphs Sub0
and Sub1
. If
ID=0
, no sub-graph is instantiated, and the compiler will
error out. The two lines containing the std::conditional
are
used to conditionally instantiate the two sub-graphs. The connection of the kernels to the
I/Os is also driven by the template parameter ID
.