0

Goal:

Describe a way to provide function calls in such a way that for a given function "T F(T a, T b)" an expression "E" written in the code as "aEb" calls the function as "F(a, b)"!

Motivation:

To provide abbreviated forms of function calls, when a certain syntax would increase clarity of the code versus the standard form.

Example:

#include <iostream>
#include <random>
#include <valarray>

using namespace std;

constexpr unsigned int SIDES = 20u;

random_device RNDNGEN;
uniform_int_distribution<unsigned int> DICE[SIDES];

// Function to be called via in-order
unsigned int operatorD(unsigned int number, unsigned int sides)
{
  valarray<unsigned int> dice(number);

  for(unsigned int & i : dice)
  {
    i = DICE[sides](RNDNGEN);
  }

  return dice.sum();
}

int main()
{
  // desired call syntax
  int x = 1 + 1d6;   // executes operator=(x, operator+(1, operatorD(1, 6)))
  cout << x << endl; // displays a number in [2, 7]
}

Note that the chosen expression d cannot collide with the expression "1.d", as the dot is required for that and the d may not be followed by a number.

Whether or not the function's name and symbolising expression (in the example "operatorD" and "d") need to be identical is of minor interest; it would be a bonus if both cases were possible.

Failed Solution Attempts:

1) Operator definition

Operator definition is not a functionality of C++ as explained here.

2) Macro definition

The #define directive does not support explicit parameter definitions:

#define (x)D(y) D(x,y)
// error: macro names must be identifiers
#define xDy D(x,y)
// does not recognise x and y as variables

Possible Solutions I do not really want to use:

1) operator overloads with a function object as described here

Triples the number of true function calls for every apparent call.

Related Problems:

Access a one-dimensional array M as a matrix with syntax 'x = M[row][column]'. It would be necessary to define two overloads of operator[] for two different classes of object: a matrix and a vector (either row or column). One 'M[row][column]' call then creates an anonymous vector from M[row], on which [column] is executed. ... Every single 'M[row]' call then generates an object from constructor, on which its own member function operator[] is called exactly once, then it is (being anonymous) immediately discarded.

Use matrix operations in quasi-mathematic notation:

class Matrix
{
  Matrix invert();
  Matrix transpose();
};

Matrix m;
// m = m.invert();
m = m^-1;    // Assuming here that the whole "^-1" were the call sign.
m = m sup-1; // Assuming here that the whole "sup-1" were the call sign.
// m = m.transpose();
m = m^T;    // Dito here; the 'T' is not supposed to be a separate Token.
m = m supT; // This should work in spite of operator^, as it would not be defined for a custom Matrix class.

Hide encapsulation of objects:

class Value
{
  member x;
};
class EncapsulatedValue<Value T>
{
  T value;

  EncapsulatedValue(T value) : value{value}
  {}

  member x()
  {
    member y = T.x;
    /* modifies y */
    return y;
  }
};

Value temp;
EncapsulatedValue v(temp);
member z;
// z = v.x();
z = v.x;
Community
  • 1
  • 1
Zsar
  • 443
  • 3
  • 14
  • The code to solve the matrix[a][b] problem would be too long to post in a comment, but suffice it to say that I could save one class definition, three operator overloads and one object instantiation per matrix[a] call, if only there was a way to tell that 'm[a][b]' is supposed to mean 'm.at(a, b)', where m is of type Matrix and a, b are integers. – Zsar Dec 06 '14 at 00:43
  • maybe you could cook a raw literal? – sp2danny Dec 06 '14 at 00:49
  • For #3, you speak of "3 function calls for every true function call". Is that because you are afraid of the performance hit? – Yakk - Adam Nevraumont Dec 06 '14 at 01:17
  • Depends on the case. For the dice example, I could not care less. The matrix example however DOES suffer from notable performance loss when the matrix becomes sufficiently big (and enough operations are run on it, like a Simplex search on a high-dimensional polyeder). – Zsar Dec 06 '14 at 12:26
  • In general, as this is mostly a vanity feature, I am ready for a lot of additional work in terms of written code, but introducing additional computing load is kind of not worth it. Consider this: If you could write m[a][b] but m.at(a, b) would be guaranteed to be _at least_ thrice as fast... would you _want_ to write m[a][b]? – Zsar Dec 06 '14 at 12:28
  • @sp2danny I am not sure how to "cook literals"... mayhap you could elaborate in a way that is a bit more [Google-friendly](http://literalcooking.blogspot.de/)? – Zsar Dec 06 '14 at 12:32
  • So have you heard of perfect forwarding and expression templates? (use @username to reply with notification) – Yakk - Adam Nevraumont Dec 06 '14 at 13:29
  • @Yakk Yes and no... but mayhap no and no: All I recall about the former is that I need to use move() or forward() on rvalue parameters when I pass them to functions within a function to avoid implicit conversion to lvalue and thence loss of move semantics. - Currently reading about expression templates on Wikipedia, the introduction looks promising. – Zsar Dec 06 '14 at 14:17
  • One easily found source claims that expression templates do what I want to be done: [1](http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Expression-template) However, none explains how exactly this is done; call syntax of the constructed expressions is always equivalent to a function call or overloaded operator. – Zsar Dec 06 '14 at 15:50
  • @Yakk While I see how expression templates can be used to solve the matrix example (by overloading the native operator[] for template parameters), I fail to see how to construct a "xdy" expression for integral numbers x, y. Mayhap you could elaborate a bit more? – Zsar Dec 06 '14 at 15:52
  • In case that this is impossible, I am interested in knowing "how close"/in which ways the desired expression can be approximated (say "(x)d(y)" instead of "xdy"). – Zsar Dec 06 '14 at 16:08
  • user defined literals : http://en.cppreference.com/w/cpp/language/user_literal – sp2danny Dec 06 '14 at 22:10

2 Answers2

0

You can't overload operators on arguments of built-in type (or raw pointers).

Apart from that you're pretty much free to do whatever you want.

What is the question?

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • The question is: How do I make "int x = 1 + 1d6;" a compilable statement equal to "int x = 1 + operatorD(1, 6);". ... With a few constraints such as "not introducing several faux-classes, creating faux-objects and making a multitude of auxilary function calls at runtime". – Zsar Dec 06 '14 at 12:15
  • @anonymous downvoter: please do explain what in your opinion is wrong in this answer. – Cheers and hth. - Alf Dec 06 '14 at 12:39
  • @Zsar: those requirements sound extremely subjective. at a guess, only you yourself can answer your question to your satisfaction. – Cheers and hth. - Alf Dec 06 '14 at 12:40
  • "[...] you're pretty much free to do whatever you want." is obviously wrong, as I already mentioned in the OP that operator definition is not an inherent feature of C++ - only overloading of existing operators is. If this were any different, my first example might even work as is - and then there would be no question. ... _I_ did not downvote, by the way, just sharing my perception. – Zsar Dec 06 '14 at 13:22
  • 1
    @Zsar: You can do pretty much anything you want, as long as you name your operators correcty. `1 + 1d6` can't be made to work, but `1 + 1*d*6` can evaluate as `operator+(1, D(1,6))` – Ben Voigt Dec 07 '14 at 06:06
  • @BenVoigt While certainly true, this does defeat the point, does it not, as 1*d*6 is hardly more readable than d(1,6) whereas "1d6" is a well-known notation inside a certain domain (in case of this spoof example, P&P roleplaying). Indeed, this is part of the question: If not achievable, _how close_ can one approximate the desired expression _for what prize_? – Zsar Dec 07 '14 at 06:20
  • @Zsar "close" is subjective. In my book `16 1-d-6 1/d/6` are all close enough. It's unlikely you can get anything beyond that anyway. – n. m. could be an AI Dec 07 '14 at 06:56
0

This is only a partial answer, but mayhap improvable by additional input.
Pointers from Yakk and sp2danny brought me this far.

The dice example can be partially solved by defining a user literal in this way:

unsigned int operator"" d6(unsigned long long int number)
{
  return operatorD(number, 6u);
}

Obviously, operatorD can be implemented entirely inside this literal definition, so the requirement not to introduce additional runtime overhead is fulfilled.

This can then indeed be called as:

unsigned int x = 4 + 3d6;

But do note that g++ will throw a warning when the operator"" name does not start with an underscore, that all not thusly prefixed names are reserved by the standardisation comitee.

The lacking part lies in the necessity to encode the second parameter of operatorD, sides of the dice to be thrown, into the literal label, that is: to explicitely define all the dice available as literals.

An implementation with variables on both sides, as seen for example on operator+, does not seem possible using this technique:

unsigned int operator"" d(unsigned long long int number, unsigned long long int sides) // Parameters like on operator+
{
  return operatorD(number, sides);
}
// error: [...] has invalid argument list

just as defining a prefix instead of a suffix seems not:

unsigned int operator"" d(unsigned long long int size, int) // Parameters like on postfix operator++
{
  return operatorD(1, size);
}
// same error
// Comparing the d6 variant with operator++ signature already provided a dead giveaway:
// The postfix operator++ has the spoof parameter, the postfix user literal does not.

Interestingly enough, using variadic templates as demonstrated here should indeed allow definition of post-order operator-likes with arbitrary numbers of parameters.

To solve this example completely, it is still necessary to produce a code which works for any number of sides, so that writing e.g. "3d13" does not suddenly fail, because no "d13" suffix had been explicitely declared before.


The mentioned but not explained first matrix example can be solved independently from this by using expression templates and overloading of operator[] in the way described here.

The other mentioned matrix examples could be solved with a single user defined suffix each:

Matrix operator"" T(Matrix & m)
{
  return m.transpose();
}

if only user literals were definable for classes and not just for primitives.


One and a half out of four so far; and the prime example is at least partially solved...
But surely it can be done even better!?

Zsar
  • 443
  • 3
  • 14