8
\$\begingroup\$

Here is my code:

#include <iostream>
#include <type_traits>
#include <functional>
#include <tuple>

namespace meta {

template <bool B, bool... Bs>
struct all_true {
   enum : bool { value = B && all_true<Bs...>::value }; 
};

template <bool B>
struct all_true<B> {
    enum : bool { value = B };
};

template <typename Fn, typename... Args>
struct is_partial_t_nothrow_ctor {
    enum : bool {
        a = std::is_nothrow_constructible<Fn, Fn&&>::value,
        b = all_true<std::is_nothrow_constructible<Args, Args&&>::value...>::value,
        value = a && b
    };
};

}

template <typename Fn, typename... Args>
class partial_t {
private:
    Fn fn_;
    std::tuple<Args...> args_;

    template <std::size_t... Is, typename... ExArgs>
    decltype(auto) op_parens_impl(std::index_sequence<Is...>, ExArgs&&... ex_args) {
        fn_(std::forward<Args>(std::get<Is>(args_))...,
            std::forward<ExArgs>(ex_args)...);
    }

    template <typename... ExArgs>
    decltype(auto) op_parens_impl(ExArgs&&... ex_args) {
        return op_parens_impl(
            std::index_sequence_for<Args...>(), std::forward<ExArgs>(ex_args)...);
    }

public:
    partial_t(Fn&& fn, Args&&... args)
        noexcept(meta::is_partial_t_nothrow_ctor<Fn, Args...>::value)
        : fn_(std::forward<Fn>(fn)), args_(std::forward_as_tuple(args...))
    {}

    template <typename... ExArgs>
    decltype(auto) operator()(ExArgs&&... ex_args)
        // noexcept(?)
    {
        return op_parens_impl(std::forward<ExArgs>(ex_args)...);
    }
};

template <typename Fn, typename... Args>
auto partial(Fn&& fn, Args&&... args)
    noexcept(meta::is_partial_t_nothrow_ctor<Fn, Args...>::value)
{
    return partial_t<Fn, Args...>(std::forward<Fn>(fn), std::forward<Args>(args)...);
}

Test code:

void test(int x, int y, int z) {
    std::cout << "my first number is " << x << std::endl;
    std::cout << "my second number is " << y << std::endl;
    std::cout << "my favorite number is " << z << std::endl;
}

int main() {
    auto f = partial(test, 5, 3);
    f(7);
}

which outputs:

my first number is 5
my second number is 3
my favorite number is 7

The function partial takes a function object as its first parameter and then a number of arguments. It returns a function object which binds these arguments to the input function as its first parameters, then the rest of the arguments are provided later when this partial function is invoked.

\$\endgroup\$
4
\$\begingroup\$

Looks like you are trying to re-invent std::bind.

#include <functional>
#include <iostream>


void test(int x, int y, int z) {
    std::cout << "my first number is " << x << std::endl;
    std::cout << "my second number is " << y << std::endl;
    std::cout << "my favorite number is " << z << std::endl;
}

int main() {
    // Add placeholders for non bound values.
    using namespace std::placeholders;

    // Note the last argument is a place holder.
    auto f = std::bind(test, 5, 3, _1);

    // Now you can call and pass a placeholder argument.
    f(7);
}

As a side note: This is the kind of reason why underscore is reserved for the implementation to allow for little shortcuts like this without having to re-invent the language.

Rather than using recursive templates. I would have used an iterative aproach. With constexpr you can do quite a lot that is evaluated at compile time.

struct all_true
{
    template<typename... T>
    static bool constexpr isTrue(T... b)
    {   
        bool result = true;
        for(auto const& a: {b...})
        {   
            result = result && a;
        }   
        return result;
    }   
};

// Then your code looks like this:
b = all_true::isTrue(std::is_nothrow_constructible<Args, Args&&>::value...),
\$\endgroup\$
  • \$\begingroup\$ Wow I totally forgot about std::bind. Oh well I still learned a bit about meta programming ^-^. Have you got any feedback for my code? \$\endgroup\$ – Cameron White Oct 24 '17 at 19:00
2
\$\begingroup\$

Thanks to the advice I realised it would be a lot easier to implement in C++17 so here is my solution:

