First, here's the definition.
template< class F, class X >
struct Part {
    F f;
    X x;
    template< class _F, class _X >
    constexpr Part( _F&& f, _X&& x )
        : f(forward<_F>(f)), x(forward<_X>(x))
    {
    }
    template< class ... Xs >
    auto operator () ( Xs&& ...xs ) 
        -> decltype( f(x,declval<Xs>()...) )
    {
        return f( x, forward<Xs>(xs)... );
    }
};
template< class F, class X >
constexpr Part<F,X> closet( F f, X x ) {
    return Part<F,X>( std::move(f), std::move(x) );
}
So, there's this thing called the upward funarg problem. Let's say we wrote a function like this:
std::function<std::vector<int>()> bad_originate() {
    std::vector<int> v = { 1, 2, 3, 4, 5 };
    return [&]{ return v; };
}
 
auto bad = bad_originate();
bad(); // Runtime error!
This fails because the reference to v becomes invalidated when it goes out of scope. We could copy v into the lambda, but our goal is a move.
So can we fix this with partial application? Let's add a bit to the example by including a function to partially apply.
std::vector<int> add_all( std::vector<int>& v, int x ) {
    // Arbitrarily move v.
    auto w = std::move(v);
    std::transform( w.begin(), w.end(), w.begin(),
                    closet(std::plus<int>(),x) );
    return w;
}
using Origin = Part<decltype(add_all)*,std::vector<int>>;
Origin originate() {
    std::vector<int> v = { 1, 2, 3, 4, 5 };
    return closet( add_all, std::move(v) );
}
And it ain't hard at all.
Origin f = originate(); print_vec( "original : ", f.x ); print_vec( "added 10 : ", f(10) ); print_vec( "original : ", f.x );
will output
original : = 1 2 3 4 5
added 10 : = 11 12 13 14 15
original : =
Maybe one would prefer that add_all take its argument by value; now, composition becomes useful.
template< class F, class G > struct Composition {
    F f = F();
    G g = G();
    Composition() { }
    Composition( F f, G g) 
        : f(move(f)), g(move(g)) { }
    template< class X, class ...Y >
    auto operator () ( X&& x, Y&& ...y ) 
        -> decltype( f(g(declval<X>()), declval<Y>()...) )
    {
        return f( g( forward<X>(x) ), forward<Y>(y)... );
    }
};
template< class F, class G > 
Composition<F,G> comp( F f, G g ) {
    return Composition<F,G>( std::move(f), std::move(g) );
}
constexpr struct Mover {
    template< class X >
    X&& operator () ( X& x ) const {
        return std::move(x);
    }
} mover{}; 
std::vector<int> add_all2( std::vector<int> w, int x ) {
    std::transform( w.begin(), w.end(), w.begin(),
                    closet(std::plus<int>(),x) );
    return w;
}
 
using MoveAddAll = decltype( comp(add_all2,mover) );
using Origin2 = Part< MoveAddAll, std::vector<int> >;
Origin2 originate2() {
    std::vector<int> v = { 1, 2, 3, 4, 5 };
    return Origin2( comp(add_all2,mover), std::move(v) );
}
and
Origin2 g = originate2(); print_vec( "original : ", g.x ); print_vec( "added 10 : ", g(10) ); print_vec( "original : ", g.x );
will output the same as above.
The code to this mini-article can be found here: https://gist.github.com/4182570
No comments:
Post a Comment
Please feel free to post any comments, concerns, questions, thoughts and ideas. I am perhaps more interested in what you have to say than you are in what I do.