[Trick] Форсирование проверки возвращаемого значения
От: remark Россия http://www.1024cores.net/
Дата: 24.12.06 16:19
Оценка: 40 (10) -1

Свершилось! Форсирование проверки возвращаемого значения в компайл-тайм возможно!

В начале несколько вводных слов, что б было понятно о чём речь:

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. В рантайм оверхед нулевой.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.