namespace detail {
    template <typename Fn, typename... Args>
    struct is_partial_functor_nothrow_constructible {
        enum : bool {
            a = std::is_nothrow_constructible_v<Fn, Fn&&>,
            b = (std::is_nothrow_constructible_v<Args, Args&&> && ...),
            value = a && b
        };
    };

    template <typename Fn, typename... Args>
    constexpr bool is_partial_functor_nothrow_constructible_v =
        is_partial_functor_nothrow_constructible<Fn, Args...>::value;
}

template <typename Fn, typename... Args>
class partial_functor {
private:
    Fn fn_;
    std::tuple<Args...> args_;

public:
    constexpr partial_functor(Fn&& fn, Args&&... args)
        noexcept(detail::is_partial_functor_nothrow_constructible_v<Fn, Args...>)
        : fn_(std::forward<Fn>(fn))
        , args_(std::forward_as_tuple(args...))
    {}

    template <typename... ExArgs>
    constexpr decltype(auto) operator()(ExArgs&&... ex_args)
        noexcept(std::is_nothrow_invocable_v<Fn, Args..., ExArgs...>)
    {
        return std::apply(fn_, std::tuple_cat(
            args_, std::forward_as_tuple(ex_args...)));
    }
};

template <typename Fn, typename... Args>
constexpr decltype(auto) partial(Fn&& fn, Args&&... args)
    noexcept(detail::is_partial_functor_nothrow_constructible_v<Fn, Args...>)
{
    return partial_functor<Fn, Args...>(
        std::forward<Fn>(fn),
        std::forward<Args>(args)...);
}
\$\endgroup\$
1
\$\begingroup\$

Consider the use of an empty typelist. What is the result of all_true<>?

Try to use the standard library as much as possible. std::true_type and std::conditional can go pretty far.

Here's a compliant version of Logical Operator Type Traits.

namespace meta {
    template <typename... Bs> 
    struct conjunction;

    template <typename B1, typename... Bs>
    struct conjunction<B1, Bs...>
        : std::conditional<B1::value, meta::conjunction<Bs...>, B1>::type {};

    template <typename B>
    struct conjunction<B> : B {};

    template <>
    struct conjunction<> : std::true_type {};

    template <typename... Bs>
    struct disjunction;

    template <typename B1, typename... Bs>
    struct disjunction<B, Bs...>
        : std::conditional<B1::value, B1, meta::disjunction<Bs...>>::type {};

    template <typename B>
    struct disjunction<B> : B {};

    template <>
    struct disjunction<> : std::false_type {};

    template <typename B>
    struct negation : std::integral_constant<bool, !B::value> {};

    // Helper variable templates
    template <typename... Bs>
    constexpr bool conjunction_v = meta::conjunction<Bs...>::value;

    template <typename... Bs>
    constexpr bool disjunction_v = meta::disjunction<Bs...>::value;

    template <typename B>
    constexpr bool negation_v = meta::negation<B>::value;
}

template <typename Fn, typename... Args>
struct is_partial_t_nothrow_ctor {
    enum : bool {
        a = std::is_nothrow_constructible<Fn, Fn&&>::value,
        b = all_true<std::is_nothrow_constructible<Args, Args&&>::value...>::value,
        value = a && b
    };
};

a and b are exposed externally. Intended?


template <std::size_t... Is, typename... ExArgs>
decltype(auto) op_parens_impl(std::index_sequence<Is...>, ExArgs&&... ex_args) {
    fn_(std::forward<Args>(std::get<Is>(args_))...,
        std::forward<ExArgs>(ex_args)...);
}

Did you intend to return the result of the function call?


You are reinventing a lot of old and new library features. Internally, std::apply with std::tuple_cat could have removed the need for the private functions. If you like living on the edge, look at the proposal for a generalized call. As Loki mentioned, you likely wanted std::bind.

If you are interested in moving beyond explicit piecewise argument chaining, currying might peak your interest. See Kari.

\$\endgroup\$
  • \$\begingroup\$ I belive generalized call should go into the next version of boost, from what the author said. Though, I couldn't find it in review schedule. \$\endgroup\$ – Incomputable Oct 25 '17 at 12:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.