2013-11-03 8 views
0

При продвижении состояния объекта использование std::swap хорошо работает для простых объектов и свопов указателей. Для других действий на месте, Boost.ScopeExit работает довольно хорошо, но это не очень элегантно, если вы хотите поделиться обработчиками выходных данных по всем функциям. Есть ли собственный способ C++ 11 для достижения чего-то подобного Boost.ScopeExit, но для лучшего использования кода?Использование std :: unique_ptr и lambdas для продвижения состояния объекта

ответ

0

(Ab) использовать пользовательские Deleters std::unique_ptr как ScopeExitVisitor или состояние сообщения. Прокрутите вниз до ~ 7-й строки main(), чтобы узнать, как это фактически используется на сайте вызова. В следующем примере можно использовать либо std::function, либо lambdas для Deleter/ScopeExitVisitor, для которых не требуются какие-либо параметры, и вложенный класс, если вам необходимо передать параметр в Deleter/ScopeExitVisitor.

#include <iostream> 
#include <memory> 

class A { 
public: 
    using Type = A; 
    using Ptr = Type*; 
    using ScopeExitVisitorFunc = std::function<void(Ptr)>; 
    using ScopeExitVisitor = std::unique_ptr<Type, ScopeExitVisitorFunc>; 

    // Deleters that can change A's private members. Note: Even though these 
    // are used as std::unique_ptr<> Deleters, these Deleters don't delete 
    // since they are merely visitors and the unique_ptr calling this Deleter 
    // doesn't actually own the object (hence the label ScopeExitVisitor). 
    static void ScopeExitVisitorVar1(Ptr aPtr) { 
    std::cout << "Mutating " << aPtr << ".var1. Before: " << aPtr->var1; 
    ++aPtr->var1; 
    std::cout << ", after: " << aPtr->var1 << "\n"; 
    } 

    // ScopeExitVisitor accessing var2_, a private member. 
    static void ScopeExitVisitorVar2(Ptr aPtr) { 
    std::cout << "Mutating " << aPtr << ".var2. Before: " << aPtr->var2_; 
    ++aPtr->var2_; 
    std::cout << ", after: " << aPtr->var2_ << "\n"; 
    } 

    int var1 = 10; 
    int var2() const { return var2_; } 

    // Forward declare a class used as a closure to forward Deleter parameters 
    class ScopeExitVisitorParamVar2; 

private: 
    int var2_ = 20; 
}; 

// Define ScopeExitVisitor closure. Note: closures nested inside of class A 
// still have access to private variables contained inside of A. 
class A::ScopeExitVisitorParamVar2 { 
public: 
    ScopeExitVisitorParamVar2(int incr) : incr_{incr} {} 
    void operator()(Ptr aPtr) { 
    std::cout << "Mutating " << aPtr << ".var2 by " << incr_ << ". Before: " << aPtr->var2_; 
    aPtr->var2_ += incr_; 
    std::cout << ", after: " << aPtr->var2_ << "\n"; 
    } 

private: 
    int incr_ = 0; 
}; 

// Can also use lambdas, but in this case, you can't access private 
// variables. 
// 
static auto changeStateVar1Handler = [](A::Ptr aPtr) { 
    std::cout << "Mutating " << aPtr << ".var1 " << aPtr->var1 << " before\n"; 
    aPtr->var1 += 2; 
}; 

int main() { 
    A a; 

    std::cout << "a: " << &a << "\n"; 

    std::cout << "a.var1: " << a.var1 << "\n"; 
    std::cout << "a.var2: " << a.var2() << "\n"; 

    { // Limit scope of the unique_ptr handlers. The stack is unwound in 
    // reverse order (i.e. Deleter var2 is executed before var1's Deleter). 
    A::ScopeExitVisitor scopeExitVisitorVar1(nullptr, A::ScopeExitVisitorVar1); 
    A::ScopeExitVisitor scopeExitVisitorVar1Lambda(&a, changeStateVar1Handler); 
    A::ScopeExitVisitor scopeExitVisitorVar2(&a, A::ScopeExitVisitorVar2); 
    A::ScopeExitVisitor scopeExitVisitorVar2Param(nullptr, A::ScopeExitVisitorParamVar2(5)); 

    // Based on the control of a function and required set of ScopeExitVisitors that 
    // need to fire use release() or reset() to control which visitors are used. 
    // Imagine unwinding a failed but complex API call. 
    scopeExitVisitorVar1.reset(&a); 
    scopeExitVisitorVar2.release(); // Initialized in ctor. Use release() before reset(). 
    scopeExitVisitorVar2.reset(&a); 
    scopeExitVisitorVar2Param.reset(&a); 

    std::cout << "a.var1: " << a.var1 << "\n"; 
    std::cout << "a.var2: " << a.var2() << "\n"; 
    std::cout << "a.var2: " << a.var2() << "\n"; 
    } 

    std::cout << "a.var1: " << a.var1 << "\n"; 
    std::cout << "a.var2: " << a.var2() << "\n"; 
} 

Который производит:

a: 0x7fff5ebfc280 
a.var1: 10 
a.var2: 20 
a.var1: 10 
a.var2: 20 
a.var2: 20 
Mutating 0x7fff5ebfc280.var2 by 5. Before: 20, after: 25 
Mutating 0x7fff5ebfc280.var2. Before: 25, after: 26 
Mutating 0x7fff5ebfc280.var1 10 before 
Mutating 0x7fff5ebfc280.var1. Before: 12, after: 13 
a.var1: 13 
a.var2: 26 

С положительной стороны, этот прием хорош, потому что:

  • Код, используемый в удаливших может получить доступ к закрытым переменным
  • Deleter код может быть централизованно
  • Использование lambdas по-прежнему возможно, хотя они могут получить доступ только к лобковым членам.
  • Параметры могут быть переданы в Deleter с помощью вложенных классов, действующих как замыкания
  • Не все std::unique_ptr экземпляры должны иметь объект, возложенные на них (например, это вполне приемлемо, чтобы оставить ненужные удаливших, установленные в nullptr)
  • Изменение поведения в среда это просто вопрос вызова reset() или release()
  • Основываясь на том, как вы построить свой стек это возможно во время компиляции, чтобы изменить гарантии безопасности на объекте, когда объем std::unique_ptr (ов) выходит из области видимости

И наконец, используя Boost.ScopeExit, вы можете переадресовывать вызовы в вспомогательную функцию или использовать условное значение, аналогичное тому, которое предлагает Boost.ScopeExit с bool commit = ...;. Нечто похожее:

#include <iostream> 
#include <boost/scope_exit.hpp> 

int main() { 
    bool commitVar1 = false; 
    bool commitVar2 = false; 
    BOOST_SCOPE_EXIT_ALL(&) { 
    if (commitVar1) 
     std::cout << "Committing var1\n" 
    if (commitVar2) 
     std::cout << "Committing var2\n" 
    }; 
    commitVar1 = true; 
} 

и нет ничего плохого в том, что, но, как было предложено в оригинальный вопрос, как вы разделяете код без проксировании на вызов где-то еще? Используйте std::unique_ptr 'Deleters' как ScopeExitVisitors.