Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / C++ [игнор отключен] [закрыт для гостей] / Автоматический откат действий / 25 сообщений из 34, страница 1 из 2
25.12.2018, 15:37
    #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
25.12.2018, 15:46
    #39752877
Anatoly Moskovsky
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Автоматический откат действий
Cerebrum,

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

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

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

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

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

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

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

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

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

тоже думал о подобном, чтобы команда в случае неудачи все отменяла, но если комада отработала нормально, а следующая нет, то как предыдущая узнает что ей надо делать откат?
Сделай класс макро-команды, которая просто набивает в очередь команды и последовательно их выполняет,
перекладывая в стек (структуру данных) выполненных.
В случае облома, все выполненные из стека выталкиваются и undo-ятся.
...
Рейтинг: 0 / 0
25.12.2018, 16:50
    #39752940
egorych
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Автоматический откат действий
Anatoly MoskovskyНе обязательно обертки писать для функций. Достаточно написать обертку для строки с оператором приведения к const char*и впрямь)) но это уже не тупая, это подумать пришлось))
...
Рейтинг: 0 / 0
25.12.2018, 17:29
    #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
25.12.2018, 17:32
    #39752964
a.guest
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Автоматический откат действий
1) Выкидываешь Action, берёшь std::function
2) В UndoActions переделываешь Add чтобы он принимал только F, при вызове суёшь лямбду, в которой захватываешь всё что нужно по значению
3) Выкидываешь UndoActions и переезжаешь на Boost.ScopeExit или на folly scopeguard или на что-нибудь подобное. Минусы: придётся руками везде проверять условие выхода. Плюсы: нет проблем с порядком разрушения, естественная структура данных для хранения действий отката.
...
Рейтинг: 0 / 0
25.12.2018, 17:57
    #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
25.12.2018, 18:06
    #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
25.12.2018, 18:10
    #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
25.12.2018, 18:12
    #39752995
NekZ
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Автоматический откат действий
egorych,

Всё равно error-prone code. Забудешь написать undo.Add где-нибудь и усё :)
...
Рейтинг: 0 / 0
25.12.2018, 18:14
    #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
25.12.2018, 18:16
    #39753002
egorych
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Автоматический откат действий
NekZegorych,
Всё равно error-prone code. Забудешь написать undo.Add где-нибудь и усё :)это же не промышленный код, вроде. для тестирования и рефакторинга сойдёт, кмк.

P.S. я бы, кстати, вместо Add сделал бы оператор << )))
...
Рейтинг: 0 / 0
25.12.2018, 18:18
    #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
25.12.2018, 18:20
    #39753007
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Автоматический откат действий
a.guestСам-то читал что по ссылке?

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

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

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


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