powered by simpleCommunicator - 2.0.58     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / C++ [игнор отключен] [закрыт для гостей] / Автоматический откат действий
25 сообщений из 34, страница 1 из 2
Автоматический откат действий
    #39752864
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Привет, холливарщикам!

Хорош сраться, лучше помогите мне придумать очередной велосипед


WARNING:
Весь код условный, на компилирование и эстетическую красоту не претендующий, мне главное - донести идею и проблему показать.

Предположим есть некий класс а ля type erasure функтор
class Action
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
class Action
{
public:
	template <typename F_t, typename ...ArgsT>
	Action(F_t&& fn, ArgsT&&... Args)
	{
		...
	}

protected:

	SomeFunctorClass	m_fn;
	std::tuple<...>		m_args;

public:
	void Invoke(void)
	{
		m_fn(m_args);
	}
}; // class Action


есть RAII обертка для массива объектов Action, которая при своем разрушении вызывает каждую из добавленных в него Action в обратном порядке
class UndoActions
Код: plaintext
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.
class UndoActions
{
	~UndoActions()
	{
		Rollback();
	}

protected:

	std::vector<Action*>	m_actions;

public:

	template <typename F_t, typename ...ArgsT>
	void Add(F_t&& fn, ArgsT&&... Args)
	{
		m_actions.push_back(new Action(std::forward<F_t>(fn), std::forward<ArgsT>(Args)...));
	}

	void Rollback()
	{
		while (m_actions.size())
		{
			auto item = m_actions.back();
			item->Invoke();
			delete item;
			m_actions.pop_back();
		}
	}
}; // class UndoActions


Смысл я думаю понятен. Мы можем добавлять в UndoAction функции и их аргументы, которые будут вызваны в момент разрушения экземпляра класса UndoActions автоматически.

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

По задумке работать это должно вот так:
Код: plaintext
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.
BOOL test(void)
{

	UndoActions undo;

	std::string sDirPath = "dir_path";
	if (CreateDirectory(sDirPath) == FALSE)
		return FALSE;

	undo.Add(&RemoveDirectory, sDirPath, ...);

	std::string sFilePath = sDirPath + "\\filename.txt";
	if (CreateFile(sFilePath.c_str()) == FALSE)
		return FALSE;

	undo.Add(&DeleteFile, sFilePath.c_str(), ...);

	std::string strDatabase = "database";
	HANDLE hDataBase = CreateDatabase(strDatabase, sFilePath.c_str());
	if (hDataBase == INVALID_HANDLE_VALUE)
		return FALSE;

	undo.Add(DestroyDatabase(strDatabase.c_str(), sFilePath.c_str(), ...));

	//
	// и т.д.
	//

	// если ошибка - возвращаем FALSE - и undo откатывает все изменения - мы молодцы!
	// если ок

	// если TRUE - очищаем undo и ничего не откатывается

	undo.Clear();
	return TRUE;
}



Проблему я думаю уже все заметили - и имя ей - порядок освобождения объектов на стеке.
Он LIFO, а значит, когда UndoActions соберется делать Rollback в деструкторе, все объекты string уже будут удалены, следовательно ничего оно почистить не сможет! Если вообще не крашанется...

Вытаскивать все string (в реальном коде там другие, более сложные RAII объекты) наверх до undo как-то по ламерски и не комильфо. Можно было бы сделать shared-типы из аргументов, но тогда будут проблемы с сигнатурами функций у функтора (он не сможет их нормально сопоставить с undo-функциями, так как они ожидают другие типы, а у компилятора шаблоны, type safety, да и как потом их чистить, функтор Action тоже про это ничего не знает).

Попробовал было __try / __finally, но был послан из-за unwinded

Может есть способ сказать компилятору, чтобы он разрушал undo раньше всех остальных или есть какие-то другие мысли, как сделать это корректно?

Если что я на MSVC2017 Cx17, поэтому все сопутствующие non standard фичи под Windows могу смело юзать.

--------------------------------------------------------------
o(O_O)o
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752877
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum,

Так передавайте строки по значению, а не c_str()
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752887
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky,

сигнатуры RemoveDirectory/RemoveFile/DestroyDatabase не знают что такое string, они знают только char*/wchar_t* поэтому компилятор тупо не сможет сопоставить функцию в Action по переданным string параметрам.
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752891
egorych
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
CerebrumAnatoly Moskovsky,

сигнатуры RemoveDirectory/RemoveFile/DestroyDatabase не знают что такое string, они знают только char*/wchar_t* поэтому компилятор тупо не сможет сопоставить функцию в Action по переданным string параметрам.тупую обёртку написать RemoveDirectory( std::string path ) { return RemoveDirectory( paht.c_str() ); }?
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752893
Фотография NekZ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum,

