• Stars
    star
    487
  • Rank 86,956 (Top 2 %)
  • Language
    C++
  • License
    Other
  • Created about 11 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Named operators for C++

Named operators

tl;dr

The following code is legal C++ and does exactly what you’d expect it to do.

auto result = "Hello" <repeat> 3 <join> ", ";
std::cout << result << '\n';

Output:

Hello, Hello, Hello

This project explains how.

Background

Named operators are (user-defined) operators which have names rather than symbols. Here’s Haskell:

x = a `div` b

and here’s R:

yup <- 4 %in% c(1, 2, 3, 4, 5)

In fact, C++ also has named operators – alternative tokens for the primary ones defined, in §2.6.

But those are fixed and not redefinable. Sure, you can #define your own names for tokens …

#define PLUS +

But this has all the usual disadvantages of macros and limits you to the already existing binary operators. Until now.

Take a look at this fully valid, macro-free, compiling and running C++ code:

int x = 42;
int y = 23;
auto z = x <divmod> y; // calculates { x / y, x % y }

You want assignment operators? Not a problem:

vector<int> vec{ 1, 2, 3 };
vec <append>= 4;
// same as:
vec = vec <append> 4;

Definition

Operators can be defined for any binary function-like object by calling make_named_operator:

auto divmod = make_named_operator(divmod_f);

where

pair<int, int> divmod_f(int x, int y) {
    return { x / y, x % y };
}

Or, if you prefer functors (and yes, templates work just fine):

auto append = make_named_operator(append_t());

with

struct append_t {
    template <typename T>
    vector<T> operator ()(vector<T> const& vs, T const& v) const {
        auto copy(vs);
        copy.push_back(v);
        return copy;
    }
};

And of course lambdas work as well:

auto in = make_named_operator(
    [](int i, vector<int> const& x) {
        return find(begin(x), end(x), i) != end(x);
    });

// …

bool result = 24 <in> vec;

Design rationale & implementation

Overloading operators with unconventional semantics generally frowned upon because it violates the user’s expectations (although it has variously been used to great effect).

Furthermore, the set of operators that can be created in this fashion is limited to a subset of the built-in operators.

On the other hand, using infix notation instead of function calls can undeniably make code more readable, especially when nesting lots of operations. Compare

auto result = contains(set_minus(set_minus(A, B), C), x);

and

auto result = x <in> (A <set_minus> B <set_minus> C);

Other languages have recognised and addressed this problem.

Since C++ allows overloading operators for custom types, named operators can be implemented by simply sticking a place-holder object between two overloaded operators (which can be entirely arbitrary):

struct some_tag {} op;
struct op_temporary {};
op_temporary operator <(int lhs, some_tag rhs);
int operator >(op_temporary lhs, int rhs);

These declarations are enough to make the following syntax valid:

int x, y, z;
z = x <op> y;

Of course, what the compiler really sees is

z = operator>(operator<(x, op), y);

This already highlights a problem: operator precedence. In effect, op will have the precedence of its surrounding operators (and woe if those don’t match!). In particular, the precedence of < and > is very low. However, I don’t believe that this is too big a problem: somebody once remarked to me,

C++ really only has two precedence rules: (1) BODMAS; (2) for everything else use parentheses.

While I don’t quite agree with this, I think it’s the right attitude in the case of named “operators” that are added via what is effectively a language hack: be on the safe side, use parentheses.

The implementation itself is straightforward: the first operator caches the left-hand side of the expression in named_operator_lhs, and the second operator performs the given operation on the cached value and the right-hand side. As we want the operators to be freely configurable, we cache it as well. This way we can adapt any callable object with two parameters into a binary named operator.

Background

The idea for user-defined names operators comes from an answer posted by Stack Overflow user Yakk. His proposal uses configurable delimiters for the operator name, allowing for operators such as -diff- and *cross*, thus taking the respective operator precedence from their delimiting operators.

More Repositories

1

box

Write reusable, composable and modular R code
R
737
star
2

cpp11-range

Range-based for loops to iterate over a range of numbers or values
C++
304
star
3

thesis

My PhD thesis, “Investigating the link between tRNA and mRNA abundance in mammals”
TeX
38
star
4

lisp.cpp

Minimal Lisp implementation in C++, inspired by “lispy”
C++
37
star
5

decorator

R function decorators
R
33
star
6

multifunction

A multicast function type for C++
C++
27
star
7

minimappr

Code minimaps for R
R
19
star
8

hyperlight

Automatically exported from code.google.com/p/hyperlight
PHP
16
star
9

fun

Module for functional programming in R
R
16
star
10

sys

Easily create reusable command line scripts with R
R
13
star
11

example-r-analysis

An example for an R analysis workflow using a Makefile, shell scripts and Knitr
R
12
star
12

trna

tRNA gene regulation downstream analysis
R
8
star
13

cv

Resume
TeX
6
star
14

unpack

Vector unpack assignment syntax for R
R
5
star
15

math-art

R
4
star
16

streampunk

Compiler for a pipe-based stream language to construct complex pipelines
C++
4
star
17

knitr-example

An example of a genomics analysis using knitr
4
star
18

rcane

Miscellaneous R tools that I haven’t had time yet to properly integrate. (deprecated and unmaintained)
R
3
star
19

modules

An alternative module system for R
R
3
star
20

vim-snakemake

Snakemake Vim definitions, copied from https://bitbucket.org/snakemake/snakemake/
Vim Script
2
star
21

.files

My dotfiles repository
Shell
2
star
22

system-setup

Bootstrap a usable system configuration for OS X
Shell
2
star
23

parser-combinators

Parser combinators in R
R
2
star
24

klmr.github.io

My website
JavaScript
2
star
25

switch-r

R version switcher for macOS
Shell
2
star
26

cpp11-raw-ptr

A raw pointer type for C++11
C++
2
star
27

te

Map, quantify and analyse transposable element expression
Makefile
2
star
28

ggplots

Some (different, better) defaults for ggplot2
R
2
star
29

trna-chip-pipeline

Upstream ChIP-seq analysis pipeline for tRNA data
Python
2
star
30

rnaseq-norm

Presentation slides about RNA-seq normalisation methods
Makefile
1
star
31

codons

Analysis of adaptation of translation efficiency by means of codon–anticodon selection
R
1
star
32

bog-2015-poster

Biology of Genomes 2015 poster
PostScript
1
star
33

roxydoxy

R
1
star
34

r-dict

R
1
star
35

poly-u

R
1
star
36

pichip

Makefile
1
star