c ++在编译时检查是否调用了函数
c++ check at compile time if a function is called
我会在底部解释可能的重复。
我想知道是否可以在编译时检查一个函数是否在另一个函数之前被调用。
我的用例看起来像这样:
auto f = foo();
if(!f.isOk())
return f.getError();
auto v = f.value();
所以在这种情况下,如果用户在调用 value 之前没有调用 isOk,我想得到一个编译时错误。
据我所知和搜索,这似乎是不可能的,但我想在这里问一下,以确保我没有错过任何 C++ 魔法。
仿冒品:
How to check at compile time that a function may be called at compile time?
这是关于了解您的函数是否是 constexpr 函数。我想知道一个函数是否在另一个函数被调用之前被调用。
如果不对您的设计进行实质性更改,您想要的东西是不可能直接实现的。
你可以做的是通过将它们包装在一个调用中来强制调用:
??? foo(const F& f) {
return f.isOk() ? f.value() : f.getError();
}
然而,这只是将问题转移到选择 return 类型上。您可以 return a std::variant
或对设计进行一些更改 std::optional
,但无论您做什么,都将留给调用者检查实际 returned 的内容.
不要假设最愚蠢的用户,也不要试图保护他们免受任何可能的错误。相反,假设他们确实阅读了文档。
必须检查 returned 值是否有效是一个很常见的模式:函数 return 指针可以 return 空指针,函数 return 迭代器可以 return end
迭代器。这种情况有据可查,负责的调用者将检查 returned 值是否有效。
为了获得更多灵感,我建议您参考 std::optional
,这是对 C++ 的一个相当现代的补充,它也严重依赖于用户了解他们正在处理的内容。
PS:作为一个反例,用户可能会编写这样的代码,这使得您当前的设计无法在编译时进行所需的检查:
int n;
std::cin >> n;
auto f = foo();
if(n > 10 && !f.isOk())
return f.getError();
auto v = f.value();
这种事情的一个策略是利用 __attribute__((warn_unused_result))
(对于 GCC)或 _Check_return_
(msvc)。
然后,将foo()
更改为return错误条件:
SomeObj obj;
auto result = foo(obj);
这将促使调用者处理错误。当然有明显的限制:例如,foo()
不能是构造函数,调用者不能使用 auto
作为类型名。
保证秩序的一种方法是将临时依赖转化为物理依赖:
将方法 F::getError()
和 F::value()
移动到它们自己的结构包装器中(Error
、Value
)。
将 bool F::isOk()
更改为:
std::variant<Error, Value> F::isOk()
然后,您不能在调用 isOk
之前使用 Error::getError
或 Value::value()
,正如预期的那样:
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();
我会在底部解释可能的重复。
我想知道是否可以在编译时检查一个函数是否在另一个函数之前被调用。
我的用例看起来像这样:
auto f = foo();
if(!f.isOk())
return f.getError();
auto v = f.value();
所以在这种情况下,如果用户在调用 value 之前没有调用 isOk,我想得到一个编译时错误。 据我所知和搜索,这似乎是不可能的,但我想在这里问一下,以确保我没有错过任何 C++ 魔法。
仿冒品:
How to check at compile time that a function may be called at compile time?
这是关于了解您的函数是否是 constexpr 函数。我想知道一个函数是否在另一个函数被调用之前被调用。
如果不对您的设计进行实质性更改,您想要的东西是不可能直接实现的。
你可以做的是通过将它们包装在一个调用中来强制调用:
??? foo(const F& f) {
return f.isOk() ? f.value() : f.getError();
}
然而,这只是将问题转移到选择 return 类型上。您可以 return a std::variant
或对设计进行一些更改 std::optional
,但无论您做什么,都将留给调用者检查实际 returned 的内容.
不要假设最愚蠢的用户,也不要试图保护他们免受任何可能的错误。相反,假设他们确实阅读了文档。
必须检查 returned 值是否有效是一个很常见的模式:函数 return 指针可以 return 空指针,函数 return 迭代器可以 return end
迭代器。这种情况有据可查,负责的调用者将检查 returned 值是否有效。
为了获得更多灵感,我建议您参考 std::optional
,这是对 C++ 的一个相当现代的补充,它也严重依赖于用户了解他们正在处理的内容。
PS:作为一个反例,用户可能会编写这样的代码,这使得您当前的设计无法在编译时进行所需的检查:
int n;
std::cin >> n;
auto f = foo();
if(n > 10 && !f.isOk())
return f.getError();
auto v = f.value();
这种事情的一个策略是利用 __attribute__((warn_unused_result))
(对于 GCC)或 _Check_return_
(msvc)。
然后,将foo()
更改为return错误条件:
SomeObj obj;
auto result = foo(obj);
这将促使调用者处理错误。当然有明显的限制:例如,foo()
不能是构造函数,调用者不能使用 auto
作为类型名。
保证秩序的一种方法是将临时依赖转化为物理依赖:
将方法 F::getError()
和 F::value()
移动到它们自己的结构包装器中(Error
、Value
)。
将 bool F::isOk()
更改为:
std::variant<Error, Value> F::isOk()
然后,您不能在调用 isOk
之前使用 Error::getError
或 Value::value()
,正如预期的那样:
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();