[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
Re: [Trick] Форсирование проверки возвращаемого значения
От: _Obelisk_ Россия http://www.ibm.com
Дата: 24.12.06 17:59
Оценка:
Здравствуйте, remark, Вы писали:

R>

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

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

Супер! Только вопрос — зачем?
Если программист хочет игнорировать возвращаемое значение — пущай игнорирует.



Душа обязана трудиться! (с) Н.Заболоцкий.
Re: [Trick] Форсирование проверки возвращаемого значения
От: IROV..  
Дата: 24.12.06 20:32
Оценка: :)
Здравствуйте, remark, Вы писали:

...

fff(1, ID /* - фээ некрасиво */ );


я не волшебник, я только учусь!
Re: [Trick] Форсирование проверки возвращаемого значения
От: IROV..  
Дата: 24.12.06 21:10
Оценка: 1 (1) :)
Здравствуйте, remark, Вы писали:

...


struct ignore_result_t {};

#    define ignore_result ( ignore_result_t() )

class checked_result
{
  ...
    void operator () ( ignore_result_t )
    {
        result_check_helper<id>::fake();
    }
  ...
}


int main()
{
    fff(2, ID) ignore_result; // Компилируется
}


Могу еще предложить вот такой забавный трюк
я не волшебник, я только учусь!
Re: [Trick] Форсирование проверки возвращаемого значения
От: johny5 Новая Зеландия
Дата: 25.12.06 03:10
Оценка:
Здравствуйте, remark, Вы писали:



R>

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

Ну во первых нужно было прям перед заголовком вот так написать:

Microsoft specific

Я может дальше и читать бы не стал


R>
R>template<int id>
R>checked_result<id> fff_wrap(int param, RESULT_CHECK_PARAM)
R>{
R>    return 0 == param;
R>}
R>


R>Как работает:


R>
R>int main()
R>{
R>    bool b1 = fff(1, ID); // Компилируется
R>    ignore_result(fff(2, ID)); // Компилируется
R>    if (fff(2, ID)); // Компилируется
R>    fff(1, ID); // НЕ Компилируется! error C2027: use of undefined type 'boost::STATIC_ASSERTION_FAILURE<x>'
R>}
R>


Тут наверно таки вызов fff_wrap?



R>

   [cut]

R>



Я так понимаю, при каждом вызове такой функции, будет инстанцироваться result_check_helper<int id>::fake()
У линкера башню то не порвёт?
Re[2]: [Trick] Форсирование проверки возвращаемого значения
От: remark Россия http://www.1024cores.net/
Дата: 25.12.06 08:09
Оценка:
Здравствуйте, _Obelisk_, Вы писали:

_O_>Здравствуйте, remark, Вы писали:


R>>

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

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

_O_>Супер! Только вопрос — зачем?

_O_>Если программист хочет игнорировать возвращаемое значение — пущай игнорирует.

Я думаю, это одна из тех вещей, которые не объяснить. Те, кто поняли зачем, тем понятно, а те кто не поняли, тем не объяснить.
Ну это типа как "А зачем программировать на С++, когда есть С?" или "Зачем использовать исключения, когда есть коды возврата?"


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: [Trick] Форсирование проверки возвращаемого значения
От: remark Россия http://www.1024cores.net/
Дата: 25.12.06 08:14
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>Здравствуйте, remark, Вы писали:


IRO>...


IRO>
IRO>fff(1, ID /* - фээ некрасиво */ ); 
IRO>


IRO>


Согласен.
Но во-первых, надо с чего-то начинать. Это первый рабочий вариант, который я видел (который работает и для методов). На rsdn точно ничего аналогичного нет, в других местах тоже не видел. В Overload ещё не так давно кто-то писал про вариант типа здесь
Автор: Sm0ke
Дата: 28.02.06
. Далее надо улучшать. Для чего собственно я это и выложил на всеобщее рассмотрение.
Во-вторых, это красивее:
func_with_very_important_result(1);





1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: [Trick] Форсирование проверки возвращаемого значения
От: remark Россия http://www.1024cores.net/
Дата: 25.12.06 08:18
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>Могу еще предложить вот такой забавный трюк


Не вижу сильного различия между:
fff(2, ID) ignore_result;

и:
ignore_result(fff(2, ID));}


что бы для этого вводить ещё макросы.

А в таком варианте это будет хуже, т.к. будет не видно:

some_func_with_long_name(/*много параметров*/, ID) /* за границей экрана*/ ignore_result;


Тут есть более серьёъные проблемы (основная — доп. параметр), которые бы хотелось решить. Я потом подробнее напишу.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: [Trick] Форсирование проверки возвращаемого значения
От: remark Россия http://www.1024cores.net/
Дата: 25.12.06 08:21
Оценка:
Здравствуйте, johny5, Вы писали:

J>Ну во первых нужно было прям перед заголовком вот так написать:

J>Microsoft specific
J>Я может дальше и читать бы не стал

Читай внимательнее:

В gcc для этих целей есть __attribute__((warn_unused_result)). Но что делать пользователям msvc?






J>Тут наверно таки вызов fff_wrap?


Да, конечно, описался...



J>Я так понимаю, при каждом вызове такой функции, будет инстанцироваться result_check_helper<int id>::fake()

J>У линкера башню то не порвёт?
J>

Нет. Тем более, что символ с внутренним связыванием.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: [Trick] Форсирование проверки возвращаемого значения
От: night beast СССР  
Дата: 25.12.06 08:28
Оценка:
Здравствуйте, remark, Вы писали:

R>

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

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

R>Работает на msvc71/80. В рантайм оверхед нулевой.


R>


может стоит ID сделать первым параментром, на случай если у функции есть параметры по умолчанию?

PS: а что, действительно большая проблема в проверкой?
Re[2]: [Trick] Форсирование проверки возвращаемого значения
От: remark Россия http://www.1024cores.net/
Дата: 25.12.06 08:34
Оценка: +1 :)
Здравствуйте, night beast, Вы писали:

NB>Здравствуйте, remark, Вы писали:


R>>

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

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

R>>Работает на msvc71/80. В рантайм оверхед нулевой.


R>>


NB>может стоит ID сделать первым параментром, на случай если у функции есть параметры по умолчанию?


Это можно варьировать по желанию для каждой функции.
В данном случае просто хотелось более показать сходство с оригинальной функцией.

NB>PS: а что, действительно большая проблема в проверкой?


С проверкой — нет, а вот с не проверкой — да

Ты не видишь в следующем коде проблемы?

CopyFile(lpExistingFileName, lpNewFileName, FALSE);


Программист по определению ленивый. Если что-то можно не сделать, то рано или поздно (например в 1 ночи ) он это обязательно не сделает.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: [Trick] Форсирование проверки возвращаемого значения
От: IROV..  
Дата: 25.12.06 10:10
Оценка:
Здравствуйте, remark, Вы писали:

R>Не вижу сильного различия между:

R>
R>fff(2, ID) ignore_result; 
R>

R>и:
R>
R>ignore_result(fff(2, ID));}
R>


вся фишка в этом

Могу еще предложить вот такой _забавный_ трюк


я не волшебник, я только учусь!
Re: [Trick] Форсирование проверки возвращаемого значения
От: Antipov  
Дата: 25.12.06 11:09
Оценка:
Здравствуйте, remark, Вы писали:

R>

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

R>Работает на msvc71/80. В рантайм оверхед нулевой.


немного добавим для демонстрации:

template<int id> checked_result<id> fff_wrap(int param, RESULT_CHECK_PARAM)
{
    static bool first_call(true);

    if(first_call)
    {
        first_call    = false;
        std::cout << "first call " << __FUNCTION__ << std::endl;
    }
    else
    {
        std::cout << "second call " << __FUNCTION__ << std::endl;
    }
    return 0 == param;
}

int _tmain(int argc, _TCHAR* argv[])
{
    //bool b1 = fff_wrap(1, ID); // Компилируется
    
    if (fff_wrap(2, ID)); // Компилируется
    if (fff_wrap(2, ID)); // Компилируется
    std::cout << std::endl;

    for(int i = 0; i < 2; ++i)
    {
        if (fff_wrap(2, ID)); // Компилируется
    }
    _getch();
    return 0;
}


Как вы думайте сколько реализаций функции fff_wrap в бинарнике ?
Вряд ли такое поведение функции ожидается, а отказ от статических переменных в функции ИМХО слишком суров.
Re[4]: [Trick] Форсирование проверки возвращаемого значения
От: remark Россия http://www.1024cores.net/
Дата: 25.12.06 11:14
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>вся фишка в этом


IRO>

IRO>Могу еще предложить вот такой _забавный_ трюк


Забавные трюки бывают разные, например, CRTP тоже можно назвать забавным...

IRO>


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: [Trick] Форсирование проверки возвращаемого значения
От: remark Россия http://www.1024cores.net/
Дата: 25.12.06 11:24
Оценка:
Здравствуйте, Antipov, Вы писали:

A>Как вы думайте сколько реализаций функции fff_wrap в бинарнике ?

