Boost logo

Boost :

From: Tobias Schwinger (tschwinger_at_[hidden])
Date: 2005-06-17 15:34:21


John Maddock wrote:
>>Like taking the address of an overloaded function or function template
>>instantiation ??
>
>
> Surely you can always just declare your function type as a typedef in that
> case, without going through function_type?
>

Yeah, but only for fixed arity. It's not neccessarily the case in every context:

     template<class F,class T>
     action make_action(F functor, T& tuple)
     {
        // takes address of templated and/or overloaded F::operator()
     }

> function_type is only useful in that it unpacks an mpl sequence defining the
> argument list into the function type, but that's only useful if you have an
> the mpl sequence to begin with. It's those situations I'm finding hard to
> imagine at present.
>

AFAIK, there is a tuples library which provides interoperability with MPL.
However, that's not the primary design consideration:

These sequences provide data structures to exchange differently sized, ordered
collections of types - and this makes the interface very flexible:

Let's say we prefer an interface like this:

     'function_type_consideration1< Tag, Result, A0, A1, A2 ... An>'.

We can easily model it:

     'function_type< Tag, mpl::vector<Result,A0,A1,A2...An> >'

Not let's consider we prefer a different one:

     'function_type_consideration2< Tag, Result, ParamsSeq >'

We can model this as well:

     'function_type< Tag, mpl::push_front<ParamSeq,Result> >'
or
     'function_type< Tag, mpl::joint_view< vector2<Result,Class>, ParamSeq >'

...

>
> OK, you mean take a function sig, decompose to mpl sequent, transform the
> sequence, and recompose again? I think I can understand that, but doesn't
> this get complicated: presumably the transform should be applied only to the
> arguments and not the return type and class type (if present) which
> complicates the code no end unless I'm mistaken.
>

For parameters only there is 'function_type_parameters<T>' ....

>
>> [... clumsy code ]

           ^^^
... and I *really* should've used it here.

>>
>>OK, OK - I admit, this implementation is ugly.
>
>
> It's hard to grok. It's basically a whole mpl tutorial in it's own right
> ;-)

The only problem is: it's so inefficient, that it shows how *not* to do it...

Let's try to use separate data sources, reduce the complexity and the number of
mpl components needed:

( Sorry, I couldn't resist, the previous one was too bad to leave it alone ;-) )

     template
     < typename FunctionType
     , typename ClassTypeTransform = add_reference<_1>
>
     struct forward_signature
       : function_type
         < plain_function
         , mpl::joint_view
           < typename mpl::apply1
             < mpl::if_
               < is_function_type<member_function_pointer,_1>
               , mpl::vector2
                 < function_type_result<_1>
                 , mpl::apply1
                   < typename mpl::lambda<ClassTypeTransform>::type
                   , function_type_class<_1>
> >
               , mpl::vector1< function_type_result<_1> >
>
             , FunctionType
>::type
           , mpl::transform_view
             < typename function_type_parameters<FunctionType>::type
             , param_type<_>
> > >
     { };

Less ugly - but still too heavy practical use. Replacing the first lambda
expressions with hard-wired code seems to be the way to go.

>
> I can see that argument for decomposing the arity, I'm less sure about the
> others, I've got no imagination I guess ;-)
>

Let's try a simple example:

Writing a wrapper functor like 'boost::mem_fn' on top of this library we'ld use

     template<typename MemFnPtr>
     mem_fn_functor<MemFnPtr> mem_fn(MemFnPtr member_function_pointer);

to let the library handle all the cv, cdecl, ellipsis stuff for us. This way
deduction doesn't hand us all parameters.
We'll need them to build up the operator() member in the functor, though.

> The main overlap is with function_traits I guess: that was always a bit of a
> stop gap so that function and signals could just get the job done.
>
> I'm not sure how the overlap with is_function/is_member_function_pointer
> goes, and/or which implementation is the more "lightweight" (probably not
> much in it), if function_traits could produce versions of these traits that
> passed all the existing tests then that would be a big portability thumbs up
> for function_types. I've certainly no objection to recasting those traits
> in terms of function_types if it simplifies things.
>

Great. As said before, FunctionTypes is not ripe for this, yet. But I'ld like to
take it there...

> One thing I've been meaning to look into, but haven't had the time yet: how
> do you handle __stdcall/__fastcall etc?

There is a configuration table that defines which ones to use (see below for an
example).

> Last time I tried to write partial
> specialisations that would select function types with specific calling

Because of ODR ?

Simple: in case custom calling conventions are configured, the default one is
assumed (the first row in the table) to be among them (so there is no
specialization without explicit attributation, then).

> conventions I couldn't get it to work. There's also the horrible __thiscall
> thing: you can't explicitly declare a member function pointer type with
> __thiscall, but it is usually (but not always) the default, so whether or

Even worse:

     int(X::*)(int) <=> int( "__thiscall" X::*)(int)
     int(X::*)(...) <=> int( __cdecl X::*)(...)

This is why you can configure, whether a calling convention should support
variadic functions or not.

So a suitable configuration would be to have an "explicit" thiscall calling
convention with an empty attributation modifier, not allowing variadic functions.

[ ATTENTION: Not a valid configuration (see below) ]

     #define BOOST_FT_CALLING_CONVENTIONS \
               /*---------------------------------------*/ \
               /* name | modifier \ allows '...' */ \
               /*------------|------------\-------------*/ \
               /* [ non-member or static function ] */ \
             ( (( defaultcall, - , 1 )) \
               /* [ member function pointers ] */ \
             , (( defaultcall, - , 1 )) \
               (( thiscall , , 0 )) \
               (( cdecl , __cdecl , 1 )) )

The only problem left, is that the preprocessing code will need a minor
adjustment, so a cell can be marked as explicitly empty (the table above can't
be safely handled by the preprocessor)...

[ Confusion protection: The '-' in the first row just indicates a cell which is
never evaluated, because the first row has a special meaning to represent the
default calling convention. It has nothing to do with what I am talking about,
here. ]

> not:
>
> void (myclass::*)();
> and
> void (__cdecl myclass::*)();
>
> are the same type or not, depends upon the command line arguments to VC, and
> you can't tell what those options are at compile time as far as I know.
>

...and (this one won't go away unless Microsoft implements __thiscall in their
frontend, too) that we may have to reconfigure the library when messing with the
default calling convention.

Setting the default to '__cdecl' in this particular would cause an ODR violation.

Thanks,

Tobias


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