<<< Text erase by cttl::edge | Lambda Home | Stack primitive >>> |
Common Text Transformation Library http://cttl.sourceforge.net/
To provide common name for lambda expression operands, CTTL borrows the term "scalar" from C++ terminology, which sometimes labels C++ built-in types as scalar types to emphasize the distinction between scalar and compound types, such as arrays or user-defined classes with data member variables. CTTL scalar classes belong to the category of lambda primitives, comparable to C++ variables, constants, and function calls. An alternative name for scalar would be "expression operand" or "expression terminal".
The purpose of the scalar concept is to adapt a user object, or a variable, intended to be used as a lambda expression operand. The scalar becomes responsible for
Allocating the object as a data member of the scalar class (called instance scalar);
Storing object's reference (called reference scalar);
Exposing the adapted object via a uniform interface.
Scalar collaborates with the class templates representing implementation of lambda components through a well-defined public interface. This establishes the groundwork for a generic computation, which may require constant and/or mutable access to rvalues and lvalues of the involved operands.
Ultimately, scalar classes play distinct role of multi-purpose object adaptors in various aspects of lambda environment. For example, an instance of a scalar class can
represent C++ variable or constant;
encapsulate closure object (delayed function call);
specify closure parameter;
capture value, returned from a higher-order function;
specify higher-order function argument;
become terminal node in a lambda composite tree.
CTTL lambda primitives are instances of the class template
cttl_impl::xst_lambda_wrap< LambdaT > // generic lambda operand
where implementation class LambdaT is one of the following types:
cttl_impl::xst_const_scalar< int > // an integer constant cttl_impl::xst_scalar< ValueT > // a variable of type ValueT cttl_impl::xst_stack< StackT > // a stack of type std::stack< ValueT >
This suggests that the scalar primitive is not the only type of object that can be used as an operand in a lambda expression. Other types are covered in later sections of the documentation:
Scalar primitives can be independently instantiated 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; //... }; // struct lambda
where ValueT is
To qualify as adaptable, a user-defined class must typedef its "value_type", which describes the type of the stored data.
Based on underlying principle of storage, scalars are divided into two categories:
Both kinds are helpful when specifying lambda expression operands.
Demo program
scalar_reference_primitive.cpp
instantiates both scalar types:
Keywords: scalar_reference_primitive.cpp, instance scalar, reference scalar
// scalar_reference_primitive.cpp // Program demonstrates usage of instance- and // reference-scalar primitives. #include "cttl/cttl.h" #include "lambda/lambda.h" using namespace cttl; int main(/*int argc, char* argv[]*/) { int var = 0; // refvar stores reference to var: lambda< int >::scalar_reference refvar( &var ); // svar makes copy of var: lambda< int >::scalar svar( var ); ( refvar = 6, svar = 5 ).evaluate(); assert( var == 6 ); assert( svar.top() == 5 ); return 0; }
Note that the initializer object of the reference-scalar is passed to the constructor by its address:
lambda< int >::scalar_reference refvar( &var );
Consequently, scalar primitives cannot accommodate pointer types. For example,
scalar( "abc" ) // *** will not compile
Instead, one could use an instance-scalar encapsulating the string object:
scalar( std::string( "abc" ) )
Default constructor of the instance-scalar invokes default constructor of the encapsulated type to create the object.
Instance-scalar constructor with one argument takes a const reference to the initializer object and makes a copy of it by invoking the object's class copy constructor.
Reference-scalar constructor takes an argument that is a pointer to a non-const object. The object, therefore, must be passed to the constructor by its address via a mutable pointer. The constructor uses the pointer argument to initialize the private object reference. Since C++ doesn't allow binding of temporaries to non-const references, passing an automatic value to the reference-scalar constructor is illegal, and, hopefully, will result in a compiler error or warning.
The scalar_types_mix.cpp sample declares two instance-scalars storing
The program also creates two of the corresponding reference-based scalars. The code uses overloaded lambda operators to read and write the values. Alias helpers invoke member functions to manipulate the objects as necessary:
Keywords: scalar_types_mix.cpp, scalar types, std::vector, alias::push_back, alias::back
// scalar_types_mix.cpp // Program demonstrates usage of miscellaneous scalar types // in lambda expressions. #define CTTL_TRACE_RULES // automatically turns lambda tracing on #include "cttl/cttl.h" #include "lambda/lambda.h" using namespace cttl; int main(/*int argc, char* argv[]*/) { cttl::lambda< int >::scalar Variable; cttl::lambda< std::vector< int > >::scalar Vector; cttl::lambda< int >::scalar_reference VariableRef( &Variable.top() ); cttl::lambda< std::vector< int > >::scalar_reference VectorRef( &Vector.top() ); ( VariableRef = 1234, alias::push_back( &VectorRef, VariableRef.top() ), CTTL_LAMBDA_ASSERT( Variable == 1234 ), CTTL_LAMBDA_ASSERT( alias::back( Vector ) == 1234 ), Variable = 5678, alias::push_back( &Vector, Variable.top() ), CTTL_LAMBDA_ASSERT( VariableRef == 5678 ), CTTL_LAMBDA_ASSERT( alias::back( VectorRef ) == 5678 ) ).evaluate(); return 0; }
Besides standalone instantiation, scalars can be created in-line, via a family of overloaded scalar() helper functions. Those helpers have the following prototypes:
// A code fragment from lambda/xst_helpers.h: // ... // Instantiates scalar primitive: template<typename ValueT> xst_lambda_wrap< xst_scalar< ValueT > > scalar( ValueT const &value_ ); // Instantiates scalar reference primitive: template<typename ValueT> xst_lambda_wrap< xst_scalar< ValueT & > > scalar( ValueT *pvalue_ );
For example, consider lambda_double.cpp program:
// lambda_double.cpp // Program demonstrates lambda expression (delayed expression), // and delayed evaluation. #include "cttl/cttl.h" #include "lambda/lambda.h" using namespace cttl; double amount = 0.0; template< typename LambdaT > void f( LambdaT lambda_ ) { assert( amount == 0.0 ); lambda_.evaluate(); // delayed evaluation assert( amount == 5.0 ); } int main(/*int argc, char* argv[]*/) { f( scalar( &amount ) = scalar( 5.0 ) ); // delayed expression "amount = 5.0" return 0; }
Here,
scalar( &amount ) returns a reference scalar holding reference to the global variable amount.
scalar(5.0) returns an instance scalar encapsulating value of 5.0 in a data member of type double.
More examples of scalars instantiated in-line:
scalar( x ) + scalar( 2 ) // lambda equivalent of ( x + 2 ) scalar( &x )^atoi^"5" // lambda equivalent of ( x = atoi( "5" ) ) scalar( &x ) = alias::size( scalar( &vect ) ) // Same as ( x = vect.size() )
In the last two expressions,
scalar( &x )^atoi^"5" is an example of a higher-order function, and
alias::size(scalar(&vect)) is an example of a closure.
Copyright © 1997-2009 Igor Kholodov mailto:cttl@users.sourceforge.net.
Permission to copy 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.
<<< Text erase by cttl::edge | Lambda Home | Stack primitive >>> |