A template is a powerful tool in C++. By passing the data type as a parameter, you eliminate the need to rewrite code to support different data types. Templates are expanded at compile time, like macros. The difference is that the compiler performs type checking before template expansion. The source code contains template functions and class definitions, but the compiled code can contain multiple copies of same function or class. Type parameters, non-type parameters, default arguments, scalar parameters, and template parameters can be passed to a template, where the compiler instantiates the function or class accordingly.
- Support for general C++ template features.
- Supported data types (T) and connection types between kernels:
- Data type (T):
int8
,uint8
,int16
,uint16
,cint16
,int32
,uint32
,cint32
,int64
,uint64
,float
,cfloat
Important:acc48
andcacc48
data types are not supported in template stream connections. - Function parameter type:
input_window<T>
,output_window<T>
,input_stream<T>
,output_stream<T>
- Data type (T):
- The compiler does not support pre-compiled headers for template kernels.
Function Templates
Function template source code defines a generic function that can be used for different data types. Example function template:
// add.h
template<typename ELEMENT_TYPE, int FACTOR, size_t NUM_SAMPLES> void add(input_window<ELEMENT_TYPE>* in,
output_window<ELEMENT_TYPE>* out);
// add.cpp
template<typename ELEMENT_TYPE, int FACTOR, size_t NUM_SAMPLES> void add(input_window<ELEMENT_TYPE>* in,
output_window<ELEMENT_TYPE>* out)
{
for (int i=0; i<NUM_SAMPLES; i++)
{
ELEMENT_TYPE value = window_readincr(in);
value += FACTOR;
window_writeincr(out, value);
}
}
// graph.h
mygraph()
{
k[0] = kernel::create(add<int32, 6, 8>);
k[1] = kernel::create(add<int16, 3, 8>);
for (int i=0; i<NUM_KERNELS; i++)
{
runtime<ratio>(k[i]) = 0.3;
source(k[i]) = "src/add.cpp";
}
connect<window<32>>(in[0], k[0].in[0]);
connect<window<32>>(k[0].out[0], out[0]);
connect<window<16>>(in[1], k[1].in[0]);
connect<window<16>>(k[1].out[0], out[1]);
}
where:
-
add.h defines a template
add()
function. -
add.cpp defines the code for the
template
add()
function. -
graph.h uses the template
add()
function withinmygraph
class.
Class Templates
Like function templates, class templates are useful when a class defines an object that is independent of a specific data type. Example class template:
// fir.h
...
template<size_t NUM_COEFFS, typename ELEMENT_TYPE> class FIR
{
private:
ELEMENT_TYPE (&coeffs)[NUM_COEFFS];
ELEMENT_TYPE tapDelayLine[NUM_COEFFS];
uint32 numSamples;
public:
FIR(ELEMENT_TYPE(&coefficients)[NUM_COEFFS], uint32 samples);
void filter(input_window<ELEMENT_TYPE>* in, output_window<ELEMENT_TYPE>* out);
//user needs to write this function to register necessary info
static void registerKernelClass()
{
REGISTER_FUNCTION(FIR::filter);
REGISTER_PARAMETER(coeffs);
}
};
// fir.cpp
...
template<size_t NUM_COEFFS, typename ELEMENT_TYPE> FIR<NUM_COEFFS, ELEMENT_TYPE>::FIR(ELEMENT_TYPE(&coefficients)[NUM_COEFFS], uint32 samples):coeffs(coefficients)
{
...
}
template<size_t NUM_COEFFS, typename ELEMENT_TYPE> void FIR<NUM_COEFFS, ELEMENT_TYPE>::filter(input_window<ELEMENT_TYPE>* in, output_window<ELEMENT_TYPE>* out)
{
...
}
// graph.h
...
mygraph()
{
k1 = kernel::create_object<FIR<12, int32>>(std::vector<int>({ 180, 89, -80, -391, -720, -834, -478, 505, 2063, 3896, 5535, 6504 }), 8);
runtime<ratio>(k1) = 0.1;
source(k1) = "src/fir.cpp";
headers(k1) = { "src/fir.h" };
k2 = kernel::create_object<FIR<15, int32>>(std::vector<int>({ -21, -249, 319, -78, -511, 977, -610, -844, 2574, -2754, -1066, 18539, 0, 0, 0 }), 8);
runtime<ratio>(k2) = 0.1;
source(k2) = "src/fir.cpp";
headers(k2) = { "src/fir.h" };
...
}
where:
-
fir.h defines a class
template where class
FIR
is declared. -
fir.cpp contains class
FIR
implementation and the classFIR
member functionfilter
implementation. -
graph.h demonstrates the
template class
FIR
instantiation within themygraph
class.