namespace my_ns {
struct append_t : named_operator::make_operator<append_t> {};
constexpr append_t append{};
template<class T, class A0, class A1>
std::vector<T, A0> named_invoke( std::vector<T, A0> lhs, append_t, std::vector<T, A1> const& rhs ) {
lhs.insert( lhs.end(), rhs.begin(), rhs.end() );
return std::move(lhs);
}
}
using my_ns::append;
std::vector<int> a {1,2,3};
std::vector<int> b {4,5,6};
auto c = a *append* b;
The core here is that we define an append object of type append_t:named_operator::make_operator<append_t>.
We then overload named_invoke( lhs, append_t, rhs ) for the types we want on the right and left.
The library overloads lhs*append_t, returning a temporary half_apply object. It also overloads half_apply*rhs to call named_invoke( lhs, append_t, rhs ).
We simply have to create the proper append_t token and do an ADL-friendly named_invoke of the proper signature, and everything hooks up and works.
For a more complex example, suppose you want to have element-wise multiplication of elements of a std::array:
This element-wise array code can be extended to work on tuples or pairs or C-style arrays, or even variable length containers if you decide what to do if the lengths don't match.
You could also an element-wise operator type and get lhs *element_wise<'+'>* rhs.
Writing a *dot* and *cross* product operators are also obvious uses.
The use of * can be extended to support other delimiters, like +. The delimeter precidence determines the precidence of the named operator, which may be important when translating physics equations over to C++ with minimal use of extra ()s.
With a slight change in the library above, we can support ->*then* operators and extend std::function prior to the standard being updated, or write monadic ->*bind*. It could also have a stateful named operator, where we carefully pass the Op down to the final invoke function, permitting:
named_operator<'*'> append = [](auto lhs, auto&& rhs) {
using std::begin; using std::end;
lhs.insert( end(lhs), begin(rhs), end(rhs) );
return std::move(lhs);
};
generating a named container-appending operator in C++17.
via Learn programming languages with books and examples
C++ Tutorial => Named operators
https://ift.tt/fV7PzvA
Example
You can extend C++ with named operators that are "quoted" by standard C++ operators.
First we start with a dozen-line library:
this doesn't do anything yet.
First, appending vectors
The core here is that we define an
append
object of typeappend_t:named_operator::make_operator<append_t>
.We then overload named_invoke( lhs, append_t, rhs ) for the types we want on the right and left.
The library overloads
lhs*append_t
, returning a temporaryhalf_apply
object. It also overloadshalf_apply*rhs
to callnamed_invoke( lhs, append_t, rhs )
.We simply have to create the proper
append_t
token and do an ADL-friendlynamed_invoke
of the proper signature, and everything hooks up and works.For a more complex example, suppose you want to have element-wise multiplication of elements of a std::array:
live example.
This element-wise array code can be extended to work on tuples or pairs or C-style arrays, or even variable length containers if you decide what to do if the lengths don't match.
You could also an element-wise operator type and get
lhs *element_wise<'+'>* rhs
.Writing a
*dot*
and*cross*
product operators are also obvious uses.The use of
*
can be extended to support other delimiters, like+
. The delimeter precidence determines the precidence of the named operator, which may be important when translating physics equations over to C++ with minimal use of extra()
s.With a slight change in the library above, we can support
->*then*
operators and extendstd::function
prior to the standard being updated, or write monadic->*bind*
. It could also have a stateful named operator, where we carefully pass theOp
down to the final invoke function, permitting:generating a named container-appending operator in C++17.
via Learn programming languages with books and examples
November 20, 2024 at 09:35AM