1

Possible duplicates I'll explain at the bottom.

I was wondering if it is possible to do a compile time check to see if a function is called before another function.
My use case looks something like this:

auto f = foo();
if(!f.isOk())
    return f.getError();

auto v = f.value();

So in this case I would want to get a compile time error if the user did not call isOk before calling value. As far as I know and searched it does not seem possible but I wanted to ask here just to be sure I didn't miss any c++ magic.

FauxDupes:
How to check at compile time that a function may be called at compile time?
This is about knowing wether your function is a constexpr function. I want to know if one function has been called before the other has been called.

turoni
  • 1,345
  • 1
  • 18
  • 37
  • 2
    Generally, the answer will be "no". With separate compilation of source files, the compiler itself will not necessarily know if a function is called at all, let alone if it is called after another function. Practically, if you have two functions and require one to always be called before the other, then combine the two into one-that combined function can then ensure that necessary operations are performed in the correct order, regardless of what code calls it or how many times it is called. In your case, I'd abolish the `isOk()` function and then `foo()` throw an exception if all is not okay – Peter Apr 15 '20 at 07:10
  • If you want compile time check you have to use templates. It could be easy if functions are constexpr. It would be reachable with some template ad hoc structure, but what comes in my mind if it really worth or it is better to re-think the design. – Moia Apr 15 '20 at 07:17
  • If it's not strictly necessary to call `isOk` before `value`, you could also do it as the standard library often does and just formulate some preconditions that have to be true for `value` to succeed (write a good documentation). Then it would be up to the user to make sure those preconditions apply before calling `value` (for example by calling `isOk`) – Lukas-T Apr 15 '20 at 07:25
  • @churill but can those preconditions be checked at compile time? That seems like a runtime thing. – turoni Apr 15 '20 at 07:28
  • @turoni No, it can't. What i meant was to let the user decide if that validation is required or if not. So user doesn't pay for what he doesn't need. But thinking about it, the currently accepted answer is probably the better alternative :) – Lukas-T Apr 15 '20 at 09:22

3 Answers3

2

What you want is not possible directly without changing your design substantially.

What you can do is enforce calling always both by wrapping them in a single call:

??? foo(const F& f) {
     return f.isOk() ? f.value() : f.getError();
}

However, this just shifts the problem to choosing the return type. You could return a std::variant or with some changes on the design a std::optional, but whatever you do it will be left to the caller to check what actually has been returned.

Don't assume the most stupid user and don't try to protect them from any possible mistake. Instead assume that they do read documentation.

Having to check whether a returned value is valid is a quite common pattern: functions that return a pointer can return a null-pointer, functions returning an iterator can return the end iterator. Such cases are well documented and a responsible caller will check if the returned value is valid.

To get further inspiration I refer you to std::optional, a quite modern addition to C++, which also heavily relies on the user to know what they are dealing with.

PS: Just as one counter-example, a user might write code like this, which makes it impossible to make the desired check at compile time with your current design:

 int n;
 std::cin >> n;
 auto f = foo();
 if(n > 10 && !f.isOk())
    return f.getError();

 auto v = f.value();
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
0

One strategy for this kind of thing is to leverage __attribute__((warn_unused_result)) (for GCC) or _Check_return_ (msvc).

Then, change foo() to return the error condition:

SomeObj obj;
auto result = foo(obj);

This will nudge the caller into handling the error. Of course there are obvious limitations: foo() cannot be a constructor, for example, and the caller cannot use auto for the typename.

tenfour
  • 36,141
  • 15
  • 83
  • 142
0

One way to ensure order is to transform the temporary dependency into physical dependency:

Move method F::getError() and F::value() into their own structure wrapper (Error, Value).

Change bool F::isOk() to something like:

  • std::variant<Error, Value> F::isOk()

Then, you cannot use Error::getError or Value::value() before calling isOk, as expected:

auto f = foo();
auto isOk = f.isOk();
if (auto* e = std::get_if<Error>(&isOk)) // Or std::visit
    return e->getError();
auto& value = std::get<Value>(&isOk);
auto v = value.value();
Jarod42
  • 203,559
  • 14
  • 181
  • 302