shape
of an expression by returning the ltl::Shape objects on one of the ltl::MArray operatnds. The anatomy of expression templates:
Expressions like the right-hand side of
A = A + B*C;
A
, B
, and C
being of type ltl::MArray<float,1>
) are represented by nested templated data types that capture the parse tree of the expression.In this example, the expression A + B*C is represented by the type (simplified for readability)
ltl::ExprBinopNode<ltl::MArrayIterConst<float, 1>, ltl::ExprNode<ltl::ExprBinopNode<ltl::MArrayIterConst<float, 1>, ltl::MArrayIterConst<float, 1>, ltl::__ltl_TMul<float, float>, 1>, 1>, ltl::__ltl_TAdd<float, float>, 1>
ExprNode<>
, which forwards all calls to the parse tree node it wraps.
The class ExprNode<>
defines an iterator-like interface that all parse tree nodes (and ultimately the iterators that parse tree nodes hold) implement. This way, a whole expression, and every sub-expression presents an iterator interface to the outside world.
For this to function we define
operator+
and sin()
. This function/operator takes types representing nodes in the parse tree as arguments and return a type representing their own node in the parse tree. For example opertator+
for two expressions: template<class A, class B, int N> inline ExprNode<ExprBinopNode<ExprNode<A,N>, ExprNode<B,N>, __ltl_TAdd<typename A::value_type, typename B::value_type>, N >, N > operator+( const ExprNode<A,N>& a, const ExprNode<B,N>& b ) { typedef ExprBinopNode<ExprNode<A,N>, ExprNode<B,N>, __ltl_TAdd<typename A::value_type, typename B::value_type>, N > ExprT; return ExprNode<ExprT,N>( ExprT(a,b) ); }
eval()
. In the above example these are ltl::__ltl_TMul
and ltl::__ltl_TAdd
. ltl::__ltl_TAdd
looks like this: template<class T1, class T2> struct __ltl_TAdd : public _et_applic_base { typedef typename promotion_trait<T1,T2>::PType value_type; static inline value_type eval( const T1& a, const T2& b ) { return a + b; } }
template<typename E> ltl::MArray<float,1>::operator=( ExprNode<E,1> ).
#define BINOP_AA | ( | operator, | |||
op | ) |
Define the global binary functions/operators for ltl::MArray expressions, version for 2 MArray operands, overloaded versions below.
Each binary function/operator takes MArrays, expressions, or literals as arguments and returns a parse tree node for the operation it represents:
ExprNode <ExprBinopNode <A, B, Operation, NDim> > function( A& RHS, B& LHS )
LHS
and RHS
are of type ltl::MArray
, ltl::ExprNode
, or scalar literals.There are 8 combination of argument types for binary ops, namely:
array op array,
expr op array,
array op expr,
expr op expr,
array op literal,
expr op literal,
literal op array, and
literal op expr.
An overloaded template function/operator is generated by these macros for each of these cases.
#define BINOP_EA | ( | operator, | |||
op | ) |
Expression MArray.
#define BINOP_AE | ( | operator, | |||
op | ) |
MArray - Expression.
#define BINOP_EE | ( | operator, | |||
op | ) |
Expression - Expression.
#define BINOP_AL | ( | operator, | |||
op, | |||||
lit_type | ) |
MArray - Literal.
#define BINOP_EL | ( | operator, | |||
op, | |||||
lit_type | ) |
Expression - Literal.
#define BINOP_LA | ( | operator, | |||
op, | |||||
lit_type | ) |
Literal - MArray.
#define BINOP_LE | ( | operator, | |||
op, | |||||
lit_type | ) |
Literal - Expression.
#define UNOP_A | ( | operator, | |||
op | ) |
Define the global unary operators, overloaded versions for marray operand.
Each binary function/operator takes MArrays, expressions, or literals as arguments and returns a parse tree node for the operation it represents:
ExprNode <ExprUnopNode <A, Operation, NDim> > function( A& operand )
operand
is of type ltl::MArray
, ltl::ExprNode
.There are 2 combination of argument types for unary operations, namely:
op array, and
op expr.
An overloaded template function/operator is generated by these macros for each of these cases.
#define UNOP_E | ( | operator, | |||
op | ) |
Define the global unary operators, overloaded versions for marray operand.
Overloaded version for expression operands.
#define DECLARE_BINOP | ( | operation, | |||
opname | ) |
Make a binary (built-in) operator available to expression templates.
This macro declares all necessary overloaded operators to build the parse tree for a given binary operator operation
. The return type is the standard C type-promoted result of the operation on built in scalar types.
It is assumed that the name of the applicative template for the same operator is called ltl::__ltl_opname
and that this template is defined elsewhere (misc/applicopsh for built-in operators).
#define DECLARE_BINOP_RET | ( | operation, | |||
ret_type, | |||||
opname | ) |
Make a binary (built-in) operator available to expression templates.
This macro declares all necessary overloaded operators to build the parse tree for a given binary operator operation
. The return type may be different than the operand type, e.g. as necessary for bool-valued operators.
It is assumed that the name of the applicative template for the same operator is called ltl::__ltl_opname
and that this template is defined elsewhere (misc/applicopsh for built-in operators).
#define DECLARE_UNOP | ( | operation, | |||
opname | ) |
Make a unary (built-in) operator available to expression templates.
This macro declares all necessary overloaded operators to build the parse tree for a given unary operator operation
. The return type is the standard C type-promoted result of the operation on built in scalar types.
It is assumed that the name of the applicative template for the same operator is called ltl::__ltl_opname
and that this template is defined elsewhere (misc/applicopsh for built-in operators).
#define DECLARE_BINARY_FUNC_ | ( | function, | |||
ret_type | ) |
Make any binary function available to expression templates.
This macro declares all necessary overloaded operators to build the parse tree for a given binary function function
. The return type may be different than the operand type.
It is assumed that the name of the applicative template for the same function is called ltl::__ltl_function
and that this template is defined elsewhere. (misc/applicopsh for standard functions).
The function itself has to be implemented with the signature
template <typename T> ret_type function( const T& a, const T& b );
#define DECLARE_UNARY_FUNC_ | ( | function, | |||
ret_type | ) |
Make any unary function available to expression templates.
This macro declares all necessary overloaded operators to build the parse tree for a given unary function function
. The return type may be different than the operand type.
It is assumed that the name of the applicative template for the same function is called ltl::__ltl_function
and that this template is defined elsewhere (misc/applicopsh for standard functions).
The function itself has to be implemented with the signature
template <typename T> ret_type function( const T& a );
#define DECLARE_BINARY_FUNC | ( | function, | |||
ret_type | ) |
Value:
MAKE_BINAP_FUNC( __ltl_##function, ret_type, function ); \ DECLARE_BINARY_FUNC_(function, ret_type)
This macro declares all necessary overloaded operators to build the parse tree for a given binary function function
. The return type may be different than the operand type.
This macro also declares and defines the applicative templates for this function. It is the only macro that needs to be called by users to make user-defined functions available to expression templates.
Assume you have a function like this:
template <typename T> ret_type function( const T& a, const T& b );
DECLARE_BINARY_FUNC(function, ret_type);
#define DECLARE_UNARY_FUNC | ( | function, | |||
ret_type | ) |
Value:
MAKE_UNAP_FUNC( __ltl_##function, ret_type, function ); \ DECLARE_UNARY_FUNC_(function, ret_type)
This macro declares all necessary overloaded operators to build the parse tree for a given unary function function
. The return type may be different than the operand type.
This macro also declares and defines the applicative templates for this function. It is the only macro that needs to be called by users to make user-defined functions available to expression templates.
Assume you have a function like this:
template <typename T> ret_type function( const T& a );
DECLARE_BINARY_FUNC(function, ret_type);
const Shape<A::dims>* ltl::_expr_getshape | ( | const A & | a, | |
const B & | b | |||
) | [inline] |
Determine the shape
of an expression by returning the ltl::Shape objects on one of the ltl::MArray operatnds.
Determine the shape
of an expression
When determining the shape
of an expression it is sufficient to return any one shape
object from any one of the MArrays in the expression since we know they will all be the conformable. However, we must make sure that we do not ask e.g. a literal constant for a shape ...
In the general case, just use the LHS Shape ... We will provide partial specializations for the cases where one of the operands does not have a shape and return the other instead.
int ltl::_expr_getalign | ( | const A & | a, | |
const B & | b | |||
) | [inline] |
Determine the alignment (w.r.t. natural vector boundaries) of the operands in an expression.
When determining the alignment of an expression it is sufficient to return any one of the alignments from any one of the MArrays in the expression since we only vectorize if we know the alignments are the same. However, we must make sure that we do not ask e.g. a literal constant for a it's alignment ...
In the general case, just use the LHS's alignment ... We will provide partial specializations for the cases where one of the operands does not have an alignment and return the other's instead.