Boost logo

Boost :

Subject: Re: [boost] [local] Help for the Alternatives section
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2011-05-06 10:49:00


On Thu, May 5, 2011 at 2:03 PM, Jeffrey Lee Hellrung, Jr.
<jeffrey.hellrung_at_[hidden]> wrote:
> On Thu, May 5, 2011 at 10:53 AM, lcaminiti <lorcaminiti_at_[hidden]> wrote:
> [...]
>
>> I have been thinking if I can do something similar to this with
>> Boost.Local.
>> Given that local classes cannot have template members I can't do too
>> much...
>> However, I _could_ allow to specify multiple types (but not a generic type)
>> for a local function parameter:
>>
>> void BOOST_LOCAL_FUNCTION_PARAMS(types(int, char) x, types(double,
>> std::string, int) y, long z) {
>>    std::cout << x << y << z << std::endl;
>> } BOOST_LOCAL_FUNCTION_NAME(l)
>>
>> Then I can do:
>>
>> l(1, 1.2, -1);
>> l('a', 1.2, -1);
>> l('a', "bcd", -1);
>> ... // and all the other parameter type combinations
>>
>> types(...) could accept a generic number of types but all these types will
>> have to be known (and not generic as for templates).
>>
> [...]
>
>> This is more overloading than polymorphism... bus still:
>> 1) Compiles on ISO C++.
>> 2) Declares l locally (information hiding).
>> 3) Does not repeat l declaration multiple times (avoid code duplication).
>> 4) l is """effectively polymorphic""" (in between _many_ quotes :) ) in its
>> argument type num as it accepts both doubles and std::strings (but no more
>> than these two types :( ). (I will not actually claim that such a local
>> function l polymorphic in num's type.)
>>
>> What do you think?
>>
> Better than nothing (=strict monomorphicity)!

Does anyone else think this feature is useful??

This is _not_ trivial to implement so I won't do it unless you guys
think it is useful. I might just document it as a possibility in the
docs and implement it only if the library gets accepted...

The macros will have to expand to something like this:

#include <iostream>
#include <vector>
#include <algorithm>

template<typename T1 = void, typename T2 = void, typename T3 = void>
struct types {};

template<typename F>
struct func {};

template<typename R, typename A1>
struct func<R (A1)> {
    typedef R (*call_ptr)(void*, A1);
    func(void* obj, call_ptr call): obj_(obj), call_(call) {}
    R operator()(A1 a1) { return call_(obj_, a1); }
private:
    void* obj_;
    call_ptr call_;
};

template<typename R, typename A1T1, typename A1T2>
struct func<R (types<A1T1, A1T2>)> {
    typedef R (*call_a1t1_ptr)(void*, A1T1);
    typedef R (*call_a1t2_ptr)(void*, A1T2);
    func(void* obj, call_a1t1_ptr call_a1t1, call_a1t2_ptr call_a1t2):
            obj_(obj), call_a1t1_(call_a1t1), call_a1t2_(call_a1t2) {}
    R operator()(A1T1 a1t1) { return call_a1t1_(obj_, a1t1); }
    R operator()(A1T2 a1t2) { return call_a1t2_(obj_, a1t2); }
private:
    void* obj_;
    call_a1t1_ptr call_a1t1_;
    call_a1t2_ptr call_a1t2_;
};

int main() {
// void BOOST_LOCAL_FUNCTION(types(const double&, const std::string&) num,
// const bind& sep, {
// std::cout << num << std::endl;
// }) BOOST_LOCAL_FUNCTION_NAME(l)

    char sep = '\n';

    struct local {
        local(const char& sep): sep_(sep) {}
        void operator()(double num) { body(sep_, num); }
        static void call(void* obj, double num)
            { (*static_cast<local*>(obj))(num); }

        void operator()(std::string num) { body(sep_, num); }
        static void call(void* obj, std::string num)
            { (*static_cast<local*>(obj))(num); }
    private:
        const char& sep_;
        void body(const char& sep, double num)
            { std::cout << num << std::endl; }
        void body(const char& sep, std::string num)
            { std::cout << num << std::endl; }
    } local_l(sep);
    func<void (types<double, std::string>)> l(&local_l,
            local_l.call, local_l.call);

    std::vector<double> v(3);
    v[0] = -1.0; v[1] = -2.0; v[2] = -3.0;
    std::for_each(v.begin(), v.end(), l);
    l(0);

    std::vector<std::string> s(3);
    s[0] = "aa"; s[1] = "bb"; s[2] = "cc";
    std::for_each(s.begin(), s.end(), l);
    l("zz");

    return 0;
}

Note that the macro will have to expand the body in multiple places
(using automatic code repetition because I can't use generic types) so
the body will have to be passed at a macro parameter:

void BOOST_LOCAL_FUNCTION(types(const double&, const std::string&)
num, const bind& sep, { // (1)
    std::cout << num << std::endl;
}) BOOST_LOCAL_FUNCTION_NAME(l)

The macro will take the body as its last parameter but only when
types() is used. If types() is not used the body will still be outside
the macro (I'd rename BOOST_LOCAL_FUNCTION_PARAMS to
BOOST_LOCAL_FUNCTION because the macro sometimes also takes the body
as one of its parameters):

void BOOST_LOCAL_FUNCTION(const double& num, const bind& sep) { //
unchanged syntax
    std::cout << num << std::endl;
} BOOST_LOCAL_FUNCTION_NAME(l)

Passing the body code as a macro parameter when types() is used as in
(1) will cause all compiler errors to have the same line number --
that's a major inconvenience.

> I would assume the types(...) syntax is only optional, correct?

Yes, types() is optional. If types() is not used for any of the
function parameters then the syntax of the macros remains the same as
the one we discussed so far (including the body being outside the
macro). So you can do:

int x // no types() (body specified outside the FUNCTION macro)
types(int) x // same as above so not very useful (body specified
inside the FUNCTION macro)
types(int, std::string, const std::vector<double>&) x // x can be of
multiple types but not of a generic type (body specified inside the
FUNCTION macro).

> Not related to the above discussion: Bound variables' types are deduced
> using Boost.TypeOf; is it possible right now to explicitly specify their
> type?

Yes, I think either you or someone else already asked for this feature
a while back. It's on my to-dos and I will implement it before
submitting the library for review.

I am trying to get this syntax to work but it's requiring good amount
of coordination between preprocessor and template metaprogramming (I
haven't given up yet :) ):

const bind& x // bind without specifying the type (uses Boost.Typeof)
const bind<int>& x // bind specifying the type (doesn't use Boost.Typeof)

If I can't get that to work, the following should be possible without
too much trouble (because I can handle both expression the same way
and just with preprocessor metaprogramming):

const bind& x // bind without specifying the type (uses Boost.Typeof)
const bind_type(int)& x // bind specifying the type (doesn't use Boost.Typeof)

-- 
Lorenzo

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