3

I want to dynamically create math expressions with lambdas, as reviewed here.

using Func = std::function<double(double)>;

Func operator+(Func const& lhs, Func const& rhs){
    return [lhs, rhs](double x){
        return lhs(x) + rhs(x);
    };
}

My next goal is to generalize this.

Thanks to this answer I managed to implement a generic type with exactly n arguments. But I do not know - if at all possible - how to specify the lambda arguments.

check it on godbolt

#include <functional>

template <std::size_t, typename T> using alwaysT = T;

    template <typename FLOAT, typename Seq> struct func_struct;

    template <typename FLOAT, std::size_t... Is>
    struct func_struct<FLOAT, std::index_sequence<Is...>> {
        using type = std::function<double(alwaysT<Is, FLOAT>... floats)>;
    };

    template <std::size_t N>
    using func_t = typename func_struct<double, std::make_index_sequence<N>>::type;

    template <std::size_t N>
    func_t<N> operator+(func_t<N> const& lhs, func_t<N> const& rhs){
        return [lhs, rhs](alwaysT<N, std::make_index_sequence<N>> args...){// how to do this?
            lhs(args) + rhs(args);
        };
    }

int main(){
    func_t<1> f1 = [](double x){return x*2;};
    func_t<2> f2 = [](double x, double y){ return x*y;};

    func_t<1> f3 = f1 + f1;


}

I'm also getting an error that the template argument N can not be deduced, in the last line.

3
  • easiest is to accept auto&&... and then do static_assert() on types and arg count. Note that it's an error to have a different number of arguments. Commented Jul 1, 2022 at 20:43
  • @lorro this should be an answer, not a comment Commented Jul 1, 2022 at 20:46
  • posted as such, thx Commented Jul 1, 2022 at 20:52

1 Answer 1

1

Not sure the standard allows you to manage argument count restriction like that, but you can definitely have an assert if the arg count is in mismatch. E.g.:

#include <functional>

template <std::size_t, typename T> using alwaysT = T;

    template <typename FLOAT, typename Seq> struct func_struct;

    template <typename FLOAT, std::size_t... Is>
    struct func_struct<FLOAT, std::index_sequence<Is...>> {
        using type = std::function<double(alwaysT<Is, FLOAT>... floats)>;
    };

    template <std::size_t N>
    struct func_t : func_struct<double, std::make_index_sequence<N>>::type {
        using Base = typename func_struct<double, std::make_index_sequence<N>>::type;
        using Base::Base;
    };

    template <std::size_t N>
    func_t<N> operator+(func_t<N> const& lhs, func_t<N> const& rhs){
        return [lhs, rhs](auto&&... args){
            return lhs(args...) + rhs(args...);
        };
    }

int main(){
    func_t<1> f1 = [](double x){return x*2;};
    func_t<2> f2 = [](double x, double y){ return x*y;};

    func_t<1> f3 = f1 + f1;
}

Note, I've changed the template<size_t N> using func_t to a template<size_t N> struct func_t so that matching on template argument is possible. using declarations are always replaced before matching and the compiler (as per current standard) is not expected to be able to deduce N from <double, 0, 1, 2, ...>.

Sign up to request clarification or add additional context in comments.

4 Comments

This does not compile (also where does T come from?)
The problem is not with the lambda arg count. It's with that you expect func_t<N> to be matched on N. That won't happen as it's a template alias to a much more sophisticated type. Either you can have a template<size_t N> struct func_t for this to work, or (not recommended) you might call the template operator+<N>() manually
Added an explanation and full source.
Your explanations and answer are very much appreciated. Great insight!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.