- not always the modern (and often the most expensive) tools is the best option. For example: you definitely need some version control system: but for small projects git may be to complicated. Old proven SVN still is a good choice
- do re-factor after every developed feature. Do not leave „technological dept”. Your code should be ready to developing nest features.
- do not change everything in your release process in one moment: first automate a build, the tests, then write deployment scripts etc
- try to keep simple solutions: if your software consist of many components, build and release its in one deployment pipeline. You will split components into different plans in your build server when it is necessary
8 lip 2012
Continuous Delivery by Jez Humble, David Farley
I have just read book „Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation” by Jez Humble, David Farley. Great position for all of you, who already had problems with releasing software. Have you ever got stuck with builds, which couldn't be released because all those showstopper bugs still were discovered during manual tests? How many times you heard from your developer „It works on my machine”? How many hours your team must spend in order to make and test build for the client? 2 days of whole team work isn't enough?
Just read „ Continuous Delivery...” and look how release process may be repeatable, pain less, fully automated. Book is written by experienced software engineers, who gave a lot of problems from real projects and how it were improved. During reading I reminded myself I meet similar problems in my projects.
What impressed me:
28 lip 2010
"undefined reference" - jak zdiagnozować takie błędy?
Jednym z bardziej irytujących błędów jakie możemy otrzymać kompilując projekt jest błąd linkowania "undefined reference".
np.:
example.cpp: undefined reference to `boost::program_options::option_description::option_description()'
Jeśli błąd dotyczy naszego kodu, to pół biedy. Wyświetlane w komunikatach nazwy coś nam mówią. Wiemy, co ostatnio modyfikowaliśmy w kodzie.
Gorzej jeżeli linker nie może znaleźć obiektu z używanej bibioteki, w dodatku jej wywołania znajdują się w innej używanej bibliotece. Wtedy to nazwy poszukiwanych przez linker symboli niewiele nam mówią.
Poniżej znajduje się kilka porad i narzędzi użytecznych podczas analizy błędów "undefined reference".
1. Należy się upewnić, że linkujemy w projekcie bibliotekę zawierającą funkcję poszukiwaną przez linker.
W powyższym przypadku musimy sprawdzić, czy wywołanie g++ (gcc) zawiera parametr:
Jeżeli linkujemy odpowiednią bibliotekę a mimo to nadal mamy błąd "undefined reference", to:
2. Sprawdzamy, czy w linkowanej biblioteece faktycznie znajduje się poszukiwany symbol.
Powyższe wywołanie readelf listuje wszystkie symbole jakie znajdują się w dynamicznej bibliotece.
Niestety symbole listowane przez readelf są udekorowane (ang. name mangling), a linker pokazuje nazwy symboli z języka C++ bez dekoracji.
Na szczęście istnieje w Linuxie narzędzie do zamiany dekorowanej nazwy na deklarację C++: c++filt.
Możemy użyć następującego zestawu instrukcji do zamiany dekoracji na identyfikatory C++ dla wszystkich symboli w danej bibliotece:
Teraz możemy odszukać symbolu w bibliotece.
Jeżeli go tam znajdziemy, to zapewne g++ podczas linkowania używa tej biblioteki w innej wersji (czyli używa innego pliku niż ten, który badaliśmy za pomocą readelf). Należy sprawdzić ścieżki podawane jako parametr -L.
Jeżeli symbolu nie znajdziemy, to prawdopodobnie do linkowania użyte zostały headery w innej wersji niż linkowana biblioteka. W takim przypadku może pomóc reinstalacja biblioteki.
np.:
example.cpp: undefined reference to `boost::program_options::option_description::option_description()'
Jeśli błąd dotyczy naszego kodu, to pół biedy. Wyświetlane w komunikatach nazwy coś nam mówią. Wiemy, co ostatnio modyfikowaliśmy w kodzie.
Gorzej jeżeli linker nie może znaleźć obiektu z używanej bibioteki, w dodatku jej wywołania znajdują się w innej używanej bibliotece. Wtedy to nazwy poszukiwanych przez linker symboli niewiele nam mówią.
Poniżej znajduje się kilka porad i narzędzi użytecznych podczas analizy błędów "undefined reference".
1. Należy się upewnić, że linkujemy w projekcie bibliotekę zawierającą funkcję poszukiwaną przez linker.
W powyższym przypadku musimy sprawdzić, czy wywołanie g++ (gcc) zawiera parametr:
g++ ... -lboost_program_options
Jeżeli linkujemy odpowiednią bibliotekę a mimo to nadal mamy błąd "undefined reference", to:
2. Sprawdzamy, czy w linkowanej biblioteece faktycznie znajduje się poszukiwany symbol.
readelf -s W /usr/lib/libboost_serialization.so
Powyższe wywołanie readelf listuje wszystkie symbole jakie znajdują się w dynamicznej bibliotece.
Niestety symbole listowane przez readelf są udekorowane (ang. name mangling), a linker pokazuje nazwy symboli z języka C++ bez dekoracji.
Na szczęście istnieje w Linuxie narzędzie do zamiany dekorowanej nazwy na deklarację C++: c++filt.
Możemy użyć następującego zestawu instrukcji do zamiany dekoracji na identyfikatory C++ dla wszystkich symboli w danej bibliotece:
readelf -s -W /usr/lib/libboost_program_options.so | awk '$8 != "" { print $8; }' | c++filt
Teraz możemy odszukać symbolu w bibliotece.
Jeżeli go tam znajdziemy, to zapewne g++ podczas linkowania używa tej biblioteki w innej wersji (czyli używa innego pliku niż ten, który badaliśmy za pomocą readelf). Należy sprawdzić ścieżki podawane jako parametr -L.
Jeżeli symbolu nie znajdziemy, to prawdopodobnie do linkowania użyte zostały headery w innej wersji niż linkowana biblioteka. W takim przypadku może pomóc reinstalacja biblioteki.
Etykiety:
undefined reference symbol
24 maj 2010
Logowanie do pliku za pomocą log4cpp
Już od dawna używam log4cpp. Dopóki rozwijałem soft, to starczało mi logowanie na konsolę.
Teraz przyszedł czas na wdrożenie i trzeba przerobić plik konfiguracyjny log4cpp tak, aby logował do pliku.
Zadanie na 10 minut - tak mi się wydawało...
Ustawiłem wszystko tak jak dla log4j (fragment dotyczący appendera do pliku):
i niby loguje się do pliku, ale jego nazwa to: 'foobar'.
Ręce opadają... żadnej dokumentacji w log4cpp do konfiguratora properties... w dodatku nie są w pełni kompatybilni z log4j.
Na szczęście log4cpp to projekt opensource. Co najlepiej zastąpi dokumentację?
Oczywiście kod źródłowy! Wystarczy poszukać ciągu 'foobar' i już wiadomo:
prawidłowa opcja dla nazwy pliku to 'fileName':
Uff! Szkoda że nie pomyślałem o kodzie źródłowym log4cpp godzinę wcześniej :-)
Teraz przyszedł czas na wdrożenie i trzeba przerobić plik konfiguracyjny log4cpp tak, aby logował do pliku.
Zadanie na 10 minut - tak mi się wydawało...
Ustawiłem wszystko tak jak dla log4j (fragment dotyczący appendera do pliku):
log4j.appender.fileAppender=org.apache.log4j.FileAppender
log4j.appender.File=app.log
log4j.appender.fileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern=%d %m %x %n
i niby loguje się do pliku, ale jego nazwa to: 'foobar'.
Ręce opadają... żadnej dokumentacji w log4cpp do konfiguratora properties... w dodatku nie są w pełni kompatybilni z log4j.
Na szczęście log4cpp to projekt opensource. Co najlepiej zastąpi dokumentację?
Oczywiście kod źródłowy! Wystarczy poszukać ciągu 'foobar' i już wiadomo:
prawidłowa opcja dla nazwy pliku to 'fileName':
log4j.appender.fileAppender=org.apache.log4j.FileAppender
log4j.appender.fileName=app.log
log4j.appender.fileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern=%d %m %x %n
Uff! Szkoda że nie pomyślałem o kodzie źródłowym log4cpp godzinę wcześniej :-)
Etykiety:
log4cpp
1 lut 2010
boost::serialization i dziwne błędy kompilacji
Błędy kompilacji kodu używającego biblioteki boost::serialization są trudne do zrozumienia.
Jednym z nich jest błąd:
Przyczyna jest prozaiczna: serializowalne pola nie mogą być const.
Jest to oczywiste, gdy wiemy że serializacja musi utworzyć "pusty" obiekt i potem wypełnić wartościami jego pola.
Dlatego też serializowalna klasa musi mieć:
Oczywiście z komunikatu o błędzie nie możemy tego wywnioskować... takie to już są uroki bibliotek wykorzystujących template-y...
Jednym z nich jest błąd:
/usr/include/boost/archive/detail/iserializer.hpp:570: error: no matching function for call to ‘load_wrapper(boost::archive::text_iarchive&, const std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, boost::serialization::is_wrapper<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >)’
Przyczyna jest prozaiczna: serializowalne pola nie mogą być const.
Jest to oczywiste, gdy wiemy że serializacja musi utworzyć "pusty" obiekt i potem wypełnić wartościami jego pola.
Dlatego też serializowalna klasa musi mieć:
- konstruktor bezparametrowy
- pola modyfikowalne (bez const)
Oczywiście z komunikatu o błędzie nie możemy tego wywnioskować... takie to już są uroki bibliotek wykorzystujących template-y...
27 sty 2010
Deklaracje zapowiadające w celu przyspieszenia kompilacji projektu
W projektach pisanych w C++ lubię korzystać z biblioteki boost.
Niestety ma ona jedną uciążliwą wadę: kod wykorzystujący boosta strasznie wolno się kompiluje. Przyczyną tego jest fakt, iż boost w dużym stopniu wykorzystuje template-y i to w bardzo zaawansowany sposób. Metaprogramy to dla kompilatora c++ nie lada wyzwanie!
W projektach składających się z kilkudziesięciu plikow źródłowych czas kompilacji całości przekracza kilka minut! Nawet przy kompilacji przyrostowej z użyciem make, po zmianie jednego z headerów w projekcie kompilacja może trwać powyżej minuty!
Jedną z technik przyspieszających kompilację jest unikanie dodawania #include do własnych headerów. Jeżeli tylko można należy wstępnie zadeklarować klasę w headerze, a właściwy #include dodać dopiero do pliku cpp.
Poniżej przykład:
Piszemy klasę Database, w której chcemy skorzystać z klasy XmlDocument. Ściślej: obiekty klasy XmlDocument będą przekazywane jako parametr w metodach klasy Database.
Jak zatem powinniśmy napisać header klasy Database.hpp:
A co jeśli do obiektu XmlDocument chcemy mieć dostęp nie przez referencję, ale przez smart pointer (boost::shared_ptr)?
Żeby zdefiniować smart pointer wcale nie jest potrzebny header klasy. Także tutaj wystarczy jej deklaracja zapowiadająca:
Dodatkowa wskazówka: aby uniknąć duplikowania kodu, dla klas z których chcę korzystać poprzez smart pointer, tworzę osobny - dodatowy header KlasaXXXPtr.hpp
Dla przykładu header taki dla klasy XmlDocument będzie wyglądał tak:
Niestety ma ona jedną uciążliwą wadę: kod wykorzystujący boosta strasznie wolno się kompiluje. Przyczyną tego jest fakt, iż boost w dużym stopniu wykorzystuje template-y i to w bardzo zaawansowany sposób. Metaprogramy to dla kompilatora c++ nie lada wyzwanie!
W projektach składających się z kilkudziesięciu plikow źródłowych czas kompilacji całości przekracza kilka minut! Nawet przy kompilacji przyrostowej z użyciem make, po zmianie jednego z headerów w projekcie kompilacja może trwać powyżej minuty!
Jedną z technik przyspieszających kompilację jest unikanie dodawania #include do własnych headerów. Jeżeli tylko można należy wstępnie zadeklarować klasę w headerze, a właściwy #include dodać dopiero do pliku cpp.
Poniżej przykład:
Piszemy klasę Database, w której chcemy skorzystać z klasy XmlDocument. Ściślej: obiekty klasy XmlDocument będą przekazywane jako parametr w metodach klasy Database.
Jak zatem powinniśmy napisać header klasy Database.hpp:
class XmlDocument;
class Database
{
void init( XmlDocument &document );
}
A co jeśli do obiektu XmlDocument chcemy mieć dostęp nie przez referencję, ale przez smart pointer (boost::shared_ptr)?
Żeby zdefiniować smart pointer wcale nie jest potrzebny header klasy. Także tutaj wystarczy jej deklaracja zapowiadająca:
class XmlDocument;
typedef boost::shared_ptr<XmlDocument> XmlDocumentPtr;
class Database
{
void init( XmlDocumentPtr document );
}
Dodatkowa wskazówka: aby uniknąć duplikowania kodu, dla klas z których chcę korzystać poprzez smart pointer, tworzę osobny - dodatowy header KlasaXXXPtr.hpp
Dla przykładu header taki dla klasy XmlDocument będzie wyglądał tak:
#ifndef XMLDOCUMENTPTR_HPP_
#define XMLDOCUMENTPTR_HPP_
#ifndef XMLDOCUMENT_H_
class XmlDocument;
#endif // XMLDOCUMENT_H_
#include <boost/shared_ptr.hpp>
typedef boost::shared_ptr<XmlDocument> XmlDocumentPtr;
#endif /* XMLDOCUMENTPTR_HPP_ */
Wszędzie, gdzie chcę zadeklarować smart pointera na XmlDocument po prostu wpisuję #include "XmlDocumentPtr.hpp", natomiast w pliku cpp (np. Database.cpp) muszę jeszcze dodać #include "XmlDocument.hpp".
22 sty 2010
Deklaracje zapowiadające obiektów z biblioteki xerces-c++
Staram się ograniczać liczbę #include-ów w moich headerach.
Jeśli to możliwe wstępnie deklaruję wykorzystywaną klasę, a właściwy #include tej klasy ląduje w pliku cpp. Dzięki temu projekt znacznie szybciej się kompiluje.
Przykład: w klasie SslSession chcę korzystać z obiektów Database.
SslSession.hpp wygląda tak:
Na szczęście chłopaki z apacha zapewnili inny mechanizm pozwalający na deklararowanie ich obiektów:
Jeśli to możliwe wstępnie deklaruję wykorzystywaną klasę, a właściwy #include tej klasy ląduje w pliku cpp. Dzięki temu projekt znacznie szybciej się kompiluje.
Przykład: w klasie SslSession chcę korzystać z obiektów Database.
SslSession.hpp wygląda tak:
class Database;
class SslSession
{
Database &m_db;
};
SslSession.cpp wygląda tak:#include "Database.hpp"
Niestety wstępna deklaracja obiektów z biblioteki xerces-c++ w powyższy sposób daje następujący błąd kompilacji:namespace xercesc
{
class xercesc::DOMNode;
}
/usr/include/xercesc/util/XercesDefs.hpp:110: error: declaration of namespace ‘xercesc’ conflicts with
../src/modules/../lib/XmlDocumentPtr.hpp:18: error: previous declaration of namespace ‘xercesc’ here
Okazuje się, że xerces-c ma mechanizm parametrycznego włączania/wyłączania namespace (w zależności od wersji xerces-c i użytego kompilatora), który nie współgra dobrze z klasycznymi deklaracjami zapowiadającymi.Na szczęście chłopaki z apacha zapewnili inny mechanizm pozwalający na deklararowanie ich obiektów:
#include <xercesc/util/XercesDefs.hpp>
XERCES_CPP_NAMESPACE_BEGIN
class DOMNode;
XERCES_CPP_NAMESPACE_END
Teraz wszystko ładnie się kompiluje!
18 gru 2009
Zwracanie rekordu z procedury składowanej w PostgreSQL
Jeśli z procedury/funkcji składowanej w bazie danych PostgreSQL chcemy zwrócić kilka wartości, to możemy zadeklarowąć ją jako zwracającą typ RECORD, ad-hoc utworzyć rekord z wartościami i zwrócić go.
Nie jest to trudne. Niestety "dokumentaliści" PostgreSQL zapomnieli pokazać jak taką funkcję wywołać.
Poniżej przykład znaleziony na jednym z forów Posrgresa:
Czyli wołamy SELECTA na tym, co zwróci nasza funkcja, ale jeszcze musimy zamapować wartości z rekordu na kolumny klauzulą AS.
Nie jest to trudne. Niestety "dokumentaliści" PostgreSQL zapomnieli pokazać jak taką funkcję wywołać.
Poniżej przykład znaleziony na jednym z forów Posrgresa:
CREATE FUNCTION xyz() RETURNS record AS
$$
declare
abc RECORD;
begin
abc := (1, 2);
return abc;
end;
$$
language plpgsql;
select a, b from xyz() as (a int, b int);
Czyli wołamy SELECTA na tym, co zwróci nasza funkcja, ale jeszcze musimy zamapować wartości z rekordu na kolumny klauzulą AS.
Etykiety:
PostgreSQL,
przykład,
RECORD,
wywołanie
29 paź 2009
Nie używać wielkich liter w nazwach funkcji/procedur składowanych PostgresQL!
Postanowiłem napisać swoją pierwszą procedurę składowaną w PostgreSQL.
Utworzyłem ją za pomocą kreatora w pgAdmin, ale za nic w świecie nie mogłem jej uruchomić.
Każda próba wywołania, kończyła się komunikatem:
No function matches the given name and argument types. You might need to add explicit type casts.
Sprawdziłem, że wywołuję ją w tym samym schemacie (public), w którym jest ona zdefiniowana.
Uprościłem treść procedury do minimum (usunąłem wszystkie parametry) i oto co mi zostało:
A wołam ją tak:
Nadal ten sam komunikat: nie może znaleźć wołanej procedury!
Minęła godzinka prób... ale zaraz, zaraz... nieco dziwnie to wygląda w definicji:
FUNCTION "InsertEvent"()
czasem pgAdmin wstawia mi cudzysłowia w identyfikatorach - nigdy nie zagłębiałem się dlaczego to robi. Zmieniłem wywołanie na:
Hurrra! Działa!
Gdybym od początku nazwał procedurę nie używając konwencji CamelCase, np. insert_event, to cudzysłowia nie byłyby potrzebne i wszystko by się wykonało za pierwszym razem.
Szczegółowe wyjaśnienie co się stało:
Gdy użyłem nazwy procedury: InsertEvent, pgAdmin utworzył tzw. Quoted identifier, który jest case sensitive.
Dla nazwy: insert_event pgAdmin utworzył zwykły identyfikator, który jest case insensitive.
Więcej o identufikatorach w PostgreSQL tutaj
Wniosek:
Na wszelki wypadek lepiej używać standardowej konwencji nazywania identyfikatorów postgres: wszystkie litery małe rozdzielone znakiem "_". Np. insert_event
Utworzyłem ją za pomocą kreatora w pgAdmin, ale za nic w świecie nie mogłem jej uruchomić.
Każda próba wywołania, kończyła się komunikatem:
No function matches the given name and argument types. You might need to add explicit type casts.
Sprawdziłem, że wywołuję ją w tym samym schemacie (public), w którym jest ona zdefiniowana.
Uprościłem treść procedury do minimum (usunąłem wszystkie parametry) i oto co mi zostało:
CREATE OR REPLACE FUNCTION "InsertEvent"()
RETURNS integer AS
$BODY$
BEGIN
RETURN 1;
END;$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100;
A wołam ją tak:
select InsertEvent() AS ret
Nadal ten sam komunikat: nie może znaleźć wołanej procedury!
Minęła godzinka prób... ale zaraz, zaraz... nieco dziwnie to wygląda w definicji:
FUNCTION "InsertEvent"()
czasem pgAdmin wstawia mi cudzysłowia w identyfikatorach - nigdy nie zagłębiałem się dlaczego to robi. Zmieniłem wywołanie na:
select "InsertEvent"() AS ret
Hurrra! Działa!
Gdybym od początku nazwał procedurę nie używając konwencji CamelCase, np. insert_event, to cudzysłowia nie byłyby potrzebne i wszystko by się wykonało za pierwszym razem.
Szczegółowe wyjaśnienie co się stało:
Gdy użyłem nazwy procedury: InsertEvent, pgAdmin utworzył tzw. Quoted identifier, który jest case sensitive.
Dla nazwy: insert_event pgAdmin utworzył zwykły identyfikator, który jest case insensitive.
Więcej o identufikatorach w PostgreSQL tutaj
Wniosek:
Na wszelki wypadek lepiej używać standardowej konwencji nazywania identyfikatorów postgres: wszystkie litery małe rozdzielone znakiem "_". Np. insert_event
8 wrz 2009
Serwer ssl wykorzystujący boost::asio zawsze korzysta z jednego wątka
Napisałem serwer wykorzystujący boost::asio::ssl, który zawierał pulę 10 wątków obsługujących żądania klientów.
Szybko jednak okazało się, że żądania od klientów zawsze są obsługiwane w jednym wątku roboczym:
jeśli wątek ten wykonywał długą operację, to request od innego klienta czekał aż operacja w pierwszym wątku zakończy się.
Dokładną przyczynę tego błędu opisałem na grupie boost-users w poście:
"[asio] SSL enabled http server3 example is blocking"
Rozwiązanie problemu jest dość proste:
nie wykonywać długich operacji bezpośrednio w handlerze wywoływanym przez async_read(), async_write(). Zamiast tego handler powinien wysłać tę operację do io_serwis, aby wykonała się asynchoriniczie.
Przykład handlera wywyoływanego po asynchronicznym odczycie danych (requestu od klienta):
void Request::readHeaderHandler( const boost::system::error_code
&error, std::size_t bytesTransferred )
{
m_ioService.post( boost::bind
( &Request::executeCommandHandler::executeCommandHandler,
shared_from_this() ) );
}
void Request::executeCommandHandler()
{
// long running operation
char c;
}
Szybko jednak okazało się, że żądania od klientów zawsze są obsługiwane w jednym wątku roboczym:
jeśli wątek ten wykonywał długą operację, to request od innego klienta czekał aż operacja w pierwszym wątku zakończy się.
Dokładną przyczynę tego błędu opisałem na grupie boost-users w poście:
"[asio] SSL enabled http server3 example is blocking"
Rozwiązanie problemu jest dość proste:
nie wykonywać długich operacji bezpośrednio w handlerze wywoływanym przez async_read(), async_write(). Zamiast tego handler powinien wysłać tę operację do io_serwis, aby wykonała się asynchoriniczie.
Przykład handlera wywyoływanego po asynchronicznym odczycie danych (requestu od klienta):
void Request::readHeaderHandler( const boost::system::error_code
&error, std::size_t bytesTransferred )
{
m_ioService.post( boost::bind
( &Request::executeCommandHandler::executeCommandHandler,
shared_from_this() ) );
}
void Request::executeCommandHandler()
{
// long running operation
char c;
}
1 wrz 2009
Jak serwer ssl z obiektu boost::asio::stream może uzyskać adres IP klienta?
// definicja stream-u ssl opartego na sockecie tcp
typedef boost::asio::ssl::stream ssl_socket;
// kod inicjujący nasłuch serwera na sockecie zakończony wywołaniem: async_accept() / accept()
// jak już klient się połączył, to mamy obiekt ssl_socket:
ssl_socket m_socket;
// z, którego można pobrać IP klienta:
const boost::asio::ip::tcp::endpoint &endpoint = m_socket.lowest_layer().remote_endpoint();
const boost::asio::ip::address &addr = endpoint.address();
std::cout << "client: " << addr.to_string() << std::endl;
typedef boost::asio::ssl::stream
// kod inicjujący nasłuch serwera na sockecie zakończony wywołaniem: async_accept() / accept()
// jak już klient się połączył, to mamy obiekt ssl_socket:
ssl_socket m_socket;
// z, którego można pobrać IP klienta:
const boost::asio::ip::tcp::endpoint &endpoint = m_socket.lowest_layer().remote_endpoint();
const boost::asio::ip::address &addr = endpoint.address();
std::cout << "client: " << addr.to_string() << std::endl;
Jak odczytywać certyfikaty X509 za pomocą openssl?
Ano tak:
http://kahdev.wordpress.com
http://kahdev.wordpress.com
Etykiety:
boosl,
certyfikat,
ssl
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!
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!
Etykiety:
__int64 illegal,
log4cpp,
visual studio
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 :-(
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 :-(
Etykiety:
MSys,
wolna kompilacja
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.
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.
Etykiety:
log4cpp,
visual studio
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:
Przykładowo:
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:
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ę:
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!
- 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!
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!
Etykiety:
c++,
deklarowanie rzucanych wyjątków,
wyjątki
Subskrybuj:
Posty (Atom)