Может, стоило бы реализовать паттерн Команда (даже в функциональном стиле, на лямбдах), чтобы все данные для do и undo принадлежали одной и той сущности? А то ты хочешь сразу на двух стульях сидеть, и писать plain код как есть и заодно stack unwinding перехитрить.
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752895
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
egorychCerebrumAnatoly Moskovsky,

сигнатуры RemoveDirectory/RemoveFile/DestroyDatabase не знают что такое string, они знают только char*/wchar_t* поэтому компилятор тупо не сможет сопоставить функцию в Action по переданным string параметрам.тупую обёртку написать RemoveDirectory( std::string path ) { return RemoveDirectory( paht.c_str() ); }?
да, можно попробовать
спасибо
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752899
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
egorychтупую обёртку написать RemoveDirectory( std::string path ) { return RemoveDirectory( paht.c_str() ); }?
Не обязательно обертки писать для функций. Достаточно написать обертку для строки с оператором приведения к const char*
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752900
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
NekZCerebrum,

Может, стоило бы реализовать паттерн Команда (даже в функциональном стиле, на лямбдах), чтобы все данные для do и undo принадлежали одной и той сущности? А то ты хочешь сразу на двух стульях сидеть, и писать plain код как есть и заодно stack unwinding перехитрить.

тоже думал о подобном, чтобы команда в случае неудачи все отменяла, но если комада отработала нормально, а следующая нет, то как предыдущая узнает что ей надо делать откат?
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752905
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovskyegorychтупую обёртку написать RemoveDirectory( std::string path ) { return RemoveDirectory( paht.c_str() ); }?
Не обязательно обертки писать для функций. Достаточно написать обертку для строки с оператором приведения к const char*

другими словами, без еще одной копии данных принадлежащей строго Action не получается
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752917
Фотография NekZ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
CerebrumNekZCerebrum,

Может, стоило бы реализовать паттерн Команда (даже в функциональном стиле, на лямбдах), чтобы все данные для do и undo принадлежали одной и той сущности? А то ты хочешь сразу на двух стульях сидеть, и писать plain код как есть и заодно stack unwinding перехитрить.

тоже думал о подобном, чтобы команда в случае неудачи все отменяла, но если комада отработала нормально, а следующая нет, то как предыдущая узнает что ей надо делать откат?
Сделай класс макро-команды, которая просто набивает в очередь команды и последовательно их выполняет,
перекладывая в стек (структуру данных) выполненных.
В случае облома, все выполненные из стека выталкиваются и undo-ятся.
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752940
egorych
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly MoskovskyНе обязательно обертки писать для функций. Достаточно написать обертку для строки с оператором приведения к const char*и впрямь)) но это уже не тупая, это подумать пришлось))
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752963
Фотография NekZ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum,

Кстати, можно было бы провернуть трюк goto-без-goto
Код: plaintext
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.
BOOL test(void)
{
    BOOL error = FALSE;
    do
    {
        std::string sDirPath = "dir_path";
        if (CreateDirectory(sDirPath) == FALSE)
        {
            error = TRUE;
            break;
        }

        std::string sFilePath = sDirPath + "\\filename.txt";
        if (CreateFile(sFilePath.c_str()) == FALSE)
        {
            error = TRUE;
            break;
        }

        std::string strDatabase = "database";
        HANDLE hDataBase = CreateDatabase(strDatabase, sFilePath.c_str());
        if (hDataBase == INVALID_HANDLE_VALUE)
        {
            error = TRUE;
            break;
        }
    }
    while(0);
    if(error)
    {
        /// Cleanup code
        return FALSE;
    }
    return TRUE;
}
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752964
a.guest
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
1) Выкидываешь Action, берёшь std::function
2) В UndoActions переделываешь Add чтобы он принимал только F, при вызове суёшь лямбду, в которой захватываешь всё что нужно по значению
3) Выкидываешь UndoActions и переезжаешь на Boost.ScopeExit или на folly scopeguard или на что-нибудь подобное. Минусы: придётся руками везде проверять условие выхода. Плюсы: нет проблем с порядком разрушения, естественная структура данных для хранения действий отката.
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752979
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
egorychAnatoly MoskovskyНе обязательно обертки писать для функций. Достаточно написать обертку для строки с оператором приведения к const char*и впрямь)) но это уже не тупая, это подумать пришлось))
пробовал по всякому
Код: plaintext
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.
62.
63.
64.
65.
66.
#pragma once
#include "windows.h"
#include "iostream"

class TypeWrapper
{
public:
	TypeWrapper(wchar_t* p) : m_pTp(p)	{}
	virtual ~TypeWrapper(void) = default;

	TypeWrapper(const TypeWrapper& other) = delete;
	TypeWrapper& operator=(const TypeWrapper& rhs) = delete;

public:
	operator wchar_t*()	{	return 	m_pTp;	}

protected:
	wchar_t*		m_pTp;

public:

}; // class TypeWrapper

#include <tuple>

