1

I have a legacy interface that has a function with a signature that looks like the following:

int provide_values(int &x, int &y)

x and y are considered output parameters in this function. Note: I'm aware of the drawbacks of using output parameters and that there are better design choices for such an interface. I'm not trying to debate the merits of this interface.

Within the implementation of this function, it first checks to see if the addresses of the two output parameters are the same, and returns an error code if they are.

if (&x == &y) {
    return -1;  // Error:  both output parameters are the same variable
}

Is there a way at compile time to prevent callers of this function from providing the same variable for the two output parameters without having such a check within the body of the function? I'm thinking of something similar to the restrict keyword in C, but that only is a signal to the compiler for optimization, and only provides a warning when compiling code that calls such a function with the same pointer.

Billy
  • 5,179
  • 2
  • 27
  • 53
  • 1
    This smells like an XY problem. Why don't you want that check in the function? – blackbrandt Aug 13 '21 at 13:15
  • 2
    @blackbrandt: I certainly want to prevent the same address, but I'd rather have it be a compile-time check than an explicit run-time branch case. I'll update the question to be clearer. – Billy Aug 13 '21 at 13:16
  • 6
    How would you compile-time check `provide_values(array[time()%10], array[time()%10])`? – Raymond Chen Aug 13 '21 at 13:18
  • 1
    Not standard C++ but you could try using the [`__restrict__` keyword](https://stackoverflow.com/questions/776283/what-does-the-restrict-keyword-mean-in-c). It will warn at compile-time if it can see both parameters point to the same variable, but not when you have run-time pointers like @RaymondChen mentioned. – G. Sliepen Aug 13 '21 at 13:22
  • 2
    Pass two structures of distinct types (which may be forced at compile time by requiring something like `std::is_same::value`, `std::is_base_of::value`, and `std::is_base_of::value` all giving a `false` result). Then let the type system do the work - preventing passing of two arguments of the same type also means that two arguments cannot have the same address (assuming the program doesn't exhibit undefined behaviour, in which case all bets are off). It would be easier to simply pass a single struct (which contains both values to be returned by the caller) by reference. – Peter Aug 13 '21 at 14:23
  • 1
    As it is a programming error, an assertion should suffice. Better yet, avoid the problem completly by *returning* a `struct { int x; int y; }` as mentionned by Peter. – Phil1970 Aug 13 '21 at 14:31
  • Assuming you cannot change the legacy interface, add a thunk interface: `int provide_values(pair& xy) { provides_values(xy.first, xy.second); }` and don't expose the legacy interface. – Eljay Aug 13 '21 at 14:56

2 Answers2

4

No, there's not. Keep in mind that the calling code could derive x and y from references returned from some arbitrary black-box functions. But even otherwise, it is provably impossible (by the Incompleteness Theorem) for the compiler to robustly determine whether they point to the same object, since what objects they are bound to is determined by the execution of the program.

Sneftel
  • 40,271
  • 12
  • 71
  • 104
1

If all you want to do is preventing that the user calls provide_values(xyz, xyz), you can use a macro as in the following example. However, this won't protect the user from calling provide_values(xyz, reference_to_xyz), so the whole this is probably pointless anyway.

#include <cstring>

void provide_values(int&, int&) {}

#define PROV_VAL(x, y) if (strcmp((#x),(#y))) { provide_values(x, y); } else { throw -1; }

int main()
{
  int x;
  int y;
  PROV_VAL(x,y);
  //PROV_VAL(x,x); // this throws

  int& z = x;
  PROV_VAL(x,z); // this passes though!
}
Enlico
  • 23,259
  • 6
  • 48
  • 102