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:
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:
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!