K
K
Kalombyr2018-08-26 18:53:09
Qt
Kalombyr, 2018-08-26 18:53:09

What is the most simple and correct way to organize behavior similar to a “transaction” (or ctrl-z) for class properties?

Good afternoon!
There is a certain class, with Q_PROPERTY(....)
From UI values ​​are set to it.
After installation, I call the transaction confirmation method.
When setting values, it is possible that you need to undo all changes made and return to the previous values ​​of all properties (at the moment when the previous transaction was confirmed).
So far, when I confirm the transaction, I enter the values ​​​​of all properties into the array,
when canceled, I restore accordingly from there.
What does not suit you is that you need to manually register each property for each class in which such behavior is needed, which is somewhat tiring, and has already encountered a copy-paste error a couple of times.
Ideally, the solution to the problem would be to inherit from some class, where the functions of confirming and canceling the transaction would be implemented, so that all properties would be remembered or restored.
Tell me, please, where to dig and how to implement?

Answer the question

In order to leave comments, you need to log in

2 answer(s)
A
al_sh, 2018-08-27
@al_sh

As far as I understand you are using Qt. In this case, do not cycle doc.qt.io/qt-5/qtwidgets-tools-undoframework-examp...

M
Maxim Moseychuk, 2018-08-26
@fshp

Put in the array not all the members of the object, but the object itself. And here an array is not needed, a stack is ideal here.
Here I gave you an example https://ideone.com/tOXA0S

Example
#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;
}

But you need to watch the implementation of the copy constructor and assignment operator so that they behave correctly.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question