Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Statyczne obiekty i wyjątki

11 views
Skip to first unread message

SasQ

unread,
Dec 11, 2006, 4:13:31 PM12/11/06
to
Siema.

Co się stanie, gdy konstruktor globalnego obiektu statycznego rzuci
wyjątek? Czy da się jakoś przechwycić taki wyjątek?

Jeśli nie, to jak inaczej można zasygnalizować w programie,
że nie udało się poprawnie utworzyć obiektu, który był
singletonem?

--
SasQ

Jacek Czerwinski

unread,
Dec 11, 2006, 4:58:01 PM12/11/06
to

Dlatego taki singleton robię tak, aby ryzykowna część tworzenia była
później, np. przy pierwszym użyciu. Wg mnie to dopiero rasowy singleton.
W części statycznej to, co zawsze się uda (null pointery, jakiś string itd)

SasQ

unread,
Dec 15, 2006, 7:32:30 PM12/15/06
to
Dnia Mon, 11 Dec 2006 22:58:01 +0100, Jacek Czerwinski napisał(a):


> Dlatego taki singleton robię tak, aby ryzykowna część tworzenia
> była później, np. przy pierwszym użyciu.

A jeśli sposobów pierwszego użycia będzie dużo?
Przecież nie będę w każdym się upewniał, czy obiekt już
został stworzony :| No chyba że masz na to jakiś sprytny trick ;)

OK, może powiem co konkretnie chcę osiągnąć.

Chciałem napisać klasę Ekran do obsługi ekranu graficznego
[taki wrapperek, który sobie mógłbym implementować przy użyciu
różnych innych bibliotek graficznych, typu np. DirectX czy SDL].
Obiekt tej klasy ma być utożsamiany z ekranem monitora.
Ekran monitora istnieje od samego początku działania programu
[a nawet przed nim ;J], i jest tylko jeden dla całego programu.
Dlatego pomyślałem, by zrealizować go jako singleton - globalny
obiekt przechowywany statycznie w klasie, w taki sposób, by
nie dało się utworzyć sobie ręcznie dodatkowych obiektów tej
klasy [konstruktor prywatny] i żeby dostęp do niego był
kontrolowany przez klasę Ekran i przez nią się odbywał.

Użytkownik tej klasy widziałby to tak, że przychodzi "na gotowe",
czyli na początku funkcji main() może być pewny, że obiekt
ekranu istniał tam od zawsze i jest od razu dostępny. W taki
sam sposób jak dostępne są od razu obiekty strumieni I/O
[std::cin, std::cout, std::err itp.].

Przygotowywanie takiego ekranu do pracy może się nie udać.
Np. może nie zostać utworzone okno, w którym on działa.
Może się nie udać inicjalizacja SDL, lub utworzenie obiektu
głównego Direct3D, czy co tam on będzie opakowywał ;P
Może się nie udać inicjalizacja ekranu w bieżącym trybie
graficznym i pobranie informacji o jego aktualnym trybie.
W takich sytuacjach chciałoby się rzucić wyjątkiem, bo
wszystko jest realizowane w konstruktorze tego statycznego
obiektu w klasie przy jego inicjalizacji.

Problem pojawił się właśnie z tymi wyjątkami. Na poniższe
pytanie nikt mi w zasadzie narazie nie odpowiedział:

>> Co się stanie, gdy konstruktor globalnego obiektu
>> statycznego rzuci wyjątek? Czy da się jakoś przechwycić
>> taki wyjątek?

Myślę nad jakimś takim rozwiązaniem, żeby sama klasa Ekran
łapała gdzieś ten wyjątek i obsługiwała wyświetlając np.
MessageBoxa z informacją, że nie udało się zainicjalizować
ekranu, czy co tam go będzie boleć...
Tylko nie wiem czy takie reakcje leżą w gestii biblioteki,
która ma służyć programowi głównemu, a nie dyktować własne
warunki i decydować o wyświetleniu komunikatu :P Nie wiem
jak to rozwiązać, by program główny miał możliwość zostać
powiadomiony, że nie udało się przygotować ekranu do pracy
przed rozpoczęciem main(), by mógł zareagować na tą sytuację
po swojemu. O to właśnie mniej więcej pytałem tu:

>> Jeśli nie, to jak inaczej można zasygnalizować w programie,
>> że nie udało się poprawnie utworzyć obiektu, który był
>> singletonem?

Heh... ciężki orzech do zgryzienia :P

--
SasQ

Jacek Czerwinski

unread,
Dec 16, 2006, 3:19:59 AM12/16/06
to

Ja bym unikał ambitnego kodu przed main. (Inicjacja bliootek standardowych,
zmiennych środowiska itd itd)
Singletona postrzegam (szkołą javowska) że najważniejszy kod jest
wykonywany w metodzie
static getMySingleton()
a nie w konstruktorze instancji statycznej (szkoła C++) - tu najwyżej
zerowanie handlerów, pointerów itd)

a ten MySingleton * MySingleton::getMySingleton() jest wołany już z main w
jego początkowych liniach

z innej strony, stary dobry kod powrotu C też wolno ci zapamiętac i
skorzystać z niego. exception to nie przymus.

Marcin 'Qrczak' Kowalczyk

unread,
Dec 16, 2006, 5:08:35 AM12/16/06
to
SasQ <sa...@go2.pl> writes:

> Chciałem napisać klasę Ekran do obsługi ekranu graficznego
> [taki wrapperek, który sobie mógłbym implementować przy użyciu
> różnych innych bibliotek graficznych, typu np. DirectX czy SDL].
> Obiekt tej klasy ma być utożsamiany z ekranem monitora.
> Ekran monitora istnieje od samego początku działania programu

Ale nie graficzny. Wyobraź sobie program, który w zależności od
sposobu uruchomienia korzysta z grafiki lub nie. Jeśli nie korzysta,
to może być uruchomiony nawet tam, gdzie grafiki nie ma (np. sesja
ssh bez tunelowania X-ów).

--
__("< Marcin Kowalczyk
\__/ qrc...@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

SasQ

unread,
Jan 9, 2007, 10:16:45 AM1/9/07
to
Odgrzeję jeszcze tego kotleta, bo sprawa mnie
dalej intryguje, a jeszcze nie wszystko mi się
przejaśniło.
Jak to jest robione w przypadku obiektów globalnych,
takich jak std::cin czy std::cout? Przecież one też
są obiektami tworzonymi jeszcze przed funkcją main()
i nie są to chyba obiekty zbyt trywialne, bo ich
implementacja wymaga pewnie utworzenia strumieni przy
pomocy systemowych API lub zdobycia do nich uchwytów,
oraz pewnie parę innych rzeczy, które mogłyby się
nie udać. Co się stanie, gdy inicjalizacja takich
obiektów strumieni standardowego wejścia/wyjścia się
nie powiedzie?
Wydaje mi się, że to jest podobna sytuacja do tej, z
którą miałem wtedy problem [właściwie to mam nadal ;J],
bo tutaj też coś może się nie udać przy inicjalizacji
obiektu jeszcze przed funkcją main(), czyli jeszcze
zanim użytkownik tego kodu może jakoś zareagować na
taką porażkę.

--
SasQ

Maciej Sobczak

unread,
Jan 10, 2007, 2:21:45 AM1/10/07
to
SasQ wrote:

> Jak to jest robione w przypadku obiektów globalnych,
> takich jak std::cin czy std::cout?
> Przecież one też
> są obiektami tworzonymi jeszcze przed funkcją main()
> i nie są to chyba obiekty zbyt trywialne, bo ich
> implementacja wymaga pewnie utworzenia strumieni przy
> pomocy systemowych API lub zdobycia do nich uchwytów,
> oraz pewnie parę innych rzeczy, które mogłyby się
> nie udać.

Tak samo, jak w przypadku zmiennych globalnych stdin czy stdout w C.
Jeśli kanały standardowego IO nie mogą zostać otwarte, to wiadomo to
jeszcze przed uruchomieniem programu i system operacyjny może wtedy
podjąć odpowiednie akcje. Właściwy program zaczyna się wtedy, gdy
wszystkie deskryptory/klamki/itp. są już gotowe.


--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/

SasQ

unread,
Jan 13, 2007, 6:21:28 AM1/13/07
to
Dnia Wed, 10 Jan 2007 08:21:45 +0100, Maciej Sobczak napisał(a):

>> Jak to jest robione w przypadku obiektów globalnych,
>> takich jak std::cin czy std::cout?
>

> Tak samo, jak w przypadku zmiennych globalnych stdin czy
> stdout w C. Jeśli kanały standardowego IO nie mogą zostać
> otwarte, to wiadomo to jeszcze przed uruchomieniem programu

Hmm... Czyli chyba zły przykład wybrałem :/
Ale konkretnie to chodzi mi o to, co się dzieje w przypadku,
gdy nie uda się utworzyć jakiegoś obiektu jeszcze przed
funkcją main(), bo np. brakło pamięci i nie da się jej już
przydzielić dla obiektu? Albo nie udało się otworzyć
jakiegoś pliku, który w funkcji main() miał być już
gotowy do zapisywania? Albo przed funkcją main() był
tworzony obiekt ładujący dynamicznie jakąś DLLkę, by jej
funkcje były w sposób "przezroczysty" dostępne już w main()?

Jest jakiś dobry mechanizm by powiadomić użytkownika, że
uruchomienie programu się nie powiodło i z jakiego powodu?
[może po prostu wypisać coś na stderr, skoro te strumienie
zawsze są dostępne, po czym zakończyć program?]
Co się stanie, gdy konstruktor takiego obiektu rzuci wyjątek
przed funkcją main()? Zostanie on gdzieś złapany przez
kod implementujący C++ ? czy może zostanie olany? :P
Bo chyba nie ma sposobu, żeby programista złapał wyjątek,
jeśli jest on poza funkcją main()? [chyba że o czymś nie wiem ;J]
Te pytania wciąż pozostają dla mnie bez odpowiedzi.

--
SasQ

Maciej Sobczak

unread,
Jan 15, 2007, 3:08:32 AM1/15/07
to
SasQ wrote:

> Ale konkretnie to chodzi mi o to, co się dzieje w przypadku,
> gdy nie uda się utworzyć jakiegoś obiektu jeszcze przed
> funkcją main()

No to, panie, bida.

> Jest jakiś dobry mechanizm by powiadomić użytkownika, że
> uruchomienie programu się nie powiodło

Tak, system operacyjny zwykle wywala w terminalu jakiś komunikat, że
poleciał niezłapany wyjątek.
Nie wiem, czy o taki "mechanizm" Ci chodzi. :-)

Na pociechę można podać fakt, że nawet w Adzie jest podobnie -
elaboracji części deklaratywnej głównego podprogramu nie da się objąć
obsługą wyjątków, co po ludzku oznacza, że coś tam się dzieje przed
pierwszym try. Z tym samym efektem, co w C++.

Maciej Sobczak

unread,
Jan 15, 2007, 3:40:25 AM1/15/07
to
SasQ wrote:

> Jest jakiś dobry mechanizm by powiadomić użytkownika, że
> uruchomienie programu się nie powiodło i z jakiego powodu?

OK, proste rozwiązanie dla C++:

#include <iostream>
#include <cstdlib>

// jakas klasa,
class C
{
public:
// ktorej konstruktor moze rzucic wyjatek:
C() { throw 0; }

void foo() { /* ... */ }
};

// zamiast obiektu globalnego klasy C robimy
// wrapperek,
struct CWrapper
{
CWrapper()
// ktory zlapie wyjatek z inicjalizacji obiektu klasy C,
try : c() {}
catch (...)
{
// i ladnie go obsluzy
std::cout << "Shit happened before main!\n";
std::exit(EXIT_FAILURE);
}

C c;
} g_CWrapper;

// a i tak mozna sie do niego odwolywac tak,
// jakby byl zadeklarowany globalnie:
C &c = g_CWrapper.c;

int main()
{
// tutaj widzimy c jak zwykly obiekt globalny:
c.foo();
}


Jeśli ten trick nie był jeszcze publikowany, to uprzejmie prosi się o
używanie nazwy "globalne try Sobczaka". ;-)

A jak już był opublikowany, to najwyraźniej ktoś mi ukradł pomysł zanim
na niego wpadłem (ktoś może oglądał film pt. "Maverick"?).

Marcin 'Qrczak' Kowalczyk

unread,
Jan 15, 2007, 6:32:08 AM1/15/07
to
Maciej Sobczak <no....@no.spam.com> writes:

> Na pociechę można podać fakt, że nawet w Adzie jest podobnie -
> elaboracji części deklaratywnej głównego podprogramu nie da się objąć
> obsługą wyjątków, co po ludzku oznacza, że coś tam się dzieje przed
> pierwszym try. Z tym samym efektem, co w C++.

A w Kogucie można :-P

SasQ

unread,
Jan 15, 2007, 9:27:09 AM1/15/07
to
Dnia Sat, 16 Dec 2006 11:08:35 +0100, Marcin 'Qrczak' Kowalczyk
napisał(a):

>> Ekran monitora istnieje od samego początku działania programu
>
> Ale nie graficzny. Wyobraź sobie program, który w zależności od
> sposobu uruchomienia korzysta z grafiki lub nie. Jeśli nie korzysta,
> to może być uruchomiony nawet tam, gdzie grafiki nie ma (np. sesja
> ssh bez tunelowania X-ów).

W moim przypadku akurat ma istnieć od początku ;)
Chodzi mi o uzyskanie pewnej abstrakcji ekranu graficznego w czymś
na kształt "silnika" do gier / biblioteki graficznej. Konieczność
"tworzenia" ekranu, czy jakiejś innej inicjalizacji ekranu zazwyczaj
wywołuje zdziwienie na twarzy początkujących użytkowników różnych
bibliotek graficznych, a przynajmniej u mnie i kilku innych osób
to zaobserwowałem ;J i spotkałem się ze stwierdzeniami: "Czemu tu
trzeba tworzyć ekran, jak on już istnieje?"]. Dlatego chciałem
ukryć przed użytkownikiem biblioteki całe to tworzenie ekranu i
w ten sposób dodatkowo uniezależnić go od implementacji tego ekranu.
Biblioteka SDL jest bliska temu rozwiązaniu, bo tam również jest to
w podobny sposób ukryte i użytkownik nie musi tworzyć ekranu, tylko
za pomocą funkcji SDL_SetVideoMode() pozyskuje strukturę opisującą
ekran już istniejący. Niestety tam tworzenie ekranu zrealizowano w
funkcji SDL_Init(), którą trzeba wywołać by korzystać z ekranu.
Ktoś może tego zapomnieć :P Dobrze zaprojektowany system nie powinien
polegać na ostrzeżeniach w dokumentacji [w stylu "zanim użyjesz ekranu,
musisz wywołać SDL_Init()"], lecz na dobrej spójnej konstrukcji i
i dobrych abstrakcjach ;J.
Chciałbym osiągnąć taki efekt, że użytkownik nie musi pamiętać o
inicjalizacji biblioteki, bo wykona się ona sama, już w samej
implementacji tej biblioteki, a ekran [czymkolwiek on będzie] będzie
już istniał od początku, gotowy do użycia i wołania na nim metod ;J
Nie musiałby też się przejmować, czym ten ekran jest: czy jest to
obszar roboczy wewnątrz okienka, okno na pełnym ekranie, bufor ramki
czy bitmapka zapisywana na dysku :P Po prostu weźmie ten gotowy ekran
i zacznie po nim rysować ;J
Moje założenie było właśnie takie, by używanie ekranu przypominało
używanie strumieni standardowego wejścia/wyjścia, które też są w
programie dostępne od początku jego istnienia i nie trzeba sobie
zawracać głowy ich inicjalizacją czy tworzeniem, bo tworzy je i
przygotowuje do pracy sama implementacja biblioteki.
Stąd moje pytania o inicjalizację obiektów statycznych, rzucanie
wyjątków gdy coś pójdzie źle podczas takiej inicjalizacji, oraz
możliwość reagowania użytkownika biblioteki na trudności z jej
inicjalizacją.

--
SasQ

0 new messages