of course we're not done. who told you we were done!?
the real beauty of c++ is making the hard easy, the easy hard, and both cause
unexpected template substitution errors. we want some convenient syntax for
this, and anyone who says we're abusing how operators work be damned! the
since python uses ** as an exponentiation operator, we should use that as our
syntax for exponentiation, too, just a little extended. c++, as you may know,
does not have a ** operator, but it does have a unary * operator — pointer
dereferencing — that's conveniently overloadable! so we'll just use that.
but, like, how? and how are we going to manage to chain them and get the right operator?
we'll start with some intermediate types, since you can't overload built-in operators and some operator overloads have to be members (they think they're sooooo darned special). so we'll pull a trick from java sorry, bjarne (pp. 22–23). and define our own numeric class for the right-hand side. we're also going to need some way to count how many asterisks we've typed — and we want to do that statically — which i've decided to do via an intermediate type that should disappear at runtime. so:
24template <typename last> 26struct next_op; 27 28struct integer { 29 constexpr const static int N = 2; int i; 31 32 integer(int i) : i(i) {} 34 35 operator int() const { return i; } 37 38 next_op<integer> operator*(); 40}; 41 42integer operator""_i(unsigned long long i) { return integer(i); }
for the purposes of this shitpost, i have refrained from testing some of the
more exotic operations you can perform on integers, such as addition. the
conversion operator operator int should take care of all that for us.
anyway, that's integer defined. now, for next_op:
44template <typename last> 45struct next_op { 46 constexpr const static int N = last::N + 1; 47 const last& l; 48 49 next_op(const last& l) : l(l) {} 51 52 next_op<next_op<last>> operator*() { return next_op<next_op<last>>(*this); } 54 55 operator int() const { return l; } 57}; 58 59next_op<integer> integer::operator*() { return next_op<integer>(*this); }
the objective here being that we can "dereference" integer some number of times,
then perform a final "multiplication" to actually apply the hyperoperator.
speaking of:
61template<typename last> 62int operator*(int a, next_op<last> b) { 63 return hyperop<decltype(b)::N>(a, b); 64}
that should do it. i refer you back to the the beginning of this post.