Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User-defined infix operators

It is easy to introduce new infix operators in C++

// User-defined infix operator framework

template <typename LeftOperand, typename Operation>
struct LeftHelper
{
    const LeftOperand& leftOperand;
    const Operation& operation;
    LeftHelper(const LeftOperand& leftOperand, 
               const Operation& operation)
        : leftOperand(leftOperand), operation(operation) {}
};

template <typename LeftOperand, typename Operation >
auto operator < (const LeftOperand& leftOperand, 
                 Operation& operation)
{
    return LeftHelper<LeftOperand, Operation>(leftOperand, operation);
}

template <typename LeftOperand, typename Operation, typename RightOperand>
auto operator > (LeftHelper<LeftOperand, Operation> leftHelper, 
                 const RightOperand& rightOperand)
{
    return leftHelper.operation(leftHelper.leftOperand, rightOperand);
}

// Defining a new operator

#include <cmath>
static auto pwr = [](const auto& operand1, const auto& operand2) { return std::pow(operand1, operand2); };

// using it
#include <iostream>
int main() 
{
   std::cout << (2 <pwr> 16) << std::endl;
   return 0;
}

Live demo

Unfortunately, this power operator has wrong precedence and associativity. So my question is: how to fix this? I want my <pow> to have higher precedence than * and associate to the right, just like in the mathematical notation.

Edit It is possible to vary the precedence by using different brackets, e.g. |op|, /op/, *op* or even, if one is so inclined, <<--op-->>, but one cannot go higher than the highest built-in operator precedence this way. But today C++ is so powerful with template metaprogramming and type deduction, there simply ought to be some other way to achieve the desired result.

Additionally, it would be nice if I could use pow and not pwr. Unfortunately in some implementations #include <cmath> brings pow into the global namespace, so there will be a conflict. Can we overload operator not such that a declaration of the form

not using std::pow;

removed std::pow from the global namespace?

Further reading: a related proposal by Bjarne Stroustrup.

like image 219
n. 1.8e9-where's-my-share m. Avatar asked Apr 01 '16 12:04

n. 1.8e9-where's-my-share m.


1 Answers

The principle of least surprise is important, and it is key that a*b *power* c * d evaluate to a* (b^c) *d. Luckily there is an easy solution.

To ensure that *power* has a higher precedence than multiplication, you have to use a similar named operator technique for multiplication.

Then instead of directly calculating the results of *power* and *times*, you instead build an expression tree. This expression tree, when evaluated, can apply arbitrary precedence rules.

We can do this with every built-in operator, giving us an easy to read syntax that permits compile-time metaprogramming of operator precedence:

auto z =equals= bracket<
  a *plus* b *times* c *power* bracket<
    a *plus* b
  >bracket *power* x *times* y
>bracket;

To avoid this expression template from being stored longer than optimal, simply overload operator auto()&& to return the deduced type. If your compiler fails to support that feature, =equals= can return the proper type at a mild cost of clarity.

Note that the above syntax is actually realizable in C++ using techniques similar to the OP's. An actual implementation is larger than a SO post should contain.

There are other benefits. As everyone knows, obscure ASCII characters in programming languages have fallen out of favor, and people reading C++ may be confuesed by expressions like:

int z = (a + b* pow(c,pow(x,a+b))*y);

With this technique, all operators have readable names that make their meaning clear, and everything is done infix instead of mixing infix and prefix notation.

Similar solutions to ensure that pow is available can be done by reimplementing <cmath> as <cmath_nopow> yourself. This avoids overloading operator not on language constructs, which causes AST grammar monads to decouple, and/or violates the standard. Maybe try Haskell?

like image 154
Yakk - Adam Nevraumont Avatar answered Oct 14 '22 04:10

Yakk - Adam Nevraumont