В начале несколько вводных слов, что б было понятно о чём речь:
bool f();
int main()
{
if (!f()) // Хорошо! Проверили возвращаемое значение
exit(1);
f(); // Плохо! Не проверили возвращаемое значение
}
Как заставить пользователя функции f() не игнорировать возвращаемое значение?
В gcc для этих целей есть __attribute__((warn_unused_result)). Но что делать пользователям msvc? (забить на msvc и перейти на gcc не предлагать
)
Есть такое
решениеАвтор: Sm0ke
Дата: 28.02.06
, но оно вносит некоторый оверхед в рантайм, плюс невозможно найти ошибку, пока код с ошибкой не отработает.
Ещё есть такое интересное
решениеАвтор: elcste
Дата: 24.02.06
, но оно работает только для свободных функций, что сводит его не нет.
Вначале пример использования. Допустим есть такая функция, для которой надо форсировать проверку:
bool fff(int param)
{
return 0 == param;
}
Для форсирования проверки придётся написать вот так:
template<int id>
checked_result<id> fff_wrap(int param, RESULT_CHECK_PARAM)
{
return 0 == param;
}
Как работает:
int main()
{
bool b1 = fff(1, ID); // Компилируется
ignore_result(fff(2, ID)); // Компилируется
if (fff(2, ID)); // Компилируется
fff(1, ID); // НЕ Компилируется! error C2027: use of undefined type 'boost::STATIC_ASSERTION_FAILURE<x>'
}
К сожалению параметр RESULT_CHECK_PARAM — необходимость — сейчас будет видно. Собственно исходный код:
// Стратегии сообщения об ошибке (куда сейчас без стратегий? :) )
// На выбор - ошибка компиляции, варнинг, ошибка без использования boost (специально для нелюбителей boost'а :) )
struct error_report_policy
{
template<typename> static void report()
{
BOOST_STATIC_ASSERT(false);
}
};
struct error_report_no_boost_policy
{
template<typename> static void report()
{
typedef char error[-1];
}
};
struct warning_report_policy
{
template<typename> static void report()
{
BOOST_STATIC_WARNING(false);
}
};
namespace
{
// Вспомогательный класс - ниже будет видно зачем
template<int id>
struct result_check_helper
{
static void fake() {}
};
}
// Собственно класс для возвращаемого значения
// id - вспомогательный параметр
// type - тип возвращаемого значения - bool, int, HRESULT и т.д.
// report_policy - одна из вышеприведённых стратегий информирования об ошибке
template<int id, typename type = bool, typename report_policy = error_report_policy>
class checked_result
{
public:
checked_result(type value)
: value_(value)
{}
operator type ()
{
// Обращаемся к классу result_check_helper<id>
// ниже будет видно зачем
result_check_helper<id>::fake();
return value_;
}
~checked_result()
{
// ЗДЕСЬ!
// Определяем есть ли класс result_check_helper<id>,
// что фактически равнозначно тому, вызывается operator type () или нет!
__if_not_exists (result_check_helper<id>)
{
report_policy::report<void>();
}
}
private:
type value_;
};
// Вспомогательный класс для передачи уникальных идентификаторов вызова
template<int id> struct long_and_inconvenient_name {static const int id = id;};
// Вспомотельный макрос для генерации уникальных идентификаторов вызова
// В реальности надо какое-то более длинное имя, чем ID, но здесь для простоты так
#define ID long_and_inconvenient_name<__COUNTER__>()
// Вспомогательный макрос
#define RESULT_CHECK_PARAM long_and_inconvenient_name<id>
// Функции для явного игнорирования возвращаемого значения
inline void ignore_result(bool) {}
inline void ignore_result(int) {}
//...
Работает на msvc71/80. В рантайм оверхед нулевой.