Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

scalar primitive


SourceForge.net Logo     CTTL on    
    SourceForge    
    Download    
    Latest    
    Documentation    
    Index    
    Library    
    News    
    CVS    
    Repository    
   Other    
   Links    

See also:

CTTL scalar primitives are comparable to C++ variables and constants: they represent lambda expression operands. Such primitives can collaborate via uniform interface, which allows both constant and mutable access to rvalues and lvalues, necessary to carry out computations that involve lambda operands.

Scalar primitives play distinct role of an operand in various subsets of CTTL lambda expressions. These subsets include:

Internally, all scalar primitives are instances of class template

    cttl_impl::xst_lambda_wrap< LambdaT >  // generic lambda operand

where implementation class LambdaT is one of the following:

    cttl_impl::xst_const_scalar< int >     // integer constant
    cttl_impl::xst_scalar< ValueT >        // variable of type ValueT
    cttl_impl::xst_stack< StackT >         // stack of type std::stack< ValueT >

Scalars can be independently instantiated for later use by templated typedefs

template< typename ValueT >
struct lambda {
    typedef xst_lambda_wrap< xst_scalar< ValueT > >               scalar;
    typedef xst_lambda_wrap< xst_scalar< ValueT& > >              scalar_reference;
    typedef xst_lambda_wrap< xst_stack< std::stack< ValueT > > >  stack;
    typedef xst_lambda_wrap< xst_stack< std::stack< ValueT >& > > stack_reference;
};  // struct lambda

ValueT is required to be of primitive type, std::pair, or it can be an adaptable compound type, such as STL container. To qualify as adaptable, object must define typedef named value_type, describing the type of stored data.

For example, the following three declarations instantiate scalars that store an integer variable, a vector of integers, and a stack of size_t values, respectively:

    cttl::lambda< int >::scalar Variable;
    cttl::lambda< std::vector< int > >::scalar Vector;
    cttl::lambda< size_t >::stack Stack;

Scalars that represent integer constants are instantiated by helper macro const_scalar_type( T ), where T is either int, or a type convertible to integer, such as enumerated type:

    enum { zero, one, two };
    const_scalar_type( zero ) ZERO;
    const_scalar_type( one )  ONE;
    const_scalar_type( two )  TWO;
    const_scalar_type( 3 )    THREE;
    const_scalar_type( -5 )   MINUS_FIVE;

Besides stand-alone instantiation, scalar primitives may be created in-line, via a family of overloaded scalar() helper functions, with the following prototypes:

// Fragment from xst_helpers.h
//
// Instantiate scalar primitive:
template<typename ValueT>  
xst_lambda_wrap< xst_scalar< ValueT > > scalar( ValueT const &value_ );


// Instantiate scalar reference primitive:
template<typename ValueT>  
xst_lambda_wrap< xst_scalar< ValueT & > > scalar( ValueT *pvalue_ );

 
// Instantiate stack primitive:
template<typename ValueT>  
xst_lambda_wrap< xst_stack< std::stack< ValueT > > > scalar( std::stack< ValueT > const &stack_ );


// Instantiate stack reference primitive:
template<typename ValueT>  
xst_lambda_wrap< xst_stack< std::stack< ValueT > & > > scalar( std::stack< ValueT > *pstack_ );

In a similar way, scalars that represent integer constants can be instantiated in-line by helper macro const_scalar( T ), where T is either an integer constant, or a type, convertible to integer constant, such as enumerated constant.

Constant scalar is a type of lambda primitive that represents immutable integer values in various subsets of lambda expressions. It is initialized with particular value that cannot be changed. Constant scalars are instatiated by stand alone declarations using const_scalar_type( N ) helper macro, or they can be instantiated in-line, using const_scalar( N ) helper macro, as shown above.

Constant scalars and singe-dimensional composite expressions, formed by constant scalars, play important role of formulating subscript-based access to individual terminal nodes of lambda composite structures.

The following table shows examples of in-line scalar operands in various subsets of lambda expressions:

Example Description
// lambda equivalent of 1 + 2
scalar( 1 ) + scalar( 2 )
This is general-purpose lambda expression implementing addition of two scalars representing C++ integers.
// lambda equivalent of x = atoi( "5" )
scalar( &x ) ^ atoi ^ "5"
Here, return value of higher-order function is specified by scalar primitive. Overloaded operator^ constructs lambda objects representing higher-order function.

// Same as x = myvector.size()
scalar( &x ) = alias::size( scalar( &myvector ) )
In this example, two scalars specify closure return value and closure parameter, respectively. Resulting closure object represents delayed function call to the size() member function of the vector object. Note that both variables are passed be address, suggesting creation of the reference-type scalars, retaining C++ references of the original objects.

const_scalar( 1 )^const_scalar( 2 )^const_scalar( 3 )
This expression instantiates lambda composite structure, which is roughly equivalent to
 struct unnamed {
     static const int x = 1;
     static const int y = 2;
     static const int z = 3;
 };

Scalar primitives can be divided into two categories. The distinction is based on underlying principle of storage: first category includes scalars that accommodate instances of C++ objects; the second attaches itself to already existing objects by storing their reference:

    void f()
    {
        int var = 0;
        // refvar stores reference to var.
        // note that constructor takes address of the variable:
        lambda< int >::scalar_reference refvar( &var );
        // svar instantiates copy of var:
        lambda< int >::scalar svar( var );
        (
            refvar = 6,
            svar = 5
        ).evaluate();
        assert( var == 6 );
        assert( svar.top() == 5 );
    }


scalar interface


In previous example, grammar expression invokes top() member function of the scalar named svar, in order to access underlying data. top() is one of member functions of scalar interface. The interface of scalar is similar to the interface of general-purpose stack:

void f()
{
    lambda< int >::scalar Variable( 4 ); // instantiates integer scalar
    assert( Variable.top() == 4 );       // read access to top element
    
    Variable.push( 3 );                  // push new value
    assert( Variable.top() == 3 );       // verify result
    
    Variable.top() = 2;                  // write access to top element
    assert( Variable.top() == 2 );       // verify result
    
    Variable.pop();                      // does nothing unless Variable is a stack
    assert( Variable.size() == 1 );      // non-stack scalar has constant size
}

From implementation standpoint, singularity of the stack protocol, restricted by access to the top element only, alongside with simple push/pop data deposit and withdrawal, brings scalar primitive to the level of adaptable generic component that has uniform appearance. Such interface is shared by all implementation classes of CTTL lambda library, providing data channels connecting objects inside expression templates. Scalar implementation is a placeholder for both lambda sub-expression (non-terminal) units, as well as scalar operands of unary and binary operators (terminals of the expression). Each expression template is managed by compile-time policy class that implements desired behavior. This technique is widespread among implementations based on expression templates.

From the user standpoint, scalar primitive behaves a lot like a variable representing instance of a C++ built-in type or a class.

Note that in order to emphasize the distinction between scalar and compound types, such as arrays or classes with member variables, C++ built-in types are themselves sometimes referred to as scalar types by C++ documentation. CTTL borrows "scalar" terminology to name its lambda expression operands. An alternative could be an "operand" or a "terminal".

Once we understand how to instantiate scalars, writing lambda expressions becomes easy: there is little difference between lambda and native C++ expressions. This is made possible by the presence of logical, arithmetic, and assignment operators, overloaded for all scalar types of the library.



Copyright © 1997-2006 Igor Kholodov mailto:cttl@users.sourceforge.net.

Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.


Generated on Thu Nov 2 17:48:53 2006 for CTTL Lambda Expression by  doxygen 1.3.9.1