c ++ проверка во время компиляции, если функция вызывается - PullRequest
1 голос
/ 15 апреля 2020

Возможные дубликаты, которые я объясню внизу.

Мне было интересно, можно ли выполнить проверку времени компиляции, чтобы увидеть, вызывается ли функция перед другой функцией.
Мой пример использования выглядит что-то вроде этого:

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

auto v = f.value();

Так что в этом случае я хотел бы получить ошибку времени компиляции, если пользователь не вызывал isOk перед вызовом значения. Насколько я знаю и искал, это кажется невозможным, но я хотел спросить здесь, чтобы быть уверенным, что я не пропустил ни одного c ++ magi c.

FauxDupes:
Как проверять во время компиляции, что функция может быть вызвана во время компиляции?
Речь идет о том, чтобы знать, является ли ваша функция функцией constexpr. Я хочу знать, была ли вызвана одна функция до вызова другой.

Ответы [ 3 ]

2 голосов
/ 15 апреля 2020

То, что вы хотите, невозможно напрямую без существенного изменения вашего дизайна.

Что вы можете сделать, так это принудительно вызывать вызовы обоими, заключив их в один вызов:

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

Однако, это просто переносит проблему на выбор типа возвращаемого значения. Вы можете вернуть std::variant или с некоторыми изменениями в дизайне std::optional, но что бы вы ни делали, он будет оставлен вызывающей стороне, чтобы проверить, что на самом деле было возвращено.

Не считайте самых глупых пользователей и не пытайтесь защитить их от любой возможной ошибки. Вместо этого предположим, что они читают документацию.

Необходимость проверить, является ли возвращаемое значение допустимым, является довольно распространенным шаблоном: функции, возвращающие указатель, могут возвращать нулевой указатель, функции, возвращающие итератор, могут возвращать итератор end. Такие случаи хорошо документированы, и ответственный вызывающий абонент проверит правильность возвращаемого значения.

Чтобы получить дополнительное вдохновение, я отсылаю вас к std::optional, довольно современному дополнению к C ++, которое также сильно зависит от пользователя, чтобы знать, с чем он имеет дело.

PS: Так же, как один контрпример, пользователь может написать такой код, который делает невозможным выполнение желаемой проверки во время компиляции с вашим текущим дизайном:

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

 auto v = f.value();
0 голосов
/ 15 апреля 2020

Один из способов обеспечить порядок - преобразовать временную зависимость в физическую зависимость:

Переместить метод F::getError() и F::value() в их собственную оболочку структуры (Error, Value).

Измените bool F::isOk() на что-то вроде:

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

Тогда вы не можете использовать Error::getError или Value::value() перед вызовом isOk , как и ожидалось:

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();
0 голосов
/ 15 апреля 2020

Одна стратегия для такого рода вещей заключается в использовании __attribute__((warn_unused_result)) (для G CC) или _Check_return_ (msv c).

Затем измените foo() для возврата условия ошибки:

SomeObj obj;
auto result = foo(obj);

Это подтолкнет вызывающего к обработке ошибки. Конечно, существуют очевидные ограничения: foo() не может быть, например, конструктором, и вызывающая сторона не может использовать auto для typename.

...