Форум программистов, компьютерный форум CyberForum.ru

C++

Войти
Регистрация
Восстановить пароль
 
volovzi
267 / 169 / 8
Регистрация: 14.03.2010
Сообщений: 501
#1

Откатываемый (undoable) класс - C++

16.03.2010, 22:07. Просмотров 422. Ответов 6
Метки нет (Все метки)

Решил тут сделать универсальный интерфейс "откатываемости", который можно было бы прикрутить к любому классу. Идея в том, чтобы, унаследовав свой класс от некоторого класса Undoable, получить возможность сохранять, отменять и повторять изменения, произошедшие с классом. Ну, как undo/redo в любой программе.
Но смысл именно в универсальности, т.е. отменять действия не на уровне приложения, а на уровне каждого отдельного класса, которому такая функциональность необходима.

Первый вариант я сделал. Но результат пока что не тот, который хотелось бы видеть. Функциональность я реализовал, но для её получения мне необходимо наследование "наоборот", т.е. унаследовать не мой класс от класса Undoable, а Undoable от моего класса.
Это решение по-своему хорошо, но мне всё-таки хотелось поместить Undoable на вершину иерархии. Не подкинете идейку?

Исходный код прилагается.

Undoable.h

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/* Данный файл содержит описание класса Undoable, расширения, дающего возможность
любому классу запоминать и восстанавливать свои состояния. */
 
#include <vector>
#import "vectorServices.h"
 
template <class T>
class Undoable : public T {
    typedef std::vector<T *> TPointerArray;
    typedef typename TPointerArray::iterator TPointerArrayIterator;
 
public:
    Undoable () {}
    ~Undoable () { deleteVector(versions); }
    
    /* Сохраняет текущее состояние объекта. Если итератор "currentVersion"
    установлен не на конец массива копий состояний, то все последующие
    сохранённые состояния удаляются и в конец добавляется новое
    сохранённое состояние. */
    void save () {
        T * newObject = new T(*this);
    
        if (!versions.empty()) versions.erase(currentVersion + 1, versions.end());
        versions.push_back(newObject);
        currentVersion = versions.end() - 1;
    }
    /* Пытается откатить состояние объекта к предыдущему сохранённому.
    Если объект находится в своём начальном состоянии, т.е. в первом из
    сохранённых, то никаких изменений не производится и возвращается
    "false". В противном случае объект трансформируется в своё предыдущее
    сохранённое состояние, и возвращается "true". */
    bool undo () {
        if (currentVersion > versions.begin()) {
            T::operator=(**--currentVersion);
            return true;
        } else return false;
    }
    /* Пытается вернуть состояние объекта к следующему сохранённому.
    Если текущее сохранённое состояние является последним из сохранённых,
    то никаких изменений не производится и возвращается "false". В противном
    случае объект трансформируется в своё следующее сохранённое состояние,
    и возвращается "true". */
    bool redo () {
        if (currentVersion < versions.end() - 1) {
            T::operator=(**++currentVersion);
            return true;
        } else return false;
    }
    /* Удаляет все сохранённые состояния, кроме текущего. */
    void clearUndoHistory () {
        if (!versions.empty()) {
            currentVersion = versions.erase(versions.begin(), currentVersion);
            versions.erase(currentVersion + 1, versions.end());
            currentVersion = versions.begin();
        }
    }
 
private:
    TPointerArray versions;
    TPointerArrayIterator currentVersion;
};


Пример использования

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#import "Undoable.h"
 
// UVector (undoable vector) — расширение стандартного вектора.
template <class T>
class UVector : public Undoable<std::vector<T> > {};
 
int main (int argc, char * argv[]) {
    UVector<int> v;
    
    randomVector(v, 10);
    showVector(v);
    v.save();
    
    v.erase(v.begin() + 3, v.end());
    showVector(v);
    v.save();
    
    v.undo();
    showVector(v);
    
    v.redo();
    showVector(v);
    
    return 0;
}
Результат выполнения:

3 6 7 5 3 5 6 2 9 1
3 6 7
3 6 7 5 3 5 6 2 9 1
3 6 7
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
16.03.2010, 22:07     Откатываемый (undoable) класс
Посмотрите здесь:

C++ Класс в С++
Класс с++ C++
класс C++
Класс C++
Класс C++
Класс C++
C++ Класс
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Nick Alte
Эксперт С++
1603 / 995 / 118
Регистрация: 27.09.2009
Сообщений: 1,918
Завершенные тесты: 1
17.03.2010, 20:17     Откатываемый (undoable) класс #2
Идейка называется CRTP (Curiously Recurring Template Pattern). Вкратце выглядит так:
C++
1
2
3
4
5
6
7
8
9
template<typename T>
class Undoable {
// все операции настраиваем на работу с типом T
};
 
