Mittwoch, 2. Januar 2008

Systemunabhängiger Code mit Boost


Bereits im letzten Beitrag habe ich die Boost-Library erwähnt, als ich ein praktisches Beispiel über den Umgang mit RAII-Objekten listete. Allerdings genügt nicht immer eine einfache Inkludierung der entsprechenden Headerdatei. So müssen für einige Teile der Bibliothek Shared-Libraries installiert werden, da diese systemabhängige Teile kapseln. Hierbei handelt es sich um:


date_time, filesystem, graph, iostreams, program_options, python, regex, serialization, signals, test, thread, wave


In diesem Beitrag werde ich anhand eines Beispiels mit Threads zeigen, wie man diese Bibliotheken richtig kompiliert und installiert. Man wird sehen, dass der exakt selbe Sourcecode danach unter Linux und Windows kompiliert und ausgeführt werden kann.

Das Beispiel-Programm sieht folgendermassen aus:

    1 #include <boost/thread/thread.hpp>
2 #include <boost/thread/xtime.hpp>
3 #include <boost/bind.hpp>
4 #include <boost/thread/mutex.hpp>
5 #include <iostream>
6 #include <cstdlib>
7
8 boost::mutex cnt_mutex;
9
10 void helloworld(unsigned int n = 1)
11 {
12 {
13 boost::mutex::scoped_lock lock(cnt_mutex);
14 std::cout << "Thread " << n << ": Hello World!" << std::endl;
15 }
16 boost::xtime xt;
17 boost::xtime_get(&xt, boost::TIME_UTC);
18 xt.sec += 60*60;
19 boost::thread::sleep(xt);
20 }
21
22 int main()
23 {
24 boost::thread_group thrd;
25
26 for(int i=0; i<5; i++){
27 thrd.create_thread(boost::bind(helloworld,i));
28 }
29 thrd.join_all();
30
31
32 system("PAUSE");
33 return EXIT_SUCCESS;
34 }




  • In der Funktion main wir eine Thread-Group mit fünf Threads initialisiert, die alle die Funktion helloworld aufrufen.

  • bind erstellt hier ein Funktionsobjekt mit dem entsprechenden Argument (hier die Nummerierung der Threads).

  • Mit dem globalen Mutex wird der cout vor gleichzeitigem Zugriff der Threads gesichert (durch scoped_lock wird der Mutex ähnlich wie beim shared_ptr am Ende seines Gültigkeitsbereichs wieder freigegeben).

  • Anschliessend werden die Threads schlafen gelegt (nur so zum Spass).




Installation unter Linux


  • Im root-Verzeichnis von Boost mit configure die Installation konfigurieren:

    ./configure --with-libraries=thread --libdir=/usr/local/lib --includedir=/usr/local/include

    (weitere Bibliotheken können dem Argument with-libraries komagetrennt angefügt werden).

  • make & make install

  • vi /etc/ld.so.conf

    +/usr/local/lib

    So wird dem System bekannt gemacht wo sich die Shared-Libraries befinden wenn ein Programm darauf zugreifen möchte.

  • run: ldconfig



Ein Programm kann anschliessend wie folgt übersetzt werden:
gcc -I/usr/local/include/boost_1_34_1 -L/usr/local/lib -lboost_thread-gcc41-mt-1_34_1 boost_test.cpp -o boost_test



Oder mit dem folgenden Makefile:


CC = gcc
OBJECTS = boost_test.o
LIBS = -L/usr/local/lib -lboost_thread-gcc41-mt-1_34_1 -L/usr/lib
LIBPATH = -I/usr/local/include/boost_1_34_1
CFLAGS = -c -O

boost_test: $(OBJECTS)
$(CC) -o $@ $(OBJECTS) $(LIBS)

boost_test.o: boost_test.cpp
$(CC) $(CFLAGS) $(LIBPATH) $*.cpp

clean:
rm *.o boost_test


Installation unter Windows
Unter Windows erfordert die Installation mehr Schritte. Ich verwende einerseits die Entwicklungsumbebung Dev-C++ 4.9.9.2 mit dem GNU GCC 3.4.5 Compiler und andererseits Microsoft Visual C++ 2008 Express Edition mit dem Microsoft 32-bit C/C++ Optimizing Compiler Version 15.


Um die Bibliotheken kompilieren zu können, muss BJam separat installiert werden. Eine detaillierte Anleitung hierfür finden sie hier.


Meine Umgebungsvariablen sehen wie folgt aus:


DevEnvDir=C:\Programme\Microsoft Visual Studio 9.0\Common7\IDE

PATH=%PATH%;C:\Programme\Microsoft Visual Studio 9.0\Common7\IDE;C:\Programme\Microsoft Visual Studio 9.0\VC\BIN;C:\Programme\Microsoft Visual Studio 9.0\Common7\Tools;C:\WINDOWS\Microsoft.NET\Framework\v3.5;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;C:\Programme\Microsoft Visual Studio 9.0\VC\VCPackages;C:\Boost\lib

INCLUDE=C:\Programme\Microsoft Visual Studio 9.0\VC\INCLUDE;C:\Boost\include\boost-1_34_1

LIB=C:\Programme\Microsoft Visual Studio 9.0\VC\LIB;C:\Boost\lib