template <typename FunctionT, typename ...ArgsT>
class FuncCaller
{
public:
	FuncCaller(FunctionT&& fn, ArgsT&& ...Args) : m_fn(fn), m_args(std::forward<ArgsT>(Args)...)	{ }

	virtual ~FuncCaller(void) = default;

private:
	FuncCaller(const FuncCaller& other) = delete;

private:
	FuncCaller& operator=(const FuncCaller& rhs) = delete;

protected:
	FunctionT					m_fn;
	std::tuple<std::remove_reference_t<ArgsT>...>	m_args;

public:

	BOOL Invoke(void)
	{
		std::wcout << std::get<0>(m_args) << std::endl;
		return m_fn(std::get<0>(m_args));
	}

}; // class FuncCaller

int main()
{
	auto pData = static_cast<LPWSTR>(::malloc(sizeof(wchar_t) * 5UL));
	::ZeroMemory(pData, sizeof(wchar_t) * 5UL);

	TypeWrapper value(pData);

	FuncCaller<BOOL (__stdcall *)(LPCWSTR), TypeWrapper> fc(&DeleteFile, value);
	
	fc.Invoke();

	return 0;
}



авторerror C2664: 'FuncCaller<BOOL (__stdcall *)(LPCWSTR),TypeWrapper>::FuncCaller(const FuncCaller<BOOL (__stdcall *)(LPCWSTR),TypeWrapper> &)': cannot convert argument 2 from 'TypeWrapper' to 'TypeWrapper &&'
note: You cannot bind an lvalue to an rvalue reference
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752991
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я вижу у тебя только операции над файлами. В этом случае транзакции тебе помогут:
https://docs.microsoft.com/en-us/windows/desktop/fileio/about-transactional-ntfs
https://docs.microsoft.com/en-us/windows/desktop/fileio/deprecation-of-txf
https://stackoverflow.com/questions/17593233/what-are-transactional-file-operations
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752994
egorych
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrumпробовал по всякомучто то как то сложно))
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
BOOL test()
{
    struct stringwrap
    {
         stringwrap( const std::string value ) : _value( value ) {}
    
         operator const char *() { return _value.c_str(); }

         std::string _value;

    };  // struct stringwrap

    UndoAction undo;

    std::string sDirPath = "dir_path";
    if( CreateDirectory( sDirPath ) == FALSE ) return FALSE;

    undo.Add( &RemoveDirectory, stringwrap( sDirPath ), ... );

    ....
}

P.S. не компилировал
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752995
Фотография NekZ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
egorych,

Всё равно error-prone code. Забудешь написать undo.Add где-нибудь и усё :)
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39752998
a.guest
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dimitry SibiryakovЯ вижу у тебя только операции над файлами. В этом случае транзакции тебе помогут:
https://docs.microsoft.com/en-us/windows/desktop/fileio/about-transactional-ntfs
Сам-то читал что по ссылке?
Microsoft strongly recommends developers utilize alternative means to achieve your applications needs. Many scenarios that TxF was developed for can be achieved through simpler and more readily available techniques. Furthermore, TxF may not be available in future versions of Microsoft Windows.
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39753002
egorych
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
NekZegorych,
Всё равно error-prone code. Забудешь написать undo.Add где-нибудь и усё :)это же не промышленный код, вроде. для тестирования и рефакторинга сойдёт, кмк.

P.S. я бы, кстати, вместо Add сделал бы оператор << )))
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39753004
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry SibiryakovЯ вижу у тебя только операции над файлами. В этом случае транзакции тебе помогут:
https://docs.microsoft.com/en-us/windows/desktop/fileio/about-transactional-ntfs
https://docs.microsoft.com/en-us/windows/desktop/fileio/deprecation-of-txf
https://stackoverflow.com/questions/17593233/what-are-transactional-file-operations
я для примера привел, чтобы народу мозг не забивать всякой работой с доменом и прочими БД, ну и файловые операции там тоже есть
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39753007
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
a.guestСам-то читал что по ссылке?

А что тебя особо пугает в "используйте технологии, соответствующие нуждам вашего
приложения"? То, что кроме первой ссылки придётся прочитать ещё две?..
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39753010
a.guest
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dimitry SibiryakovА что тебя особо пугает в "используйте технологии, соответствующие нуждам вашего
приложения"?Нужнам каких приложений соответствуют deprecated технологии?
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39753011
blonduser
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum,

В данном варианте в случае ошибки просто убиваешь папку со всем содержимым.
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39753014
a.guest
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
blonduserв случае ошибки просто убиваешь папкуБедный папка, его-то за что?
...
Рейтинг: 0 / 0
Автоматический откат действий
    #39753015
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
blonduser,

Жизнь прекрасна и проста в простых тестовых примерах
...
Рейтинг: 0 / 0
25 сообщений из 34, страница 1 из 2
Форумы / C++ [игнор отключен] [закрыт для гостей] / Автоматический откат действий
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]