<<< Closure translator example | Lambda Home | CTTL_MEMBER_ACTION >>> |
Common Text Transformation Library http://cttl.sourceforge.net/
In the previous section delayed higher-order functions were presented. Because higher-order functions manifest nested calls, their format is limited to free functions and function objects with one argument.
Closure is a better generalization of a delayed function call. Closures give lambda expressions a way to invoke functions, including member functions of objects.
CTTL closure is represented by a structure that encapsulates three objects:
Note: in current version, CTTL closures are limited to functions with only one or no arguments.
The closure_vect_size.cpp sample was briefly discussed in Connection to CTTL grammar section:
// closure_vect_size.cpp // The program extracts the length of the vector and // stores it in the variable named length. #include "cttl/cttl.h" #include "lambda/lambda.h" using namespace cttl; int main(/*int argc, char* argv[]*/) { std::vector< int > vect( 5 ); std::vector< int >::size_type length; ( scalar( &length ) = *scalar( action( &vect, std::mem_fun( &std::vector< int >::size ) )) ).evaluate(); assert( length == 5 ); return 0; }
The helper function named action() instantiates closure object for the constant member function size of STL vector. The function has no arguments:
std::vector< int >::size_type std::vector< int >::size();
The scalar() helper creates a scalar that encapsulates the closure:
scalar( action( ... ) ) // closure encapsulating reference to vect // and a pointer to "std::vector< int >::size()" member
To invoke encapsulated function, lambda expression must dereference the resulting scalar; the closure class overloads dereference operator* specifically for this purpose:
scalar( &length ) = *scalar( action( ... ) )
The length of the vector is returned by the call, and the result is assigned to the variable length.
Closures are somewhat tedious to write. The code must provide exact signature of the object and the function, including all template arguments for every template involved. To simplify notation for well known combinations of classes and their members, the library provides a family of function helpers enclosed inside cttl::alias namespace. The helpers for a majority of STL containers and their member functions are provided. A rewritten program closure_vect_size_alias.cpp uses cttl::alias::size() helper in the assignment expression from the previous sample:
// closure_vect_size_alias.cpp // The program extracts the length of the vector and // stores it in the variable named length. #include "cttl/cttl.h" #include "lambda/lambda.h" using namespace cttl; int main(/*int argc, char* argv[]*/) { std::vector< int > vect( 5 ); std::vector< int >::size_type length; ( scalar( &length ) = alias::size( scalar( &vect ) ) ).evaluate(); assert( length == 5 ); return 0; }
Using an alias helper makes the code a bit more readable. When aliases used, no dereference operator is required. That, too, makes an alias call look more like a plain C++ function call. The members of cttl::alias namespace are discussed in later sections of lambda documentation.
Besides using alias for well-known member functions, a general-purpose closure can be instantiated by macros,
CTTL_MEMBER_ACTION_NOARG( obj, mf1 ) CTTL_MEMBER_ACTION( obj, mf2, arg ) CTTL_STATIC_ACTION_NOARG( X, f ) CTTL_STATIC_ACTION( f1, arg )
or by a family of overloaded action(...) helper functions, which support all of the above macro formats and return an appropriate closure object.
There is no performance penalty for using macros, and for most applications macros should be preferred, because they improve readability of the trace output and simplify lambda expression debugging.
The parameters are:
obj is an object that implements the member function to be invoked. Note: obj is not a scalar type. Constant objects are passed by const reference; mutable objects - by address.
mf1 and mf2 are function objects acting as member function pointer adaptors. The adaptors are models of STL adaptable unary function and binary function, respectively. Either adaptor mf1 or mf2 is usually the result of the helper call std::mem_fun().
arg is an instance of the function argument. Constant objects are passed by const reference; mutable - by address. Note: the argument arg is not a scalar type. If the argument is already enclosed by another scalar Z, the original object reference should be extracted using Z.top() member.
f1 and f are function pointers to functions with one or no arguments, respectively. Those can also be function objects acting as function pointer adaptors for global or static member functions, compatible with STL adaptable unary function, such as a pointer to unary function (usually the result of STL helper call std::ptr_fun() ).
X is a template parameter specifying the return type of a static or global function.
The following fragment of code uses function g( ) to construct lambda expression, and function f( ) to make a delayed function call:
Keywords: std::ptr_fun, evaluate, function pointer adaptor, dereferenced closure
template< typename LambdaT > void f( LambdaT lambda_ ) { lambda< char >::scalar CH; ( CH = *lambda_ ).evaluate(); // (B) Dereferenced closure invokes the function assert( CH.top() == 'A' ); } void g() { lambda< char >::scalar ch( 'a' ); f( scalar( // (A) Constructs "delayed"... CTTL_STATIC_ACTION( // ...closure... std::ptr_fun( &toupper ), // ...with function pointer adaptor ch.top() // ...and function argument ) ) ); assert( var == 3 ); }
Another sample, closure_toupper.cpp , converts a string of characters to its uppercase version:
Keywords: closure_toupper.cpp, const_edge, character, node, first isalpha, toupper
// closure_toupper.cpp // Program demonstrates conversion to uppercase using // closure (delayed function call.) #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[]*/) { std::string inp = "abc"; const_edge<> substring( inp ); lambda< char >::scalar ch; // temp character value lambda< node<> >::scalar character( substring.first ); size_t result = ( *( // for each character, character.top()( first( isalpha ) ) // match single character + *( ch = character[ 0 ], // get character value character[ 0 ] = *scalar( CTTL_STATIC_ACTION( // convert to UPPERCASE std::ptr_fun( &toupper ), ch.top() ) ) ) ) ).match( substring ); assert( result != std::string::npos ); assert( inp == "ABC" ); return 0; }
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.
<<< Closure translator example | Lambda Home | CTTL_MEMBER_ACTION >>> |