A>Вряд ли такое поведение функции ожидается, а отказ от статических переменных в функции ИМХО слишком суров.


Ну вообще использоваться это должно примерно так:

template<int id> checked_result<id> fff_wrap(int param, RESULT_CHECK_PARAM)
{
  return fff(param);
}


Т.к. плохо только из-за этого вытаскивать всю реализацию в h'ник
Т.ч. проблем не будет.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: [Trick] Форсирование проверки возвращаемого значения
От: _Obelisk_ Россия http://www.ibm.com
Дата: 25.12.06 13:15
Оценка:
Здравствуйте, remark, Вы писали:


R>Я думаю, это одна из тех вещей, которые не объяснить. Те, кто поняли зачем, тем понятно, а те кто не поняли, тем не объяснить.

R>Ну это типа как "А зачем программировать на С++, когда есть С?" или "Зачем использовать исключения, когда есть коды возврата?"

На эти два вопросы имеются ответы, а вот необходимости в compile-time проверки учета возвращаемого значения не вижу.



Душа обязана трудиться! (с) Н.Заболоцкий.
Re[4]: [Trick] Форсирование проверки возвращаемого значения
От: remark Россия http://www.1024cores.net/
Дата: 25.12.06 13:32
Оценка:
Здравствуйте, _Obelisk_, Вы писали:

_O_>Здравствуйте, remark, Вы писали:



R>>Я думаю, это одна из тех вещей, которые не объяснить. Те, кто поняли зачем, тем понятно, а те кто не поняли, тем не объяснить.

R>>Ну это типа как "А зачем программировать на С++, когда есть С?" или "Зачем использовать исключения, когда есть коды возврата?"

_O_>На эти два вопросы имеются ответы, а вот необходимости в compile-time проверки учета возвращаемого значения не вижу.


Без форсирования:
1. Неправильный код написать легче, чем правильный
2. Не явны намерения программиста: он забыл проверить или хотел это сделать?
3. Код менее читабельный: f() — а эта функция что-то ещё и возвращает?

Форсирование решает все эти проблемы.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: [Trick] Форсирование проверки возвращаемого значения
От: _Winnie Россия C++.freerun
Дата: 25.12.06 13:55
Оценка: +1
Здравствуйте, remark, Вы писали:

Прикольно, но ИМХО неудобства написания шаблонного враппера к каждому нешаблонному методу здесь слишком большие. И это не удовлетворяет более ранним требования — "есть дофига старого кода, хочу проверить ошибки там" — нужен параметр ID.
Можно конечно макросы ещё написать — #define Foo(x) Foo(x, ID), но это всё более и более уход в Темную Сторону.

Я бы выбрал или runtime-проверку с тестрованием, или трюк с двумя проходами компиляции — http://www.rsdn.ru/Forum/?mid=1698165&amp;flat=0
Автор: Erop
Дата: 25.02.06

или code guide с написанием скрипта — "эту функцию можно вызывать только так — ^bool :i = foo\(.*\);$ и как if \(.*foo" (это не реальные регэкспы а просто как начинать их писать)
Можно даже скрипт не писать, а сразу Edit->Find In Files. И заграть в макрокнопку студии.
Правильно работающая программа — просто частный случай Undefined Behavior
Re: [Trick] Форсирование проверки возвращаемого значения
От: IROV..  
Дата: 25.12.06 14:23
Оценка: +1
Здравствуйте, remark, Вы писали:

R>

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


как по мне на лицо борьба с ветряными мельницами! а именно, ну а кто мне _леньтяю_ запретит писать вот такое..

if( foo( 1, 2 ) );


я не волшебник, я только учусь!
Re[2]: [Trick] Форсирование проверки возвращаемого значения
От: rg45 СССР  
Дата: 25.12.06 14:32
Оценка: +2
"IROV.." <35375@users.rsdn.ru> wrote in message news:2279806@news.rsdn.ru...
> Здравствуйте, remark, Вы писали:
>
> R>

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

>
> как по мне на лицо борьба с ветряными мельницами! а именно, ну а кто мне _леньтяю_ запретит писать вот такое..
>
>
> if( foo( 1, 2 ) );
>

>
>

Так в этом то и состоит цель этой затеи, чтоб заставить пользователя так писать. Таким образом пользователь как бы говорит: "я осознанно проигнорировал результат, всю ответственность беру на себя...".
Цель, вообще говоря, хорошая, но, имхо, не такой ценой как в предлагаемом варианте: один только макрос RESULT_CHECK_PARAM чего стоит.
Posted via RSDN NNTP Server 2.0
--
Не можешь достичь желаемого — пожелай достигнутого.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.