turoni
turoni

Reputation: 1435

c++ check at compile time if a function is called

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.

Upvotes: 1

Views: 2200

Answers (3)

Jarod42
Jarod42

Reputation: 217358

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();

Upvotes: 0

tenfour
tenfour

Reputation: 36896

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.

Upvotes: 0

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122486

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();

Upvotes: 2

Related Questions