Boost logo

Boost :

From: Tobias Schwinger (tschwinger_at_[hidden])
Date: 2005-06-18 09:34:08


John Maddock wrote:
>>>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.
>
>
> Right, it's how you build up the operator() that I'm stuck on, I can see how
> you can generate the required signature, and you can use that to *declare*
> the right operator, but how do you then implement the body of the function
> when you don't know how many parameters there are? Sorry if I'm being
> dense!
>

This is the only place which needs per-arity-code. Pseudo-code ('#' denotes
preprocessing - or a lot of typing):

    template<std::size_t Arity> struct invokers;

    #FOR ARITY=0 TO MAX_ARITY
    template<> invokers struct< ARITY >
    {
      template<typename F> struct functor
      {
      #FOR PARAM_INDEX=0 TO ARITY-1
        // <-- get parameter types for operator()
      #ENDFOR
      }
    }
    #ENDFOR

However, at this point we only have to deal with the arity, not with ellipsis,
CC-attributes, cv for member function pointers and ptr/ref for static function
pointers (which makes things either incomplete, obscure or huge) because it's
all contained in F.

>>>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 ?
>
>
> No just couldn't get it to work reliably, maybe I didn't try hard enough, I
> was in a rush at the time...
>
>
>>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).
>
>
> I saw the mention in the docs, but I suspect most users won't be familiar
> with the preprocessing library and how to set this up. An example would
> help a great deal (I suspect most folks will just cut and paste the example
> in any case).

Right. It would be better to have this inlined in the docs.

>
> In fact if the code is robust enough (no idea if it is as I haven't had a
> chance to test it), I would favour enabling support for all the VC calling
> conventions by default for that compiler, and adding a separate preprocessed
> header for that one as well.
>

I was not sure on reasonable defaults. That's why I set the default to 100% C++,
for now.

Further I thought it would be "sticking to Boost policy":

<CITE>
   Why doesn't bind automatically recognize nonstandard functions?

   Non-portable extensions, in general, should default to off to prevent vendor
   lock-in. Had the appropriate macros been defined automatically, you could have
   accidentally taken advantage of them without realizing that your code is,
   perhaps, no longer portable.
</CITE>

Personally, I have nothing against enabling it by default, as I like things to
just work out of the box.

Talking about preprocessed files:

Generally, I'ld like to keep the preprocessed files compiler-neutral
(dispatching at file level e.g. "partial template specialization or not" and
using trigraphs or digraphs in the generator code for small in-place workarounds
and preprocessing with *graph-substitution disabled, substituting them
afterwards so *graph support is only needed when using the library in
preprocessing mode (*1) (*2)).

This way it is possible to generate the preprocessed files with a single run for
all compilers without requiring them to be installed ("cross preprocessing" - so
to say ;-) ).

Because of this, I'ld like to generalize your idea of "adding preprocessed files
for MSVC" to "adding preprocessed files for possible default configurations".

Another level of indirection, which can be expressed as functions like this:

    part_of_filename_or_path(default_configuration(compiler,compiler_settings))

instead of:

    part_of_filename_or_path(compiler,compiler_settings)

as several compilers may share the same default configuration but may require
different workarounds.

(*1) The preprocessor of GCC is available as prebuilt binary for pretty much
every platform you can possibly run a compiler on and allows switching trigraph
substitution on and off. Wave will be another option for this task.

(*2) Currently there is a Perl-script which when preprocessed and run dumps and
beautifies here-documents (with an '#include' in the un-preprocessed script) to
create the preprocessed files. While this works for me and is a and funny hack
(and the finalizing trigraph substitution could be done there) it is better to
replace this with a C++ program using Boost.Regex and a Jamfile to control the
preprocessing, in the long term.

>
>>>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.
>
>
> Yep, good.
>
>
>>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)...
>
>
> Right, but ideally you would also really need to be able to detect whether
> __thiscall is indeed the default, or if a command line switch has overridden
> it.

Is there some macro that tells me the default calling convention ? If so, it's
doable.

Well, we can detect this at template level but it won't help us much, because
the following code of course can't work:

     #if ! boost::is_same< void(), all_the_others >
     #define __thiscall // or whatever...
     #endif

;-)

Except when writing a small code generator that dumps a configuration snippet
for MSVC, of course.

>
>>...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.
>

Or only use overloading (we only need a declaration) and the "typeof-bug" for
MSVC<8 and hope the "no explicit __thiscall issue" will be gone in the final
version 8...

>
> I know, this whole area of that compiler is a real pain.
>

It says: "keyword reserved for future use", seems they just forgot...

Regards,

Tobias


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