Kalombyr
@Kalombyr

Как наиболее просто и правильно организовать для свойств класса поведение, аналогичное «транзакции» (или ctrl-z)?

Добрый день!
Есть некий класс, с Q_PROPERTY(....)
Из UI задаются им значения.

После установки вызываю метод подтверждения транзакции.
При установке значений возможна ситуация, когда нужно отменить все сделанные изменения и вернуться к предыдущим значениям всех свойств (на момент, когда предыдущая транзакция была подтверждена).

Пока что я при подтверждении транзакции я заношу в массив значения всех свойств,
при отмене, соответственно восстанавливаю оттуда.

Что не устраивает - нужно каждое свойство прописывать руками для каждого класса, в котором нужно такое поведение, что несколько утомляет, да и уже пару раз столкнулся с ошибкой копи-паста.

В идеале решение проблемы было бы унаследоваться от какого-либо класса, где были бы реализованы функции подтверждения и отмены транзакции, что бы все свойства запоминались или восстанавливались.

Подскажите, пожалуйста, куда копать и как бы реализовать?
  • Вопрос задан
  • 104 просмотра
Пригласить эксперта
Ответы на вопрос 2
@al_sh
насколько я понял Вы используете Qt. В таком случае не надо велосипедить doc.qt.io/qt-5/qtwidgets-tools-undoframework-examp...
Ответ написан
Комментировать
Кладите в массив не все члены объекта, а сам объект. И тут не массив нужен, тут идеально стек подходит.

Вот я вам накидал примерчик https://ideone.com/tOXA0S

Пример
#include <iostream>
#include <stack>
#include <memory>

template<typename T>
class Undo : public T {
private:
    std::shared_ptr<std::stack<Undo>> history_ptr = std::make_shared<std::stack<Undo>>();

public:
    template <typename... Args>
    explicit Undo(const Args&... args): T(args ...) {}

    void save() {
        history_ptr->push(*this);
    }

    void undo() {
        if (!history_ptr->empty()) {
            *this = history_ptr->top();
            history_ptr->pop();
        }
    }
};

class Foo {
public:
    int a = 0;
    std::string s;

    Foo(const int _a, std::string _s): a(_a), s(std::move(_s)) {}
};

std::ostream &operator<<(std::ostream &out, const Foo &foo) {
    out << "Foo(" << foo.a << ", \"" << foo.s << "\")";
    return out;
}

int main() {
    Undo<Foo> foo(1, "Hello World!");

    foo.save();
    std::cout << "Save\t" << foo << std::endl;

    foo.a = 6661313;
    foo.save();
    std::cout << "Save\t" << foo << std::endl;

    foo.s = "Lakad Matataaag!";
    foo.save();
    std::cout << "Save\t" << foo << std::endl;

    foo.a = 9000;
    foo.s = "Normalin, Normalin!";
    foo.save();
    std::cout << "Save\t" << foo << std::endl;

    foo.a = 12345678;
    foo.s = "Da next lebel play!";
    std::cout << "Current\t" << foo << std::endl;

    foo.undo();
    std::cout << "Undo\t" << foo << std::endl;

    foo.undo();
    std::cout << "Undo\t" << foo << std::endl;

    foo.undo();
    std::cout << "Undo\t" << foo << std::endl;

    foo.undo();
    std::cout << "Undo\t" << foo << std::endl;

    return 0;
}


Но вам нужно следить за реализацией конструктора копирования и оператора присваивания, что бы они корректно себя вели.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы