Boost logo

Boost :

From: Matt Calabrese (rivorus_at_[hidden])
Date: 2006-07-12 17:04:35


This email actually introduces a few different concepts, so at least skim
the following paragraphs if the first couple of topics don't capture your
interest. As a summary, I am introducing manipulatable preprocessor function
and function template descriptions, a macro which automatically generates a
"result" template for use with result_of that does not rely of typeof (based
off of such function descriptions), the possibilty of macros which use
function descriptions to generate tempalte metafunctions which can yield if
a function object call is ambiguous or if there is no match for provided
parameter types, and finally an "object" concept for preprocessor
metaprogramming which allows for simulation of "virtual" or "overloaded"
macros which could potentially be used to join Boost.Preprocessor containers
by "overloaded" macros, allowing for fewer named preprocessor macros exposed
to the user and opens the possibilty up for more generic preprocessor
algorithms.

_____

Preprocessor function descriptions:

While developing my current library,
I had to come up with a way to describe functions, member functions,
function templates, and member functions in such away that they could
be examined and manipulated easily by the preprocessor, ideally all
with the same generic code. Such descriptions contain named
template parameters in the case of a template, a specifier, a return type,
named function parameters, and a qualifier in the case of member functions.
Using macros, programmers can then easily change the name of the function,
change the return type, transform paramters in order to change their types
and/or name, and automatically generate function headers. First, is there
any interest for such macros outside of use from my library. For clarity,
usage is as such:

// where int isthe return type, test is the name, float left is the first
parameter, and float right is the second parameter
#define A_FUNCTION BOOST_FUN( (int), test, ((float),left)((double),right) )

BOOST_FUN_IMPL_HEADER( A_FUNCTION )
{
  return static_cast< int >( left + right );
}

Uses for this primarily include things such as automatic creation of
forwarding functions which may alter parameter types, but
also allow for several more interesting applications.
___________

That brings me to perhaps a more common use. Using this little sublibrary of
functions, I am making a result template generator for function objects
which allows a user to very easily create a result metafunction which is
automatically defined, without the need for typeof. For instance:

struct fun_obj
{
  BOOST_RESULT( ( BOOST_MEM_FUN( (int), operator (),
((float),left)((double),right) ) )
                                    ( BOOST_CONST_MEM_FUN( (int), operator
(), ((float),left)((double),right) ) )
                                    ( BOOST_MEM_FUN_T(
((typename),L)((typename),R), (float), operator (), ((L),left)((R),right) )
)
                                  )

 // rest of object body
};

// result is equal to float
typedef result_of< fun_obj( float, float ) >::type result;

____________

Going further, a macro could easily be created which also produces a
metafunction that tells you if a function object call is ambiguous, and a
macro could be created which produces a metafunction which tells you if a
function object call has no matches for the provided argument
types. Such metafunctions could potentially
improve error reporting, particularly for forwarding functions.

___________

Finally, if none of that is of immediate interest, the underlying mechanism
for all of this may be. What I do in order to get polymorphic behavior for
different kinds of functions (normal functions, member functions, and
templates), is I use a form of object
system. The concept is simple, but is applicable to a large amount of
areas in preprocessor metaprogramming, and I offer it as a potential
fundamental change to the way
Boost.Preprocessor
 is interfaced with by simulating macro overloading, allowing for
fewer named functions but with the same functionality and for more
simple creation of generic preprocessor algorithms.

The concept is simply this: An object is represented by the form

( OBJECT_ID, (object_data) )

Where OBJECT_ID is a unique "type" for the object and object_data is the
internal implementation fo the container.

The way it works is you "construct" an object by calling a macro which takes
in the data and assembles it into the object representation just described,
then, internally when calling overloaded" macros arguments are forwarded to
a macro which internally concatenates the function name to the OBJECT_ID and
forwards the arguments once more along with the internal representation of
the object. The result is a seemingly "overloaded" or "virtual" macro. As an
example of how this could be extremely useful for Boost.Preprocessor is it
allows for an easy way to represent consistent container concepts. For
instance, as Boost.Preprocessor stands, common container operations for
arrays and sequences use different names, making it difficult to work with
containers generically as you would in the STL or even in MPL. However, this
could all be changed, and done so by merely building on top of code that is
already there. As an example, here is a BOOST_PP_FRONT macro that works with
either BOOST_PP_ARRAYs or BOOST_PP_SEQs (and could be easily be made to work
with lists as well).

// Seq constructor
#define BOOST_PP_SEQ( seq ) ( BOOST_PP_SEQ_OBJ, (seq) )

// Seq FRONT implementation
#define BOOST_PP_SEQ_OBJ_FRONT( seq ) BOOST_PP_SEQ_HEAD( seq )

// Array constructor
#define BOOST_PP_ARRAY( size, tuple ) ( BOOST_PP_ARRAY_OBJ, ((size,tuple)) )

// Array FRONT implementation
#define BOOST_PP_ARRAY_OBJ_FRONT( array ) BOOST_PP_ARRAY_ELEM( 0, array )

// FRONT implementation
#define BOOST_PP_FRONT( obj ) BOOST_PP_NULLARY_VFUN( obj, FRONT )

// And here is how the code is used:

#define SOME_ARRAY BOOST_PP_ARRAY( 5, (a,b,c,d,e) )
#define SOME_SEQ BOOST_PP_SEQ( (a)(b)(c)(d)(e) )

int main()
{
// int a;
int BOOST_PP_FRONT( SOME_ARRAY );

// a = 5;
BOOST_PP_FRONT( SOME_SEQ ) = 5;
}

__________

Of course, front is a simple operation, but the same technique can be used
to join all common container operations. For instance, a generic
BOOST_PP_FOR_ALL macro could be made as well as generic transformation
algorithms, etc. No longer would you have to be concerned about which
preprocessor container types you are dealing with and you don't have to
explicitly append the container name to container macros. With this
functionality, Boost.Preprocessor could be described in a much more familiar
sense to generic C++ programmers, through concepts, models, and overloaded
macros.

-- 
-Matt Calabrese

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk