Wyjątki a wydajność programu[C++]


(Marcinochman1993) #1

Są wakacje, dłuuuuugie wakacje, bo jestem po maturze i zaczynam studiować dopiero w październiku, dlatego dla zabicia nudy próbuję sobie napisać własną okrojoną obiektową wersję WinAPI na wzór Windows Forms z .NET(tak wiem, że są MFC, ale mówię, ma to na celu zabicie nudy oraz podszkolenie się w C++, może kiedyś uda mi się stworzyć grę i użyć mojej biblioteczki przy tworzeniu okna :D). Mam jednak dylemat, zaimplementowałem już zdarzenia (podobne do tych z C#, jednak bardziej ograniczone - stała liczba argumentów funkcji: pierwszy to wskaźnik na obiekt wywołujący, drugi to dowolny typ, jednak z założenia mają to być informacje o zdarzeniu) za pomocą szablonu klasy WindowEvent:

template

	class WindowEvent

	{

	public:

		void operator()(void* sender, EventArgsType args);

		void operator+=(void (*) (void*,EventArgsType));

	private:

		std::vector functions; //przechowuje wskazniki funkcji, które maja byc wywoływane za pomoca operatora ()

	};


template

		void WindowEvent::operator()(void* sender, EventArgsType args)

		{

		for(unsigned int register i=0;i
		{

			functions[i](sender,args);

		}

}


template

		void WindowEvent::operator+=(void (*function)(void* sender, EventArgsType args))

		{

			functions.push_back(function);

		}

Niby wszystko jest OK, ale problem pojawia się kiedy wywołuje dane zdarzenie, bo muszę sprawdzić czy np zdarzenie WindowEvent onClose ma "w środku jakieś wskaźniki na funkcje", inaczej musze wywołać funkcje DefWindowProc. Pojawiła mi się myśl, że mógłbym zgłaszać wyjątki w momencie kiedy vector functions jest pusty, jednak czy jest to dobre rozwiązanie? Bo kiedy większość zdarzeń będzie pusta, to przy przetwarzaniu wiadomości okna, będzie zgłaszanych mnóstwo wyjątków, czy wpłynie to drastycznie na wydajność? Oczywiśie istnieje inne rozwiązanie - publiczna metoda bool isEmpty(), którą trzeba wywołać przed wywołaniem operatora(), ale przecież wyjątki mają być mechanizmem, który ma zlikwidować takie właśnie sprawdzanie. Więc co myślicie?


(Grzegorz Olszewski) #2

Wyjątki nie powinny być mechanizmem sterującym przepływem programu podczas normalnego wykonania. Wyjątek, jak sama nazwa wskazuje, to sytuacja nieoczekiwana, która nie powinna wystąpić,a której nie możemy obsłużyć. Tak jak brak pliku, którego oczekujemy, albo nieoczekiwane zakończenie połączenia. Uważam, że powinno się zachować tę semantykę chociażby dla przejrzystości kodu.

Jeśli chodzi o wydajność, to zazwyczaj samo try..catch bez wystąpienia wyjątku nie powoduje zauważalnego spadku wydajności, ale kiedy wyjątek się pojawia, ma negatywny wpływ na wydajność programu. Jest to zresztą zgodne z założeniami - wyjątek to sytuacja wyjątkowa, i występująca rzadko.

Do poczytania do poduszki:

http://www.codeproject.com/Articles/38449/C-Exceptions-Pros-and-Cons

http://www.cplusplus.com/forum/general/28656/


(Marcinochman1993) #3

Tak jak napisałeś, wyjątek powinien być zgłaszany w sytuacji nieoczekiwanej, ale co ciekawe dla klasy implementującej zdarzenie (WindowEvent) jest to właśnie owa nieoczekiwana sytuacja (głupek utworzył zdarzenie, które tak naprawdę nie ma funkcji do wywołania a chce je wywołać, można byłoby to porównać do operacji na plikach: ktoś chce otworzyć plik, który nie istnieje). A co do sterowania przepływem, to w sumie przy wyrzuceniu wyjątku przez zdarzenie należałoby je tylko obsłużyć, nie do końca nazwałbym to tak. Może ja wrzucę kod funkcji, która realizuje operacje na zdarzeniach, wtedy bedzie łatwiej "ogarnąć" o co mi chodzi:

LRESULT CALLBACK MOSoft::winProc(HWND hWnd,UINT message, WPARAM wParam, LPARAM lParam)

{

	Window* window=Window::getWindowByHWnd(hWnd); //funkcja, która zwraca obiekt typu Window, aby można było poźniej działać na jego zdarzeniach


	if(window!=nullptr)

	{

		switch(message)

		{

		case WM_CLOSE:

			{

				MOSoft::WindowEventArguments args(wParam,lParam);

				window->onClose(window,args);

				return 0;

			}

		case WM_CREATE:

			{

				MOSoft::WindowEventArguments args(wParam,lParam);

				window->onCreate(window,args);

				return 0;

			}

		case WM_DESTROY:


			{

				MOSoft::WindowEventArguments args(wParam,lParam);

				window->onDestroy(window,args);

				window->deleteFromList();

				return 0;

			}


		case WM_SETFOCUS:

			{

				MOSoft::WindowEventArguments args(wParam,lParam);

				window->onSetFocus(window,args);

				return 0;

			}

		case WM_KILLFOCUS:

			{

				MOSoft::WindowEventArguments args(wParam,lParam);

				window->onKillFocus(window,args);

				return 0;

			}

		case WM_PAINT:

			{

				MOSoft::WindowEventArguments args(wParam,lParam);

				window->onPaint(window,args);

				return 0;

			}

		default: 

			return DefWindowProc(hWnd,message,wParam,lParam);

		}

	}

	else

		return DefWindowProc(hWnd,message,wParam,lParam);

}

Chodzi mi o to, że zamiast takiego czegoś byłoby np.:

case WM_CLOSE:

			try{

				MOSoft::WindowEventArguments args(wParam,lParam);

				window->onClose(window,args);

				return 0;

			}catch(NoFunctionsException& e)

			{

				return DefWindowProc(...);

			}

-- Dodane 21.08.2012 (Wt) 10:08 --

Przetestowałem dzisiaj oba rozwiązania i faktycznie przy wykorzystaniu wyjątków program zużywa większe zasoby komputera, ze względu na to, że wyjątki są baaaardzo często zgłaszane, dlatego mimo tego, że z punktu widzenia klasy WindowEvent zgłaszanie wyjątku nie jest niczym złym, to wykorzystanie jej razem z klasą Window, która chce wywołać co chwilę jakieś zdarzenie w odpowiedzi na wiadomość okna, mija się z celem, ponieważ wyjątków jest za dużo. Mam nauczkę na przyszłość: co może być dobre z punktu widzenia jednej klasy, nie musi być dla drugiej... budda86 dzięki za odpowiedź i przede wszystkim za linki, pozdrawiam :wink: