Each C++ operator has a special tag type associated with it. For example the binary + operator has a plus_op tag type associated with it. This operator tag is used to specialize either:

unary_operator<TagT, T0>

binary_operator<TagT, T0, T1>

template classes (see unary_operator and binary_operator below). Specializations of these unary_operator and binary_operator are the actual workhorses that implement the operations. The behavior of each lazy operator depends on these unary_operator and binary_operator specializations.

Preset specializations conform to the canonical operator rules modeled by the behavior of integers and pointers:

The & (address-of), * (dereference) both return a reference to an object.

Prefix ++ returns a reference to its mutable argument after it is incremented.

Postfix ++ returns the mutable argument by value before it is incremented.

The += and its family accept mutable right hand side (rhs) operand and return a reference to the rhs operand.

Infix + and its family accept constant arguments and return an object by value.

The == and its family accept constant arguments and return a boolean result.

Operators && and || accept constant arguments and return a boolean result and are short circuit evaluated as expected.

Special operators and extensibility

It is of course possible to override the standard operator behavior when appropriate. For example, the behavior of std::cout does not conform to the canonocal shift left operator << (i.e. the rhs std::cout is a mutable reference). Odd balls such as this are placed in special_ops.hpp. There you will find specializations for various classes found in the standard lib.

The library is meant to be extensible. Users may implement their own specializations to allow other libraries to be adapted to be partial-function-evaluation savvy. Later on, in the section "Interfacing (to applications, libraries and frameworks)", discussion will be focused on interfacing and extending the framework.

Operator tags

Each C++ operator has a corresponding tag type. This is used as a means for specializing the unary_operator and binary_operator (see below). The tag also serves as the lazy operator type compatible with a composite as an operation (see composite). Here are two examples of operator tags:

Notice that these are again perfect examples of a composite operation. This style of specialized function is ubiquitous in the framework. We shall see how the unary_operator<negative_op, T0> and the binary_operator<plus_op, T0, T1> template classes, work in a short while.

unary_operator

The unary_operator class implements most of the C++ unary operators. Each specialization is basically a simple static eval function plus a result_type typedef that determines the return type of the eval function.

TagT is one of the unary operator tags above and T is the data type (argument) involved in the operation. Here is an example:

This example is exactly what was being referred to by the first example we saw in the section on operator tags.

Only the behavior of C/C++ built-in types are taken into account in the specializations provided in operator.hpp. For user-defined types, these specializations may still be used provided that the operator overloads of such types adhere to the standard behavior of built-in types.

A separate special_ops.hpp file implements more STL savvy specializations. Other more specialized unary_operator implementations may be defined by the client for specific unary operator tags/data types.

binary_operator

The binary_operator class implements most of the C++ binary operators. Each specialization is basically a simple static eval function plus a result_type typedef that determines the return type of the eval function.

TagT is one of the binary operator tags above T0 and T1 are the (arguments') data types involved in the operation. Here is an example:

This example is exactly what was being referred to by the second example we saw in the section on operator tags. higher_rank<T0, T1> is a type computer. We shall see how this works in a short while, pardon the forward information.

Only the behavior of C/C++ built-in types are taken into account in the specializations provided in operator.hpp. For user-defined types, these specializations may still be used provided that the operator overloads of such types adhere to the standard behavior of built-in types.

A separate special_ops.hpp file implements more STL savvy specializations. Other more specialized unary_operator implementations may be defined by the client for specific unary operator tags/data types.

All binary_operator except the logical_and_op and logical_or_op have an eval static function that carries out the actual operation. The logical_and_op and logical_or_op d are special because these two operators are short-circuit evaluated.

Short Circuiting || and &&

The logical_and_op and logical_or_op are special due to the C/C++ short circuiting rule, i.e. a || b and a && b are short circuit evaluated. A forwarding operation cannot be used because all function arguments are evaluated before a function is called. logical_and_op and logical_or_op are specialized composites with implied operations.

rank

rank<T> class has a static int constant 'value' that defines the absolute rank of a type. rank<T> is used to choose the result type of binary operators such as +. The type with the higher rank wins and is used as the operator's return type. A generic user defined type has a very high rank and always wins when compared against a user defined type. If this is not desireable, one can write a rank specialization for the type. Here are some predefined examples:

A template type computer higher_rank<T0, T1> chooses the type (T0 or T1) with the higher rank. We saw in the binary_operator for plus_op how it was used. Specifically:

higher_rank<T0, T1>::type

returns either T0 or T1 depending on which has a higher rank. In some operator applications such as a + b, the result is actually the one with the higher rank. For example if a is of type int and b is of type double, the result will be of type double. This facility can also be quite useful for evaluating some functions. For instance if we have a sum(a, b, c, d, e) function, we can call this type computer to get the type with the highest rank: