- See also:
-
Limitation of higher-order functions is that nested functions work fine with free functions and function objects. However, a generalization of delayed function call is needed, to allow lambda expressions invoke member functions of objects.
Closures are abstractions of delayed function calls, encapsulating a function pointer (plus object reference, if member function is invoked.) In addition, closures store references to function arguments. (In this implementation, closures are limited to functions with only one or no arguments.) For example,
template< typename LambdaT >
void f( LambdaT lambda_ )
{
lambda_.evaluate();
}
void g()
{
int var = 0;
std::vector< int > vect( 3 );
f(
scalar( &var ) =
*scalar( action(
&vect,
std::mem_fun( &std::vector< int >::size )
)
);
assert( var == 3 );
}
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 object types and members, for example, member functions of STL containers, the library provides a family of free functions put inside cttl::alias namespace. Assignment expression in sample above can be rewritten using alias::size() call:
template< typename LambdaT >
void f( LambdaT lambda_ )
{
lambda_.evaluate();
}
void g()
{
int var = 0;
std::vector< int > vect( 3 );
f(
scalar( &var ) = alias::size( scalar( &vect ) )
);
assert( var == 3 );
}
This change makes the above code a bit more readable.
As mentioned in scalar primitive documentation, scalars can be explicitly instantiated:
template< typename LambdaT >
void f( LambdaT lambda_ )
{
lambda_.evaluate();
}
void g()
{
lambda< int >::scalar var;
std::vector< int > vect;
lambda< std::vector< int > >::scalar_reference refvect( &vect );
f(
(
alias::push_back( &refvect, 7 )
,
var = alias::size( refvect )
)
);
assert( vect.back() == 7 );
}
This sample reduces clutter of multiple cttl::scalar() helper calls that would otherwise instantiate every scalar primitive in-line.
As a general guideline for dealing with CTTL closures, one could use the following three steps:
- (1) construct unary or binary adaptable function by capturing
- - (a) instance of [member] function pointer adaptor (or simply function pointer for free function with no arguments);
- - (b) function argument, if any;
- (2) add "delayed" property to the closure by enclosing it inside scalar primitive;
- (3) finally, invoke function by dereferencing the closure object.
Note that if function requires an argument, it should be specified by constant reference, or by mutable pointer. If argument is encapsulated by scalar primitive X, reference and pointer can be obtained as X.top() and &X.top(), respectively. For example,
template< typename LambdaT >
void f( LambdaT lambda_ )
{
lambda< char >::scalar CH;
( CH = *lambda_ ).evaluate();
assert( CH.top() == 'A' );
}
void g()
{
lambda< char >::scalar ch( 'a' );
f(
scalar(
CTTL_STATIC_ACTION(
std::ptr_fun( &toupper ),
ch.top()
)
)
);
assert( var == 3 );
}
The library provides a number of helper macros and functions for creation of CTTL closures. This section of documentation gives an overview of these facilities and sample code illustrations.
Notes:
-
This section of documentation provides an overview of existing helpers that
create CTTL closure objects. For the sake of clarity, it is using simplified
notation to describe function signatures. Please refer to code documentation
for exact signatures, including template parameters.
-
Any constant xobject or xargument
should be passed by reference.
-
Any mutable object should be passed by its address.
-
All uppercase names, such as CTTL_ACTION, are macros.
-
All cttl::action()
are overloaded function templates.
-
For most applications, macros should be preferred, since they improve tracing
and simplify debugging of lambda expressions.
CTTL_MEMBER_ACTION
(
xobject,
xaction,
xargument
)
cttl::action(
xobject,
xaction,
xargument
)
|
Returns closure for member function with one argument, where
xobject is a C++ object implementing desired member function. Constant
objects are passed by reference; mutable objects - by address.
xaction
is an instance of function pointer adaptor for unary member function.
xargument is a member function argument. Constant objects should
be passed by reference; mutable objects - by address.
|
struct digit_parser {
std::vector< std::string > vect_digit_names;
lambda< char >::scalar sdigit;
digit_parser()
: vect_digit_names( 2 )
{
vect_digit_names[ 0 ] = "zero";
vect_digit_names[ 1 ] = "one";
}
std::string get_digit_name( char digit_ ) const
{
assert( digit_ == 0 || digit_ == 1 );
return vect_digit_names[ digit_ ];
}
template< typename UniverseT >
size_t grammar( UniverseT& edge_ )
{
return (
*(
first( isdigit )
&
*(
// calculate digit that got parsed:
sdigit = scalar( edge_.first )[ 0 ] - '0',
// replace digit name:
scalar( &edge_ ) = *scalar( CTTL_MEMBER_ACTION(
*this,
std::mem_fun( &digit_parser::get_digit_name ),
sdigit.top()
))
)
)
).match( edge_ );
}
};
|
CTTL_MEMBER_ACTION_NOARG
(
xobject,
xaction
)
cttl::action(
xobject,
xaction
)
|
Returns closure for member function with no arguments, where
xobject is a C++ object implementing desired member function;
xaction is an instance of function pointer adaptor for unary member
function.
|
#include "cttl/cttl.h"
#include "lambda/lambda.h"
using namespace cttl;
int main(int argc, char* argv[])
{
int var = 0;
std::vector< int > vect( 3 );
(
scalar( &var ) =
*scalar( CTTL_MEMBER_ACTION_NOARG(
vect,
std::mem_fun( &std::vector< int >::size )
))
).evaluate();
assert( var == 3 );
return 0;
}
|
CTTL_STATIC_ACTION
(
xaction,
xargument
)
cttl::action(
xaction,
xargument
)
|
Returns closure for free function with one argument, where
xaction is a function pointer adaptor for global or static member unary
function;
xargument function argument.
|
#include "cttl/cttl.h"
#include "lambda/lambda.h"
using namespace cttl;
// function with mutable argument
void fm( int& arg_ )
{
++arg_;
}
// function with constant argument
void fc( int arg_ )
{
std::cout
<< arg_
<< std::endl
;
}
int main(int argc, char* argv[])
{
// construct and initialize scalar:
lambda< int >::scalar sint( 28 );
(
*scalar(
CTTL_STATIC_ACTION(
std::ptr_fun( &fm ),
&sint.top()
)
)
).evaluate();
assert( sint.top() == 29 );
(
*scalar(
CTTL_STATIC_ACTION(
std::ptr_fun( &fc ),
sint.top()
)
)
).evaluate();
return 0;
}
|
CTTL_STATIC_ACTION_NOARG
(
xreseulttype,
xaction
)
cttl::action<xreseulttype>(
xaction
)
|
Returns closure for free function with no arguments. where
xaction is a function pointer;
xresulttype is a template parameter specifying function return
type
|
#include "cttl/cttl.h"
#include "lambda/lambda.h"
void free_f()
{
}
using namespace cttl;
int main(int argc, char* argv[])
{
(
*scalar(
CTTL_STATIC_ACTION_NOARG(
void,
&free_f
)
)
).evaluate();
return 0;
}
|
|   |
  |
#include "cttl/cttl.h"
#include "lambda/lambda.h"
int const& free_f()
{
static const int value = 1;
return value;
}
using namespace cttl;
int main(int argc, char* argv[])
{
lambda< int >::scalar sint;
(
sint = *scalar(
CTTL_STATIC_ACTION_NOARG(
int const&,
&free_f
)
),
CTTL_LAMBDA_ASSERT( sint == 1 )
).evaluate();
return 0;
}
|
- See also:
-
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
1.3.9.1