31 sie 2009

error C2632: '__int64' followed by '__int64' is illegal przy korzystaniu z log4cpp

Kompilowałem pod Visual C++ 2008 projekt wykorzystujący log4cpp.

Niestety biblioteka log4cpp dawno już nie była aktualizowana, stąd nie do końca chce ona chodzić z aktualnymi kompilatorami.

Podczas kompilacji pod VC++2008 otrzymywałem następujący błąd:
error C2632: '__int64' followed by '__int64' is illegal

Nigdzie w necie nie znalazłem rozwiązania tego problemu. Powyższy komunikat nie wskazuje, że problem leży w log4cpp. Myślałem, że wina jest w Boost.

Po małym rozpoznaniu okazało się, że należy dodać:
#define LOG4CPP_HAVE_INT64_T

przed włączeniem pierwszego pliku nagłówkowego z log4cpp i wszystko gra!

Wolna kompilacja projektu za pomocą make z MSys

Mam projekt oparty o makefile.

Po uruchomieniu make z pakietu MSys muszę czekać ponad minutę (!) aby dowiedzieć, się że:
make: Nothing to be done for `all'.

Natomiast make zainstalowany z MinGW (mingw32-make.exe) wykonuje to samo w 10 sekund!

Zatem cóż, trzeba zmusić Eclipse żeby używał make-a z mingw. Skopiowałem make z MinGW do MSys i voila, teraz kompilacja projektu przyspieszyła!

Niestety daleko do szybkości linuksowej: poniżej 3 sekund na projekcie bez zmian :-(

13 sie 2009

Kompilacja log4cpp pod Visual C++ 2008

Próbowałem skompilować bibliotekę do logowania log4cpp, używając sposobu opisanego w samej bibliotece. Niestety autorzy przygotowali tylko plik dsp dla Visuala 6.0, który po imporcie do Visuala 2008 powoduje następujący błąd kompilacji:

error PRJ0019: A tool returned an error code from "Performing Custom
Build Step" log4cpp

Okazuje się jednak, że wystarczy utworzyć w Visualu nowy projekt z istniejącego kodu: File / New / Project From existing code

wskazać źródłowe log4cpp

w Properties dla projektu dodać katalog /include projektu log4cpp

i tak utworzony projekt ładnie skompiluje nam bibliotekę log4cpp.

Tutaj informacja jak zmusić nagłówki log4cpp do poprawnej kompilacji w naszym programie kompilowanym pod VC++2008.

4 sie 2009

Deklarować rzucane wyjątki? Tak, ale tylko w komentarzu!

Załóżmy, że piszemy metodę, która obsługuje błędy przez rzucenie wyjątku:
int calculate( int something ) throw( MyException )
{
    int calculated = 0;

    if( something < 0 )
    {
        throw MyException( "Parametr something musi byc dodatni" );
    }

    initCalculator();

    return calculated;
}

Pytanie: czy powinniśmy zadeklarować, że metoda rzuca wyjątek?

Przykładowo:
int calculate( int something ) throw( MyException );
Bardzo mi się podoba podejście Javy w kwestii deklarowania rzucanych wyjątków:
jeśli metoda rzuca jakiś wyjątek to musi on być zadeklarowany w nagłówku metody.
Pilnuje tego kompilator: jeśli programista rzuci wyjątek bez odpowiedniej deklaracji throw w nagłówku metody, to otrzyma błąd kompilacji.

Dzięki takiemu podejściu osoba wykorzystująca naszą metodę wie jakie wyjątki mogą być przez nią rzucone, przez co unikamy błędów wykonania typu "unhandled exception".
Co więcej jeśli ktoś wywoła naszą metodę bez odpowiedniej obsługi wyjątku, to otrzyma błąd kompilacji!

A co się dzieje w c++? Niestety tutaj podejście jest mniej racjonalne :-(

Przede wszystkim: kompilator do niczego nas nie zmusza. Nie musimy deklarować rzucanego wyjątku. Ale załóżmy, że jesteśmy porządni (programowaliśmy w Javie :-) i deklarujemy wyjątek. Co się dzieje?

Spójrzmy na ten fragment kodu:
struct MyException{ MyException( const string &what ){} };
struct OtherException{ OtherException( const string &what ){} };

void initCalculator() throw( OtherException )
{
    throw OtherException( "Kalkulator nie jest zainstalowany" );
}

int calculate( int something ) throw( MyException )
{
    int calculated = 0;

    if( something < 0 )
    {
        throw MyException( "Parametr something musi być dodatni" );
    }

    initCalculator();

    return calculated;
}

int main()
{
    try
    {
        calculate( 2 );
        cout << "ok" << endl;
    }
    catch( const MyException &exc )
    {
        cout << "MyException" << endl;
    }
    catch( const OtherException &exc )
    {
        cout << "OtherException" << endl;
    }
    catch( ... )
    {
        cout << "całkiem inny wyjątek" << endl;
    }
    return 0;
}

Wynik działania programu jest taki:
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

Jak widzimy main() wywołuje calculate(), która wywołuje initCalculator();
Niestety initCalculator() rzuca nam wyjątek OtherException, który został zadeklarowany w initCalculator() ale nie został zadeklarowany w calculate().

I teraz najciekawsze:
Wyjątek OtherException nie zostanie złapany w main, pomimo że mamy tam odpowiednią klauzulę catch( OtherException )!!!

Co się dzieje?
Kompilator widzi deklarację:
int calculate( int something ) throw( MyException )
i zakłada, że tylko wyjątki typu MyException mogą być przez nią rzucone.
Jeśli calculate() rzuci inny wyjątek, to natychmiast spowoduje to wywołanie globalnej funkcji kończącej program (ustawianej przez ::set_terminate()).

A jeśli nie zadeklarujemy, że metoda calculate() rzuca jakikolwiek wyjątek, to wyjątkek rzucony przez initCalculator() zostanie złapany w main(). Dokładnie tak, jak się tego spodziewamy!

Jak zatem postępować?

Deklarować wyjątki w sposób nieformalny, umieszczając słowo kluczowe throw w komentarzu:
int calculate( int something ) //throw( MyException )
Dzięki temu:
- użytkownik naszej metody będzie wiedział jakich wyjątków może się spodziewać
- jeśli nasza metoda calculate przypadkiem rzuci innym wyjątkiem, to catch(...) go złapie!