Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++20 functional-style function calls

I have been recently struggling to achieve a way of calling functions in a functional (piped) style such as given a function foo(int, float) can be called in such way 10 | foo(10.f) .

(Assuming the pipe operator here, which can actually be random)

This is as far as I've gotten

string _add(int&& a1, int&& a2)
{
    return "ADD i:" + to_string(a1) + " i:" + to_string(a2);
}

string _add(float&& a1, int&& a2)
{
    return "ADD f:" + to_string(a1) + " i:" + to_string(a2);
}

string _add(int&& a1, float&& a2)
{
    return "ADD i:" + to_string(a1) + " f:" + to_string(a2);
}

struct add_name;
template <typename SUBJ, typename A1>
auto operator|(SUBJ subj, args<add_name, A1, monostate> args)
{
    return _add(move(subj), move(args.a1));
}
template <typename A1> using add = args<add_name, A1, monostate>;

where args is a template structure to hold arguments. This allows me to invoke the functions like this

string s1 = 10.f | add(15);
string s2 = 10 | add(15);
string s3 = 10 | add(10.f);

Eventually there is a macro that generates the templates for each function name and I am able to use this quite successfully. Also, this works very well for overloads and function templates.

There is, however a huge drawback to this, i.e all function specializations need to be known prior the definition of the operator template.

It makes it more and more challenging to use, while the codebase grows.

I've been back and forth with different ideas, but there are C++ limitations that does not allow me to implement this the way it would be much more useful, including

  1. no way of passing a function name down the template specialization chain
  2. C++ complaining about (re)defining the exact same template

Is there a way to achieve function pipelining as described above ?

like image 859
foo Avatar asked Dec 19 '25 10:12

foo


1 Answers

all function specializations need to be known prior the definition of the operator template.

For builtin and non-dependant type, yes. For dependent type, ADL might help.

no way of passing a function name down the template specialization chain

Passing overloads set cannot be passed by name, but can be by lambda.

C++ complaining about (re)defining the exact same template

You have tag, to allow your different overload, so it shouldn't be problematic.

But, you might write only one operator| if you provide Functor as template parameter, something like:

template <auto, typename T>
struct Args
{
    Args(T data) : data(std::move(data)) {}
    
    T data;  
};

template <typename LHS, auto F, typename T>
auto operator|(LHS&& lhs, Args<F, T> rhs)
{
    return F(std::forward<LHS>(lhs), std::move(rhs.data));
}

then

// Your overloads
std::string _add(int&& a1, int&& a2)
{
    return "ADD i:" + std::to_string(a1) + " i:" + std::to_string(a2);
}

std::string _add(float&& a1, int&& a2)
{
    return "ADD f:" + std::to_string(a1) + " i:" + std::to_string(a2);
}

std::string _add(int&& a1, float&& a2)
{
    return "ADD i:" + std::to_string(a1) + " f:" + std::to_string(a2);
}

// The alias
template <typename T>
using add = Args<[](auto&& lhs, auto&& rhs){ return _add((decltype(lhs))lhs, (decltype(rhs))rhs); }, T>;

Demo.

like image 100
Jarod42 Avatar answered Dec 22 '25 00:12

Jarod42



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!