class MyClass: public Undoable<MyClass>
{
// независимая реализация MyClass, к которой автоматом добавляется функциональность Undoable.
};
volovzi
267 / 169 / 8
Регистрация: 14.03.2010
Сообщений: 501
18.03.2010, 13:22  [ТС]     Откатываемый (undoable) класс #3
Nick Alte, что-то пока не получается реализовать. Ты не мог бы схематично расписать одну из функций класса Undoable?
В моём способе Undoable наследуется от класса T, поэтому он может работать с переменными этого класса. А как работать с классом, когда ты не знаешь, что в нём лежит?

Добавлено через 31 минуту
А, я понял, там нужно приведение типов делать. Но всё равно суть остаётся та же: откатываемый класс находится на дне иерархии. А вот можно ли как-нибудь его наверх вытащить?

Добавлено через 15 часов 21 минуту
Так-с, с паттерном, вроде, разобрался. Спасибо за наводку.
Но по-прежнему не могу создать копию объекта внутри класса Undoable.

Вот такая конструкция в классе Undoable
C++
1
2
3
void save () {
    T * newVersion = new T(*static_cast<T *>(this));
}
выдаёт ошибку:

/Developer/SDKs/MacOSX10.5.sdk/usr/include/c++/4.0.0/debug/safe_iterator.h:127:
error: attempt to copy-construct an iterator from a singular iterator.

Objects involved in the operation:
iterator "this" @ 0x0x1001cc {
type = N11__gnu_debug14_Safe_iteratorIN9__gnu_cxx17__normal_iteratorIPP7myClassN10__gnu_norm6vectorIS4_SaIS4_EEEEEN15__gnu_debug_def6vectorIS4_S8_EEEE (mutable iterator);
state = singular;
}
iterator "other" @ 0x0xbffff724 {
type = N11__gnu_debug14_Safe_iteratorIN9__gnu_cxx17__normal_iteratorIPP7myClassN10__gnu_norm6vectorIS4_SaIS4_EEEEEN15__gnu_debug_def6vectorIS4_S8_EEEE (mutable iterator);
state = singular;
}


Как решить проблему?
Nick Alte
Эксперт С++
1603 / 995 / 118
Регистрация: 27.09.2009
Сообщений: 1,918
Завершенные тесты: 1
18.03.2010, 20:52     Откатываемый (undoable) класс #4
Тут идёт какая-то заморочка с итераторами. Для обычных типов конструкция правильная, должно работать.
CyBOSSeR
Эксперт C++
2298 / 1668 / 86
Регистрация: 06.03.2009
Сообщений: 3,675
18.03.2010, 21:04     Откатываемый (undoable) класс #5
volovzi, то что ты пытаешься сделать невыгодно.

Предположим у меня есть объект имеющий 1000 членов данных. Я перед выполнением некой операции, изменяющей 1 член данных, сохраняю состояние всех членов с помощью вызова твоей операции save (вызывая возможно очень дорогостоящий конструктор копирования).
Вопрос на кой хрен мне хранить лишние состояние 999 членов, если я изменения касаются только одного?

volovzi, все уже придумано до тебя, см. в сторону паттерна COMMAND (Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес Приемы объектно-ориентированного проектирования. Паттерны проектирования).
Genius Ignat
1234 / 772 / 44
Регистрация: 16.09.2009
Сообщений: 2,014
18.03.2010, 21:10     Откатываемый (undoable) класс #6
Начал эту книгу читать, книга ok...
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
19.03.2010, 01:56     Откатываемый (undoable) класс
Еще ссылки по теме:

C++ Класс
C++ С++. 11 класс.
C++ Builder Класс и класс-наследник
C++ Класс данных, класс сортировок
C++ Класс из C++ на C

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
volovzi
267 / 169 / 8
Регистрация: 14.03.2010
Сообщений: 501
19.03.2010, 01:56  [ТС]     Откатываемый (undoable) класс #7
Цитата Сообщение от CyBOSSeR Посмотреть сообщение
volovzi, то что ты пытаешься сделать невыгодно.

Предположим у меня есть объект имеющий 1000 членов данных. Я перед выполнением некой операции, изменяющей 1 член данных, сохраняю состояние всех членов с помощью вызова твоей операции save (вызывая возможно очень дорогостоящий конструктор копирования).
Вопрос на кой хрен мне хранить лишние состояние 999 членов, если я изменения касаются только одного?
Действительно... Но надо же с чего-то начинать :) .

volovzi, все уже придумано до тебя, см. в сторону паттерна COMMAND (Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес Приемы объектно-ориентированного проектирования. Паттерны проектирования).
Спасибо, прочту.
Yandex
Объявления
19.03.2010, 01:56     Откатываемый (undoable) класс
Ответ Создать тему
Опции темы

Текущее время: 22:21. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru