пятница, 18 ноября 2016 г.

[prog.c++.bicycle] Пытаюсь наваять совсем простенькую функцию n_times

Когда-то уже заходила речь (кажется в G+), что хорошо было бы иметь в C++ функцию n_times для того, чтобы не нужно было циклы выписывать руками. Т.е. вместо того, чтобы писать:

const auto iterations = std::stoul(argv[1]);
forunsigned long i = 0; i != iterations; ++i )
   do_some_action();

А можно было бы изобразить что-то вроде:

n_times( std::stoul(argv[1]), []{ do_some_action(); } );

Ну и в итоге дозрел до того, чтобы добавить n_times в нашу маленькую библиотечку cpp_util. Самый первый, черновой вариант, выглядит вот так:

templatetypename COUNT_TYPE, typename LAMBDA >
void
n_times( COUNT_TYPE n, LAMBDA && body )
   {
      loops_details::ensure_non_negative( n );
      for( ; n; --n )
         body();
   }

В нем вспомогательная функция ensure_non_negative в случае, если COUNT_TYPE является знаковым типом, проверит, чтобы n имел неотрицательное значение. В случае же отрицательного значения будет порождено исключение std::invalid_argument.

И вот тут у меня есть сомнения. Ведь такая проверка может вести к дополнительным накладным расходам. В каких-то случаях компилятор ее выбросит. Но в каких-то оставит. А это может не понравится некоторой части потенциальных пользователей. Посему есть мысль пойти по такому варианту:

using namespace cpp_util_3;

// Пользователь не против получить исключение, если ошибся
// со значением аргумента.
n_times< checked_loop >( std::stol(argv[1]), []{ do_some_action(); } );

// Пользователь зуб дает, что все будет чики-пуки.
n_times< unchecked_loop >( std::stol(argv[1]), []{ do_some_action(); } );

Или по такому:

using namespace cpp_util_3;

// Пользователь не против получить исключение, если ошибся
// со значением аргумента.
n_times( untrusted_n(std::stol(argv[1])), []{ do_some_action(); } );

// Пользователь зуб дает, что все будет чики-пуки.
n_times( trusted_n(std::stol(argv[1])), []{ do_some_action(); } );

Интересно мнение читателей: вам бы какой вариант был бы более удобен? Вообще без проверок? С проверками всегда? С отключаемыми проверками?

При этом если n -- это беззнаковое значение, то оно не проверяется вообще. Тут в дело вступает совсем простая шаблонная магия, которая устраняет избыточные проверки прям в compile-time.

PS. Кроме n_times еще хочется сделать up_to (или for_every_i):

using namespace cpp_util_3;

for_every_i( 010, [](auto i) { std::cout << i << std::endl; } );

Тут так же можно подумать на счет checked_loop/unchecked_loop...

Комментариев нет: