Очищено пример @Deleet
from collections import Iterable
def flatten(l, a=[]):
for i in l:
if isinstance(i, Iterable):
flatten(i, a)
else:
a.append(i)
return a
daList = [[1,4],[5,6],[23,22,234,2],[2], [ [[1,2],[1,2]],[[11,2],[11,22]] ] ]
print(flatten(daList))
Пример: https://repl.it/G8mb/0
Boost.ScopeExit - это макрос, который должен работать с кодом не C ++ 11, то есть кодом, который не имеет доступа к lambdas на языке. Он использует некоторые умные шаблонные хаки (например, злоупотребляет двусмысленностью, возникающей из-за использования <
как для шаблонов, так и для операторов сравнения!) И препроцессора для эмуляции лямбда-функций. Вот почему код длиннее.
Показанный код также является ошибкой (что, вероятно, является самой сильной причиной для использования существующего решения): он вызывает неопределенное поведение из-за возврата ссылок на временные файлы.
Поскольку вы пытаетесь использовать возможности C ++ 11, код можно было бы значительно улучшить, используя семантику перемещения, ссылки на rvalue и совершенную пересылку:
template< typename Lambda >
class ScopeGuard
{
bool committed; // not mutable
Lambda rollbackLambda;
public:
// make sure this is not a copy ctor
template <typename L,
DisableIf<std::is_same<RemoveReference<RemoveCv<L>>, ScopeGuard<Lambda>>> =_
>
/* see http://loungecpp.net/w/EnableIf_in_C%2B%2B11
* and http://stackoverflow.com/q/10180552/46642 for info on DisableIf
*/
explicit ScopeGuard(L&& _l)
// explicit, unless you want implicit conversions from *everything*
: committed(false)
, rollbackLambda(std::forward<L>(_l)) // avoid copying unless necessary
{}
template< typename AdquireLambda, typename L >
ScopeGuard( AdquireLambda&& _al , L&& _l) : committed(false) , rollbackLambda(std::forward<L>(_l))
{
std::forward<AdquireLambda>(_al)(); // just in case the functor has &&-qualified operator()
}
// move constructor
ScopeGuard(ScopeGuard&& that)
: committed(that.committed)
, rollbackLambda(std::move(that.rollbackLambda)) {
that.committed = true;
}
~ScopeGuard()
{
if (!committed)
rollbackLambda(); // what if this throws?
}
void commit() { committed = true; } // no need for const
};
template< typename aLambda , typename rLambda>
ScopeGuard< rLambda > // return by value is the preferred C++11 way.
makeScopeGuard( aLambda&& _a , rLambda&& _r) // again perfect forwarding
{
return ScopeGuard< rLambda >( std::forward<aLambda>(_a) , std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
}
template<typename rLambda>
ScopeGuard< rLambda > makeScopeGuard(rLambda&& _r)
{
return ScopeGuard< rLambda >( std::forward<rLambda>(_r ));
}
Вам может быть интересно увидеть это презентацию самим Андреем самостоятельно, как улучшить scopedguard с помощью c ++ 11
Вы уже выбрали ответ, но я все равно возьму вызов:
#include <iostream>
#include <type_traits>
#include <utility>
template < typename RollbackLambda >
class ScopeGuard;
template < typename RollbackLambda >
auto make_ScopeGuard( RollbackLambda &&r ) -> ScopeGuard<typename
std::decay<RollbackLambda>::type>;
template < typename RollbackLambda >
class ScopeGuard
{
// The input may have any of: cv-qualifiers, l-value reference, or both;
// so I don't do an exact template match. I want the return to be just
// "ScopeGuard," but I can't figure it out right now, so I'll make every
// version a friend.
template < typename AnyRollbackLambda >
friend
auto make_ScopeGuard( AnyRollbackLambda && ) -> ScopeGuard<typename
std::decay<AnyRollbackLambda>::type>;
public:
using lambda_type = RollbackLambda;
private:
// Keep the lambda, of course, and if you really need it at the end
bool committed;
lambda_type rollback;
// Keep the main constructor private so regular creation goes through the
// external function.
explicit ScopeGuard( lambda_type rollback_action )
: committed{ false }, rollback{ std::move(rollback_action) }
{}
public:
// Do allow moves
ScopeGuard( ScopeGuard &&that )
: committed{ that.committed }, rollback{ std::move(that.rollback) }
{ that.committed = true; }
ScopeGuard( ScopeGuard const & ) = delete;
// Cancel the roll-back from being called.
void commit() { committed = true; }
// The magic happens in the destructor.
// (Too bad that there's still no way, AFAIK, to reliably check if you're
// already in exception-caused stack unwinding. For now, we just hope the
// roll-back doesn't throw.)
~ScopeGuard() { if (not committed) rollback(); }
};
template < typename RollbackLambda >
auto make_ScopeGuard( RollbackLambda &&r ) -> ScopeGuard<typename
std::decay<RollbackLambda>::type>
{
using std::forward;
return ScopeGuard<typename std::decay<RollbackLambda>::type>{
forward<RollbackLambda>(r) };
}
template < typename ActionLambda, typename RollbackLambda >
auto make_ScopeGuard( ActionLambda && a, RollbackLambda &&r, bool
roll_back_if_action_throws ) -> ScopeGuard<typename
std::decay<RollbackLambda>::type>
{
using std::forward;
if ( not roll_back_if_action_throws ) forward<ActionLambda>(a)();
auto result = make_ScopeGuard( forward<RollbackLambda>(r) );
if ( roll_back_if_action_throws ) forward<ActionLambda>(a)();
return result;
}
int main()
{
auto aa = make_ScopeGuard( []{std::cout << "Woah" << '\n';} );
int b = 1;
try {
auto bb = make_ScopeGuard( [&]{b *= 2; throw b;}, [&]{b = 0;}, true );
} catch (...) {}
std::cout << b++ << '\n';
try {
auto bb = make_ScopeGuard( [&]{b *= 2; throw b;}, [&]{b = 0;}, false );
} catch (...) {}
std::cout << b++ << '\n';
return 0;
}
// Should write: "0", "2", and "Woah" in that order on separate lines.
Вместо того, чтобы создавать функции создания и конструктор, вы ограничиваетесь только функциями создания, причем основным конструктором является private
. Я не мог понять, как ограничить экземпляры friend
-ed только теми, которые связаны с текущим параметром шаблона. (Возможно, потому, что параметр упоминается только в возвращаемом типе.) Возможно, на этом сайте может быть исправлено исправление. Поскольку первое действие не нужно хранить, оно присутствует только в функциях создания. Существует флаг Boolean, если флаг throw
от первого действия вызывает возврат или нет.
Часть std::decay
разделяет как cv-квалификаторы, так и ссылочные маркеры. Но вы не можете использовать его для этой общей цели, если тип ввода является встроенным массивом, так как он будет применять преобразование между массивами и указателями.
Без отслеживания обязательств, но очень аккуратно и быстро.
template <typename F>
struct ScopeExit {
ScopeExit(F&& f) : m_f(std::forward<F>(f)) {}
~ScopeExit() { m_f(); }
F m_f;
};
template <typename F>
ScopeExit<F> makeScopeExit(F&& f) {
return ScopeExit<F>(std::forward<F>(f));
};
#define STRING_JOIN(arg1, arg2) STRING_JOIN2(arg1, arg2)
#define STRING_JOIN2(arg1, arg2) arg1 ## arg2
#define ON_SCOPE_EXIT(code) auto STRING_JOIN(scopeExit, __LINE__) = makeScopeExit([&](){code;})
Использование
{
puts("a");
auto _ = makeScopeExit([]() { puts("b"); });
// More readable with a macro
ON_SCOPE_EXIT(puts("c"));
} # prints a, c, b
Вероятность того, что этот подход будет стандартизован в C ++ 17 или в Основах Библиотеки TS посредством предложения P0052R0
template <typename EF>
scope_exit<see below> make_scope_exit(EF &&exit_function) noexcept;
template <typename EF>
scope_exit<see below> make_scope_fail(EF && exit_function) noexcept;
template <typename EF>
scope_exit<see below> make_scope_success(EF && exit_function) noexcept;
На первый взгляд это имеет одинаковую оговорку как std::async
, потому что вам нужно сохранить возвращаемое значение, или деструктор будет вызван немедленно, и он не будет работать должным образом.
Еще короче: я не знаю, почему вы, ребята, настаиваете на том, чтобы поместить шаблон в класс охраны.
#include <functional>
class scope_guard {
public:
template<class Callable>
scope_guard(Callable && undo_func) try : f(std::forward<Callable>(undo_func)) {
} catch(...) {
undo_func();
throw;
}
scope_guard(scope_guard && other) : f(std::move(other.f)) {
other.f = nullptr;
}
~scope_guard() {
if(f) f(); // must not throw
}
void dismiss() noexcept {
f = nullptr;
}
scope_guard(const scope_guard&) = delete;
void operator = (const scope_guard&) = delete;
private:
std::function<void()> f;
};
Обратите внимание, что код очистки не бросается, иначе вы получаете в том же случае, что и при метании деструкторов.
Использование:
// do step 1
step1();
scope_guard guard1 = [&]() {
// revert step 1
revert1();
};
// step 2
step2();
guard1.dismiss();
Мое вдохновение было тем же статьей DrDobbs , что и для OP.
Редактировать 2017/2018: После просмотра (некоторые из) презентации Андрея , с которой связан Андре (я пропустил до конца, где он сказал «Больно близко к идеалу!»). Я понял, что это выполнимо. Большую часть времени вы не хотите иметь дополнительных охранников для всего. Вы просто делаете что-то, и, в конце концов, это либо преуспевает, либо должен произойти откат.
Изменить 2018: добавлена политика выполнения, которая устраняет необходимость вызова dismiss
.
#include <functional>
#include <deque>
class scope_guard {
public:
enum execution { always, no_exception, exception };
scope_guard(scope_guard &&) = default;
explicit scope_guard(execution policy = always) : policy(policy) {}
template<class Callable>
scope_guard(Callable && func, execution policy = always) : policy(policy) {
this->operator += <Callable>(std::forward<Callable>(func));
}
template<class Callable>
scope_guard& operator += (Callable && func) try {
handlers.emplace_front(std::forward<Callable>(func));
return *this;
} catch(...) {
if(policy != no_exception) func();
throw;
}
~scope_guard() {
if(policy == always || (std::uncaught_exception() == (policy == exception))) {
for(auto &f : handlers) try {
f(); // must not throw
} catch(...) { /* std::terminate(); ? */ }
}
}
void dismiss() noexcept {
handlers.clear();
}
private:
scope_guard(const scope_guard&) = delete;
void operator = (const scope_guard&) = delete;
std::deque<std::function<void()>> handlers;
execution policy = always;
};
Использование:
scope_guard scope_exit, scope_fail(scope_guard::execution::exception);
action1();
scope_exit += [](){ cleanup1(); };
scope_fail += [](){ rollback1(); };
action2();
scope_exit += [](){ cleanup2(); };
scope_fail += [](){ rollback2(); };
// ...
makeScopeGuard возвращает ссылку на константу. Вы не можете сохранить эту константную ссылку в const ref со стороны вызывающего абонента в строке, подобной:
const auto& a = RAII::makeScopeGuard( [&]() { myVec.pop_back(); } );
. Таким образом, вы вызываете неопределенное поведение.
Herb Sutter GOTW 88 дает некоторое представление о сохранении значений в ссылках на const.
Вы можете использовать std::unique_ptr
для этой цели, которая реализует шаблон RAII. Например:
vector<int> v{};
v.push_back(42);
unique_ptr<decltype(v), function<void(decltype(v)*)>>
p{&v, [] (decltype(v)* v) { if (uncaught_exception()) { v->pop_back(); }}};
throw exception(); // rollback
p.release(); // explicit commit
Функция удаления из unique_ptr p
возвращает ранее введенное значение, если область была оставлена при активном исключении. Если вы предпочитаете явное коммит, вы можете удалить вопрос uncaugth_exception()
в функции делетера и добавить в конце блока p.release()
, который освобождает указатель. См. Демо здесь.
Еще один ответ, но, боюсь, я нахожу других, которых так или иначе не хватает. Примечательно, что принятый ответ датируется 2012 годом, но он имеет важную ошибку (см. этот комментарий ). Это показывает важность тестирования.
Здесь представляет собой реализацию a> = C ++ 11 scope_guard, которая открыта и широко протестирована. Он предназначен для / иметь:
std::function
или виртуальных таблиц] См. Также полный список функций .
Я использую это как шарм, без дополнительного кода.
shared_ptr<int> x(NULL, [&](int *) { CloseResource(); });
Вот еще один, теперь вариант на @ kwarnke's:
std::vector< int > v{ };
v.push_back( 42 );
auto guard_handler =
[ & v ] ( nullptr_t ptr )
{
v.pop_back( );
};
std::shared_ptr< decltype( guard_handler ) > guard( nullptr , std::move( guard_handler ) );