LIBPATH=C:\WINDOWS\Microsoft.NET\Framework\v3.5;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;C:\Programme\Microsoft Visual Studio 9.0\VC\LIB;C:\Boost\lib

Patchen vor Beginn:
Die Boost-Bibliothek 1.34.1 ist noch nicht für Visual C++ 9.0 upgedated worden. Darum sollten vor Beginn der Installation folgende Zeilen in den Sourcen angepasst werden:
lists.boost.org


(Falls der Link nicht mehr aktuell ist finden Sie die erforderlichen Änderungen am Ende dieses Posts)

Installieren von BJam (mit msvc):
c:\...\boost_1_34_1\tools\jam\build_dist.bat
oder
c:\...\boost_1_34_1\tools\jam\src\build.bat msvc

Installieren/Kompillieren der gewünschten Libraries:
%path_to_bjam% --toolset=compiler --with-%library-list% install
z.B.
c:\...\boost_1_34_1>tools\jam\src\boost-jam-3.1.14-1-ntx86\bjam --toolset=msvc --with-thread install
c:\...\boost_1_34_1>tools\jam\src\boost-jam-3.1.14-1-ntx86\bjam --toolset=gcc --with-thread install


Projekteinstellungen:
Bei beiden Entwicklungsumgebungen muss nun das include- und lib-Verzeichnis angegeben werden:

C:\Boost\include\boost-1_34_1

C:\Boost\lib



Bei Dev-C++ müssen zusätzlich die Bibliotheken unter dem Register Parameter dem Linker bekannt gemacht werden. VC++ findet dies über die Umgebungsvariablen.


Ausgabe des Programms:





Die Reihenfolge muss nicht immer genau diese sein. Im Taskmanager kann man die Anzahl Threads des Programms anzeigen lassen.



Boost-Patch für Visual C++ 9.0:


msvc.jam
{
# Even if version is not explicitly specified, try to detect the version
# from the path.
+ if [ MATCH "(Microsoft Visual Studio 9.0)" : $(command) ]
+ {
+ version = 9.0 ;
+ }
+else if [ MATCH "(Microsoft Visual Studio 8)" : $(command) ]
-if [ MATCH "(Microsoft Visual Studio 8)" : $(command) ]
{
version = 8.0 ;
.ProgramFiles = [ path.make [ common.get-program-files-dir ] ] ;
-.known-versions = 8.0 8.0express 7.1 7.1toolkit 7.0 6.0 ;
+.known-versions = 9.0 9.0express 8.0 8.0express 7.1 7.1toolkit 7.0 6.0 ;
# Version aliases
.version-alias-6 = 6.0 ;
.version-alias-6.5 = 6.0 ;
.version-alias-7 = 7.0 ;
.version-alias-8 = 8.0 ;
+.version-alias-9 = 9.0 ;
# Name of the registry key that contains Visual C++ installation path
# (relative to "HKEY_LOCAL_MACHINE\SOFTWARE\\Microsoft"
.version-7.1-reg = "VisualStudio\\7.1\\Setup\\VC" ;
.version-8.0-reg = "VisualStudio\\8.0\\Setup\\VC" ;
.version-8.0express-reg = "VCExpress\\8.0\\Setup\\VC" ;
+.version-9.0-reg = "VisualStudio\\9.0\\Setup\\VC" ;
+.version-9.0express-reg = "VCExpress\\9.0\\Setup\\VC" ;
# Visual C++ Toolkit 2003 do not store its installation path in the registry.
# The environment variable 'VCToolkitInstallDir' and the default installation

auto_link.hpp
// vc71:
# define BOOST_LIB_TOOLSET "vc71"
-#elif defined(BOOST_MSVC) && (BOOST_MSVC >= 1400)
+#elif defined(BOOST_MSVC) && (BOOST_MSVC == 1400)
// vc80:
# define BOOST_LIB_TOOLSET "vc80"
+#elif defined(BOOST_MSVC) && (BOOST_MSVC >= 1500)
+
+ // vc90:
+# define BOOST_LIB_TOOLSET "vc90"
+
#elif defined(__BORLANDC__)

visualc.hpp
#error "Compiler not supported or configured - please reconfigure"
#endif
//
-// last known and checked version is 1400 (VC8):
-#if (_MSC_VER > 1400)
+// last known and checked version is 1500 (VC9):
+#if (_MSC_VER > 1500)
# if defined(BOOST_ASSERT_CONFIG)
# error "Unknown compiler version - please run the configure tests and report the results"
# else

named_slot_map.hpp
|| slot_ == other.slot_));
}
-#if BOOST_WORKAROUND(_MSC_VER, <= 1400)
+#if BOOST_WORKAROUND(_MSC_VER, <= 1500)
void decrement();
void advance(difference_type);
#endif

named_slot_map.cpp
typedef slot_container_type::const_iterator const_group_iterator;
-#if BOOST_WORKAROUND(_MSC_VER, <= 1400)
+#if BOOST_WORKAROUND(_MSC_VER, <= 1500)
void named_slot_map_iterator::decrement() { assert(false); }
void named_slot_map_iterator::advance(difference_type) { assert(false); }
#endif