From f4043ec1e9f02efc7f653274b9e4e22ef2782a51 Mon Sep 17 00:00:00 2001 From: Adrian Kummerlaender Date: Sun, 10 Aug 2014 13:09:42 +0200 Subject: Switched content formatter to kramdown * implemented language selection for automatic syntax highlighting ** language selection requires the language to be used to be passed as a class of the code element ** kramdown enables easy definition of this class attribute * kramdown offers more functionality such as table and class attribute support * updated all articles accordingly --- articles/2010-02-06_debian_auf_dem_sheevaplug.md | 44 +++- articles/2011-03-31_sheevaplug_ueberwachung.md | 112 +++++---- ...1-06-14_darstellen_von_gps_daten_mit_gnuplot.md | 114 +++++---- ...-16_kurztipp_n900_retten_ohne_neu_zu_flashen.md | 5 +- .../2011-11-08_informationen_umformen_mit_xsl.md | 196 ++++++++------- articles/2012-01-25_erfahrungen_mit_openslides.md | 45 ++-- ...-08-29_erfahrungen_mit_einer_ssd_unter_linux.md | 19 +- ...rung_mit_kvm_und_virtuelle_netzwerke_mit_vde.md | 70 +++--- ...functions_local_to_a_translation_unit_in_cpp.md | 24 +- articles/2013-10-06_notizen_zu_cpp_und_unicode.md | 82 +++--- ...res_as_tuples_using_template_metaprogramming.md | 279 +++++++++++---------- ...tiated_class_template_specializations_in_cpp.md | 67 ++--- ...4-07-11_mapping_arrays_using_tuples_in_cpp11.md | 84 ++++--- 13 files changed, 625 insertions(+), 516 deletions(-) diff --git a/articles/2010-02-06_debian_auf_dem_sheevaplug.md b/articles/2010-02-06_debian_auf_dem_sheevaplug.md index df98769..23b0a17 100644 --- a/articles/2010-02-06_debian_auf_dem_sheevaplug.md +++ b/articles/2010-02-06_debian_auf_dem_sheevaplug.md @@ -4,36 +4,54 @@ Auf dem herkömmlichen [Weg](http://www.cyrius.com/debian/kirkwood/sheevaplug/) Als erstes laden wir uns den normalen SheevaInstaller herunter mit dem man Ubuntu 9.04 auf dem Sheeva installieren kann ([klick](http://www.plugcomputer.org/index.php/us/resources/downloads?func=select&id=5)). Nach dem wir den Tarball heruntergeladen haben sollte man ihn entpacken. Als nächstes laden wir ein Debian Lenny / Squeeze Prebuild von [hier](http://www.mediafire.com/sheeva-with-debian) herunter. Nun ersetzen wir die „rootfs.tar.gz“ mit der neuen Debian „rootfs.tar.gz“. Bevor wir jedoch mit der Installation beginnen müssen wir die neue „rootfs.tar.gz“ mit „tar“ entpacken. Dazu führen wir auf der Konsole - mkdir rootfs - cd rootfs - tar xfvz rootfs.tar.gz +~~~ +mkdir rootfs +cd rootfs +tar xfvz rootfs.tar.gz +~~~ +{: .language-sh} aus. In dem Ordner führen wir nun ein - mknod -m 660 dev/ttyS0 c 4 64 +~~~ +mknod -m 660 dev/ttyS0 c 4 64 +~~~ +{: .language-sh} aus. Mit diesem Befehl wird die fehlende serielle Konsole erstellt. Jetzt verpacken wir das Archiv wieder mit - tar cfvz ../rootfs.tar.gz * +~~~ +tar cfvz ../rootfs.tar.gz * +~~~ +{: .language-sh} Den gesamten Inhalt des Ordners /install im SheevaInstaller-Verzeichniss müssen wir jetzt auf einen mit FAT formatierten USB-Stick kopieren. Diesen stecken wir schon einmal in den USB-Port des Sheevas. Als nächstes verbinden wir den SheevaPlug mit einem Mini-USB Kabel mit unserem Computer und führen das Script „runme.php“ aus. Wenn alles klappt wird nun ein neuer Boot-Loader und das rootfs auf den Plug kopiert. Dannach ist unser Debian einsatzbereit. Sollte der Plug den USB-Stick nicht erkennen und somit auch das rootfs nicht kopieren können hilft es einen anderen USB-Stick zu verwenden. Beispielsweise wurde mein SanDisk U3 Stick trotz gelöschtem U3 nicht erkannt. Mit einem anderen Stick funktionierte es jedoch tadelos. Auch sollte der Stick partitioniert sein. Der Bootloader des SheevaPlugs kann nicht gut mit direkt auf dem Stick enthalten Dateisystemen umgehen. Wenn die MiniUSB Verbindung beim Ausführen des runme-Scripts nicht funktionieren sollte hilft oft ein - rmmod ftdi_sio +~~~ +rmmod ftdi_sio +~~~ +{: .language-sh} und - modprobe ftdi_sio vendor=0x9e88 product=0x9e8f +~~~ +modprobe ftdi_sio vendor=0x9e88 product=0x9e8f +~~~ +{: .language-sh} (beides als root). Ebenfalls sollte kein zweites Serielles-Gerät am Computer angeschlossen sein. Bei neueren Plugs kann man bei Problemen bei der Installation die Datei „uboot/openocd/config/interface/sheevaplug.cfg“ im SheevaInstaller-Ordner nach - interface ft2232 - ft2232_layout sheevaplug - ft2232_vid_pid 0×0403 0×6010 - #ft2232_vid_pid 0x9e88 0x9e8f - #ft2232_device_desc “SheevaPlug JTAGKey FT2232D B” - jtag_khz 2000 +~~~ +interface ft2232 +ft2232_layout sheevaplug +ft2232_vid_pid 0×0403 0×6010 +#ft2232_vid_pid 0x9e88 0x9e8f +#ft2232_device_desc “SheevaPlug JTAGKey FT2232D B” +jtag_khz 2000 +~~~ +{: .language-sh} umändern. Wenn Debian jetzt auf dem SheevaPlug läuft und man sich über SSH einloggen konnte (pwd: nosoup4u), sollte man als erstes ein Update machen. diff --git a/articles/2011-03-31_sheevaplug_ueberwachung.md b/articles/2011-03-31_sheevaplug_ueberwachung.md index 74dfa88..0cbea1c 100644 --- a/articles/2011-03-31_sheevaplug_ueberwachung.md +++ b/articles/2011-03-31_sheevaplug_ueberwachung.md @@ -9,7 +9,10 @@ Den Systemmonitor Conky lasse ich mit dem Befehl `ssh -X -vv -Y -p 22 adrian@ast Zusätzlich loggt der SheevaPlug regelmäßig die aktuellen Systemdaten wie CPU-Auslastung, Netzwerkverkehr und belegten Arbeitsspeicher und generiert sie zu Graphen die mir dann jede Nacht per eMail zugesand werden. Zum Loggen der Daten verwende ich dstat das mit folgendem, von einem Cron-Job gestarteten Befehl aufgerufen wird: - dstat -tcmn 2 1 | tail -1 >> /var/log/systat.log +~~~ +dstat -tcmn 2 1 | tail -1 >> /var/log/systat.log +~~~ +{: .language-sh} Die Argumente -tcmn geben hierbei die zu loggenden Systemdaten und deren Reihenfolge an – heraus kommen Zeilen wie diese: @@ -17,61 +20,70 @@ Die Argumente -tcmn geben hierbei die zu loggenden Systemdaten und deren Reihenf Um 0 Uhr wird dann die Log-Datei von einem Cron-Job mit diesem Script wegkopiert, Graphen werden von gnuplot generiert und dann mit einem kleinen Python-Programm versendet. - #!/bin/sh - mv /var/log/systat.log /root/sys_graph/stat.dat - cd /root/sys_graph/ - ./generate_cpu.plot - ./generate_memory.plot - ./generate_network.plot - ./send_report.py +~~~ +#!/bin/sh +mv /var/log/systat.log /root/sys_graph/stat.dat +cd /root/sys_graph/ +./generate_cpu.plot +./generate_memory.plot +./generate_network.plot +./send_report.py +~~~ +{: .language-sh} Hier das gnuplot-Script zur Erzeugung des CPU-Graphen als Beispiel: - #!/usr/bin/gnuplot - set terminal png - set output "cpu.png" - set title "CPU usage" - set xlabel "time" - set ylabel "percent" - set xdata time - set timefmt "%d-%m %H:%M:%S" - set format x "%H:%M" - plot "stat.dat" using 1:4 title "system" with lines, \ - "stat.dat" using 1:3 title "user" with lines, \ - "stat.dat" using 1:5 title "idle" with lines +~~~ +#!/usr/bin/gnuplot +set terminal png +set output "cpu.png" +set title "CPU usage" +set xlabel "time" +set ylabel "percent" +set xdata time +set timefmt "%d-%m %H:%M:%S" +set format x "%H:%M" +plot "stat.dat" using 1:4 title "system" with lines, \ +"stat.dat" using 1:3 title "user" with lines, \ +"stat.dat" using 1:5 title "idle" with lines +~~~ +{: .language-sh} … und hier noch das Python-Programm zum Versenden per Mail: - #!/usr/bin/python2 - import smtplib - from time import * - from email.mime.image import MIMEImage - from email.mime.multipart import MIMEMultipart - - lt = localtime() - - # Mail-Header - msg = MIMEMultipart() - msg['Subject'] = strftime('Leistungsreport – %d%m%Y', lt) - msg['From'] = 'reports@asterix' - msg['To'] = 'mail@mail.mail' - - msg.preamble = strftime('Leistungsreport – %d%m%Y', lt) - - # Attachments - fileArray = ['cpu.png','memory.png','network.png'] - for file in fileArray: - fp = open(file, ‘rb’) - img = MIMEImage(fp.read()) - fp.close() - msg.attach(img) - - # Login in SMTP-Server - s = smtplib.SMTP('smtpmail.t-online.de') - s.login('mail@mail.mail', '#####') - - s.sendmail('mail@mail.mail', 'mail@mail.mail', msg.as_string()) - s.quit() +~~~ +#!/usr/bin/python2 +import smtplib +from time import * +from email.mime.image import MIMEImage +from email.mime.multipart import MIMEMultipart + +lt = localtime() + +# Mail-Header +msg = MIMEMultipart() +msg['Subject'] = strftime('Leistungsreport – %d%m%Y', lt) +msg['From'] = 'reports@asterix' +msg['To'] = 'mail@mail.mail' + +msg.preamble = strftime('Leistungsreport – %d%m%Y', lt) + +# Attachments +fileArray = ['cpu.png','memory.png','network.png'] +for file in fileArray: + fp = open(file, ‘rb’) + img = MIMEImage(fp.read()) + fp.close() + msg.attach(img) + +# Login in SMTP-Server +s = smtplib.SMTP('smtpmail.t-online.de') +s.login('mail@mail.mail', '#####') + +s.sendmail('mail@mail.mail', 'mail@mail.mail', msg.as_string()) +s.quit() +~~~ +{: .language-python} Als Endergebniss erhalte ich dann täglich solche Grafiken per Mail: diff --git a/articles/2011-06-14_darstellen_von_gps_daten_mit_gnuplot.md b/articles/2011-06-14_darstellen_von_gps_daten_mit_gnuplot.md index 8687ba1..83202a3 100644 --- a/articles/2011-06-14_darstellen_von_gps_daten_mit_gnuplot.md +++ b/articles/2011-06-14_darstellen_von_gps_daten_mit_gnuplot.md @@ -4,43 +4,53 @@ Bei meiner letzten Wanderung in den Schweizer Alpen habe ich spaßeshalber das N Die Daten der Messpunkte sind im XML als `trkpt`-Tags gespeichert. Enthalten sind jeweils der Längen- und Breitengrad, die Uhrzeit, der Modus (3d / 2d), die Höhe über Null und die Anzahl der zur Positionsbestimmung genutzten Satelliten. Aussehen tut das ganze dann z.B. so: - - - 3d - 870 - 6 - +~~~ + + + 3d + 870 + 6 + +~~~ +{: .language-xml} Diese Daten lassen sich nun sehr einfach Verarbeiten – ich habe das Python `xml.dom.minidom` Modul verwendet. Um die Positionen einfacher verwenden zu können, werden sie mit dieser Funktion in Listenform gebracht: - def getPositions(xml): - doc = minidom.parse(xml) - node = doc.documentElement - rawTrkPt = doc.getElementsByTagName("trkpt") - positions = [] - for TrkPt in rawTrkPt: - pos = {} - pos["lat"] = TrkPt.getAttribute("lat") - pos["lon"] = TrkPt.getAttribute("lon") - pos["ele"] = int(TrkPt.getElementsByTagName("ele")[0].childNodes[0].nodeValue) - positions.append(pos) - return positions +~~~ +def getPositions(xml): + doc = minidom.parse(xml) + node = doc.documentElement + rawTrkPt = doc.getElementsByTagName("trkpt") + positions = [] + for TrkPt in rawTrkPt: + pos = {} + pos["lat"] = TrkPt.getAttribute("lat") + pos["lon"] = TrkPt.getAttribute("lon") + pos["ele"] = int(TrkPt.getElementsByTagName("ele")[0].childNodes[0].nodeValue) + positions.append(pos) + return positions +~~~ +{: .language-python} Aus dieser Liste kann ich jetzt schon einige Kennzahlen ziehen: - def printStats(gpxPositions): - highEle = gpxPositions[0]["ele"] - lowEle = gpxPositions[0]["ele"] - for pos in gpxPositions: - if pos["ele"] > highEle: - highEle = pos["ele"] - if pos["ele"] < lowEle: - lowEle = pos["ele"] - eleDiv = highEle - lowEle - print "Measure points: " + str(len(gpxPositions)) - print "Lowest elevation: " + str(lowEle) - print "Highest elevation: " + str(highEle) - print "Height difference: " + str(eleDiv) +~~~ +def printStats(gpxPositions): + highEle = gpxPositions[0]["ele"] + lowEle = gpxPositions[0]["ele"] + for pos in gpxPositions: + if pos["ele"] > highEle: + highEle = pos["ele"] + if pos["ele"] < lowEle: + lowEle = pos["ele"] + eleDiv = highEle - lowEle + print "Measure points: " + str(len(gpxPositions)) + print "Lowest elevation: " + str(lowEle) + print "Highest elevation: " + str(highEle) + print "Height difference: " + str(eleDiv) +~~~ +{: .language-python} + Die Kennzahlen für meine Testdaten wären: @@ -51,10 +61,13 @@ Die Kennzahlen für meine Testdaten wären: Da die Daten ja, wie schon im Titel angekündigt, mit gnuplot dargestellt werden sollen werden sie mit dieser Funktion in für gnuplot lesbares CSV gebracht: - def printCsv(gpxPositions): - separator = ';' - for pos in gpxPositions: - print pos["lat"] + separator + pos["lon"] + separator + str(pos["ele"]) +~~~ +def printCsv(gpxPositions): + separator = ';' + for pos in gpxPositions: + print pos["lat"] + separator + pos["lon"] + separator + str(pos["ele"]) +~~~ +{: .language-python} ### Plotten mit gnuplot @@ -62,20 +75,23 @@ Da die Daten ja, wie schon im Titel angekündigt, mit gnuplot dargestellt werden Eine solche, dreidimensionale Ausgabe der GPS Daten zu erzeugen ist mit der `splot`-Funktion sehr einfach. - #!/usr/bin/gnuplot - set terminal png size 1280,1024 - set output "output.png" - set multiplot - set yrange [9.365:9.31] - set xrange [47.325:47.28] - set zrange [800:1700] - set view 28,272,1,1 - set ticslevel 0 - set grid - set datafile separator ';' - splot "/home/adrian/projects/gpxplot/wanderung_120611.csv" with impulses lt 3 lw 1 - splot "/home/adrian/projects/gpxplot/wanderung_120611.csv" with lines lw 2 - unset multiplot +~~~ +#!/usr/bin/gnuplot +set terminal png size 1280,1024 +set output "output.png" +set multiplot +set yrange [9.365:9.31] +set xrange [47.325:47.28] +set zrange [800:1700] +set view 28,272,1,1 +set ticslevel 0 +set grid +set datafile separator ';' +splot "/home/adrian/projects/gpxplot/wanderung_120611.csv" with impulses lt 3 lw 1 +splot "/home/adrian/projects/gpxplot/wanderung_120611.csv" with lines lw 2 +unset multiplot +~~~ +{: .language-sh} Mit `set terminal png size 1280,1024` und `set output "output.png"` werden zuerst das Ausgabemedium, die Größe und der Dateiname der Ausgabe definiert. Dannach aktiviert `set multiplot` den gnuplot-Modus, bei dem mehrere Plots in einer Ausgabe angezeigt werden können. Dieses Verhalten brauchen wir hier, um sowohl die Strecke selbst als rote Line, als auch die zur Verdeutlichung verwendeten blauen Linien gleichzeitig anzuzeigen. Mit `set [y,x,z]range` werden die Außengrenzen des zu plottenden Bereichs gesetzt. Dies ließe sich natürlich auch über ein Script automatisch erledigen. Als Nächstes wird mit `set view 28,272,1,1` die Blickrichtung und Skalierung definiert. `set ticslevel 0` sorgt dafür, dass die Z-Achse direkt auf der Grundebene beginnt. Um ein Gitter auf der Grundfläche anzuzeigen, gibt es `set grid`. diff --git a/articles/2011-10-16_kurztipp_n900_retten_ohne_neu_zu_flashen.md b/articles/2011-10-16_kurztipp_n900_retten_ohne_neu_zu_flashen.md index 65fc7e7..f59b709 100644 --- a/articles/2011-10-16_kurztipp_n900_retten_ohne_neu_zu_flashen.md +++ b/articles/2011-10-16_kurztipp_n900_retten_ohne_neu_zu_flashen.md @@ -7,6 +7,9 @@ Erst sah es so aus, als würde ich nicht darum herum kommen das Betriebsystem ne Dabei handelt es sich um ein kleines Linux welches mithilfe des normalen [Flashers](http://tablets-dev.nokia.com/maemo-dev-env-downloads.php) direkt in den RAM des N900 kopiert und dort gebootet werden kann. Vom rescueOS aus kann man dann das Root-Dateisystem problemlos einbinden und Probleme beheben. Zum Starten reicht das [initrd Image](http://n900.quitesimple.org/rescueOS/rescueOS-1.0.img) und folgender Befehl: - flasher-3.5 -k 2.6.37 -n initrd.img -l -b"rootdelay root=/dev/ram0" +~~~ +flasher-3.5 -k 2.6.37 -n initrd.img -l -b"rootdelay root=/dev/ram0" +~~~ +{: .language-sh} Nähere Informationen zur Verwendung und den Funktionen finden sich in der rescueOS [Dokumentation](http://n900.quitesimple.org/rescueOS/documentation.txt). diff --git a/articles/2011-11-08_informationen_umformen_mit_xsl.md b/articles/2011-11-08_informationen_umformen_mit_xsl.md index 40777d0..47f9973 100644 --- a/articles/2011-11-08_informationen_umformen_mit_xsl.md +++ b/articles/2011-11-08_informationen_umformen_mit_xsl.md @@ -8,49 +8,57 @@ Schlussendlich habe ich dann eine [XSLT](http://de.wikipedia.org/wiki/XSLT) gesc Mit XSL lassen sich XML Dateien in andere Formen bringen. Da Mediawiki mehr oder weniger valides XHTML ausgibt, kann man, nachdem das XHTML mit [Tidy](http://tidy.sourceforge.net) ein wenig aufgeräumt wurde, sehr einfach die [Terminliste](http://wiki.piratenpartei.de/BW:Kreisverband_Konstanz/Termine) extrahieren und gleichzeitig in RSS umformen: - - - - - - - - - - Termine des KV Konstanz - http://wiki.piratenpartei.de/BW:Kreisverband_Konstanz/Termine - Termine des KV Konstanz - - - - - - - - - - - <xsl:value-of select="x:td[3]"/> am <xsl:call-template name="format-date"> - <xsl:with-param name="date" select="x:td[1]/x:span/."/> - <xsl:with-param name="format" select="'d.n.Y'"/></xsl:call-template> um <xsl:value-of select="x:td[2]"/> Uhr - http://wiki.piratenpartei.de - - - - - - - - - +~~~ + + + + + + + + + + Termine des KV Konstanz + http://wiki.piratenpartei.de/BW:Kreisverband_Konstanz/Termine + Termine des KV Konstanz + + + + + + + + + <xsl:value-of select="x:td[3]"/> + <xsl:text> am </xsl:text> + <xsl:call-template name="format-date"> + <xsl:with-param name="date" select="x:td[1]/x:span/."/> + <xsl:with-param name="format" select="'d.n.Y'"/> + </xsl:call-template> + <xsl:text> um </xsl:text> + <xsl:value-of select="x:td[2]"/> + <xsl:text> Uhr</xsl:text> + + + http://wiki.piratenpartei.de + + + + + + + + +~~~ +{: .language-xsl} Der Kern dieses XSL ist nicht mehr als ein Template welches auf den [XPATH](http://de.wikipedia.org/wiki/XPATH) zum Finden der Terminliste reagiert. Die For-Each-Schleife iteriert dann durch die Termine und formt diese entsprechend in RSS um. Der einzige Knackpunkt kommt daher, dass XHTML kein normales XML ist und somit seinen eigenen Namespace hat. Diesen sollte man im Element `xsl:stylesheet` korrekt angeben, sonst funktioniert nichts. Auch muss im XPATH Ausdruck dann vor jedem Element ein `x:` eingefügt werden um dem XSL Prozessor den Namespace für das jeweilige Element mitzuteilen. @@ -64,66 +72,72 @@ Zum Anpassen des Datums verwende ich - wie auch in diesem Blog - die [date-time. Mit der eben beschriebenen XSLT lässt sich jetzt in drei Schritten das fertige RSS generieren: - #!/bin/sh - wget -O termine_wiki.html "http://wiki.piratenpartei.de/BW:Kreisverband_Konstanz/Termine" - tidy -asxml -asxhtml -O termine_tidy.html termine_wiki.html - xsltproc --output termine_kvkn.rss --novalid termine_kvkn.xsl termine_tidy.html +~~~ +#!/bin/sh +wget -O termine_wiki.html "http://wiki.piratenpartei.de/BW:Kreisverband_Konstanz/Termine" +tidy -asxml -asxhtml -O termine_tidy.html termine_wiki.html +xsltproc --output termine_kvkn.rss --novalid termine_kvkn.xsl termine_tidy.html +~~~ +{: .language-sh} In PHP ist das ganze dann zusammen mit sehr einfachem Caching auch direkt auf einem Webserver einsetzbar: - define('CACHE_TIME', 6); - define('CACHE_FILE', 'rss.cache'); +~~~ +define('CACHE_TIME', 6); +define('CACHE_FILE', 'rss.cache'); - header("Content-Type: application/rss+xml"); +header("Content-Type: application/rss+xml"); - if ( file_exists(CACHE_FILE) ) - { - if ( !filemtime(CACHE_FILE) < time() - CACHE_TIME * 3600 ) { - readfile(CACHE_FILE); - } - else { - if ( !generate_rss() ) { - readfile(CACHE_FILE); - } - } +if ( file_exists(CACHE_FILE) ) +{ + if ( !filemtime(CACHE_FILE) < time() - CACHE_TIME * 3600 ) { + readfile(CACHE_FILE); } else { - ob_start(); - - generate_rss(); - - ob_end_flush(); - - $cache_file = fopen(CACHE_FILE, 'w'); - fwrite($cache_file, ob_get_contents()); - fclose($cache_file); + if ( !generate_rss() ) { + readfile(CACHE_FILE); + } } - - function generate_rss() +} +else { + ob_start(); + + generate_rss(); + + ob_end_flush(); + + $cache_file = fopen(CACHE_FILE, 'w'); + fwrite($cache_file, ob_get_contents()); + fclose($cache_file); +} + +function generate_rss() +{ + $event_page = file_get_contents('http://wiki.piratenpartei.de/BW:Kreisverband_Konstanz/Termine'); + + if ($event_page != false) { - $event_page = file_get_contents('http://wiki.piratenpartei.de/BW:Kreisverband_Konstanz/Termine'); - - if ($event_page != false) - { - $config = array('output-xhtml' => true, 'output-xml' => true); + $config = array('output-xhtml' => true, 'output-xml' => true); - $tidy = new tidy(); + $tidy = new tidy(); - $xml = new DomDocument(); - $xml->loadXML( $tidy->repairString($event_page, $config) ); + $xml = new DomDocument(); + $xml->loadXML( $tidy->repairString($event_page, $config) ); - $xsl = new DomDocument(); - $xsl->load('termine_kvkn.xsl'); + $xsl = new DomDocument(); + $xsl->load('termine_kvkn.xsl'); - $xslt = new XsltProcessor(); - $xslt->importStylesheet($xsl); + $xslt = new XsltProcessor(); + $xslt->importStylesheet($xsl); - $rss = $xslt->transformToXML($xml); - echo $rss; - - return true; - } - else { - return false; - } + $rss = $xslt->transformToXML($xml); + echo $rss; + + return true; + } + else { + return false; } +} +~~~ +{: .language-php} diff --git a/articles/2012-01-25_erfahrungen_mit_openslides.md b/articles/2012-01-25_erfahrungen_mit_openslides.md index 3d7c534..a3fde3a 100644 --- a/articles/2012-01-25_erfahrungen_mit_openslides.md +++ b/articles/2012-01-25_erfahrungen_mit_openslides.md @@ -20,27 +20,30 @@ Theoretisch ermöglicht es diese Software also - bei entsprechender Authentifizi Der einzige Punkt der mich wirklich störte war die fehlende Anzeige von Wahlergebnissen der Teilnehmer die eine Wahl verloren hatten. Die jeweilige Stimmenzahl war im Frontend erst sichtbar nachdem der entsprechende Kandidat als Sieger markiert worden war. Doch auch dieses Problem ließ sich - OpenSource sei Dank - schnell über manuelles Eingreifen im Quelltext korrigieren: - --- /home/adrian/Downloads/original_views.py - +++ /opt/hg.openslides.org/openslides/agenda/views.py - @@ -132,14 +132,11 @@ - for poll in assignment.poll_set.all(): - if poll.published: - if candidate in poll.options_values: - - if assignment.is_elected(candidate): - - option = Option.objects.filter(poll=poll).filter(user=candidate)[0] - - if poll.optiondecision: - - tmplist[1].append([option.yes, option.no, option.undesided]) - - else: - - tmplist[1].append(option.yes) - + option = Option.objects.filter(poll=poll).filter(user=candidate)[0] - + if poll.optiondecision: - + tmplist[1].append([option.yes, option.no, option.undesided]) - else: - - tmplist[1].append("") - + tmplist[1].append(option.yes) - else: - tmplist[1].append("-") - votes.append(tmplist) +~~~ +--- /home/adrian/Downloads/original_views.py ++++ /opt/hg.openslides.org/openslides/agenda/views.py +@@ -132,14 +132,11 @@ + for poll in assignment.poll_set.all(): + if poll.published: + if candidate in poll.options_values: +- if assignment.is_elected(candidate): +- option = Option.objects.filter(poll=poll).filter(user=candidate)[0] +- if poll.optiondecision: +- tmplist[1].append([option.yes, option.no, option.undesided]) +- else: +- tmplist[1].append(option.yes) ++ option = Option.objects.filter(poll=poll).filter(user=candidate)[0] ++ if poll.optiondecision: ++ tmplist[1].append([option.yes, option.no, option.undesided]) + else: +- tmplist[1].append("") ++ tmplist[1].append(option.yes) + else: + tmplist[1].append("-") + votes.append(tmplist) +~~~ +{: .language-diff} OpenSlides ist wirklich ein tolles Stück Software und ich kann nur jedem der vor der Aufgabe steht eine Versammlung zu organisieren, sei es die eines Vereins oder wie in meinem Fall die einer Partei, empfehlen sich es einmal näher anzusehen. Weitere Angaben zur Installation und Konfiguration finden sich auf der [Webpräsenz](http://openslides.org/de/index.html) und im [Quell-Archiv](http://openslides.org/download/openslides-1.1.tar.gz). diff --git a/articles/2012-08-29_erfahrungen_mit_einer_ssd_unter_linux.md b/articles/2012-08-29_erfahrungen_mit_einer_ssd_unter_linux.md index b714997..ebe48e5 100644 --- a/articles/2012-08-29_erfahrungen_mit_einer_ssd_unter_linux.md +++ b/articles/2012-08-29_erfahrungen_mit_einer_ssd_unter_linux.md @@ -21,14 +21,17 @@ nötig sein sollte ist das umso besser. Die 16 GiB SD-Karte des SheevaPlugs läu Hier meine derzeitige `/etc/fstab`: - # - # /etc/fstab: static file system information - # - # - tmpfs /tmp tmpfs nodev,nosuid 0 0 - - /dev/sda1 swap swap defaults,noatime,discard 0 0 - /dev/sda2 / ext4 defaults,noatime,discard,data=ordered 0 0 +~~~ +# +# /etc/fstab: static file system information +# +# +tmpfs /tmp tmpfs nodev,nosuid 0 0 + +/dev/sda1 swap swap defaults,noatime,discard 0 0 +/dev/sda2 / ext4 defaults,noatime,discard,data=ordered 0 0 +~~~ +{: .language-sh} ### Verschlüsselung? diff --git a/articles/2012-11-20_virtualisierung_mit_kvm_und_virtuelle_netzwerke_mit_vde.md b/articles/2012-11-20_virtualisierung_mit_kvm_und_virtuelle_netzwerke_mit_vde.md index 42cc891..ae7c783 100644 --- a/articles/2012-11-20_virtualisierung_mit_kvm_und_virtuelle_netzwerke_mit_vde.md +++ b/articles/2012-11-20_virtualisierung_mit_kvm_und_virtuelle_netzwerke_mit_vde.md @@ -25,8 +25,11 @@ des Gast-Systems werden auf die gleiche Weise gesetzt. Aus diesem Grund gehe ich Hier als Beispiel mein derzeitiger Standardaufruf von KVM, der für alle VMs gleich ist. Dynamisch ist allein das zu verwendende Speicher-Gerät - in meinem Fall verschiedene Image-Dateien. - #!/bin/sh - qemu-kvm -cpu host -hda $1 -m 1024 -daemonize -vnc none -usb -net nic -net vde +~~~ +#!/bin/sh +qemu-kvm -cpu host -hda $1 -m 1024 -daemonize -vnc none -usb -net nic -net vde +~~~ +{: .language-sh} Dieser verwendet das als erster Parameter übergebene Gerät als Festplatte und setzt neben den nötigen Netzwerk-Einstellungen die VM mittels `-daemonize` in den Hintergrund. Falls benötigt, kann als Wert des Parameters `-vnc` auch eine von einem Doppelpunkt angeführte Zahl übergeben werden um die Grafik-Ausgabe der VM an einen VNC-Server anzubinden (z.B. `-vnc :1` für `127.0.0.1:1`). Diese @@ -38,19 +41,22 @@ Funktion verwende ich nicht, da ich ausschließlich über das interne Netzwerk d Meine Konfiguration hält sich dabei im wesentlichen an den [Vorschlag](https://wiki.archlinux.org/index.php/QEMU#Networking_with_VDE2) im englischen Arch-Wiki, welchen ich in ein einfaches Script verpackt habe: - #!/bin/sh - case "$1" in - start) - tunctl -t tap0 - vde_switch -tap tap0 -daemon -pidfile .switch.pid -mod 660 -group kvm - ifconfig tap0 192.168.100.254 netmask 255.255.255.0 - ;; - stop) - kill -9 `cat .switch.pid` - rm .switch.pid - tunctl -d tap0 - ;; - esac +~~~ +#!/bin/sh +case "$1" in + start) + tunctl -t tap0 + vde_switch -tap tap0 -daemon -pidfile .switch.pid -mod 660 -group kvm + ifconfig tap0 192.168.100.254 netmask 255.255.255.0 + ;; + stop) + kill -9 `cat .switch.pid` + rm .switch.pid + tunctl -d tap0 + ;; +esac +~~~ +{: .language-sh} Dieses Script erzeugt ein virtuelles Interface `tap0` und verbindet ein neues, als Daemon im Hintergrund laufendes, virtuelles Switch mit diesem. Als nächstes wird dann noch eine statische IP Konfiguration für das virtuelle Interface definiert. Durch diese können alle mit `-net nic -net vde` Parametern gestarteten KVM Gäste über die IP `192.168.100.254` auf den Host zugreifen. @@ -65,17 +71,20 @@ In den Gast-Systemen selbst muss zusätzlich eine statische IP Konfiguration vor Um das interne virtuelle LAN bei Bedarf mit der Außenwelt zu verbinden gibt es ebenfalls ein kleines Script welches die nötige Route erzeugt bzw. löscht: - #!/bin/sh - case "$1" in - start) - echo "1" > /proc/sys/net/ipv4/ip_forward - iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -o $2 -j MASQUERADE - ;; - stop) - iptables -t nat -X - echo "0" > /proc/sys/net/ipv4/ip_forward - ;; - esac +~~~ +#!/bin/sh +case "$1" in + start) + echo "1" > /proc/sys/net/ipv4/ip_forward + iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -o $2 -j MASQUERADE + ;; + stop) + iptables -t nat -X + echo "0" > /proc/sys/net/ipv4/ip_forward + ;; +esac +~~~ +{: .language-sh} Anpassbar ist hierbei das Interface, um z.B. daheim `eth0` und unterwegs wahlweise `wlan0` oder `usb0` verwenden zu können. @@ -84,12 +93,12 @@ Anpassbar ist hierbei das Interface, um z.B. daheim `eth0` und unterwegs wahlwei Nach einer Kernel Aktualisierung im ArchLinux Gast-System, kam es beim Neustart plötzlich zu dieser etwas erschreckenden Meldung: KVM: entry failed, hardware error 0x80000021 - + If you're running a guest on an Intel machine without unrestricted mode support, the failure can be most likely due to the guest entering an invalid state for Intel VT. For example, the guest maybe running in big real mode which is not supported on less recent Intel processors. - + EAX=00000000 EBX=0020fa5c ECX=00000000 EDX=fffff000 ESI=f6d29014 EDI=00000001 EBP=f6461fa0 ESP=f6461f60 EIP=c0128443 EFL=00010246 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0 @@ -111,7 +120,10 @@ Nach einer Kernel Aktualisierung im ArchLinux Gast-System, kam es beim Neustart Dies sah für mich im ersten Moment wie ein klarer Hardware-Fehler aus, alle Informationen die sich über meine bevorzugte Suchmaschine finden ließen, waren jedoch für andere Situationen und ältere Kernel Versionen als `3.6.6-1`. Nach Tests mit verschiedenen Parametern für die zuständigen Kernel-Module, funktionierte es schließlich nach Laden des Moduls mit folgendem Befehl wieder einwandfrei: - modprobe kvm_intel emulate_invalid_guest_state=0 +~~~ +modprobe kvm_intel emulate_invalid_guest_state=0 +~~~ +{: .language-sh} Um den `emulate_invalid_guest_state` Parameter dauerhaft zu setzen reicht ein Eintrag in `/etc/modprobe.d`. diff --git a/articles/2013-04-27_declaring_functions_local_to_a_translation_unit_in_cpp.md b/articles/2013-04-27_declaring_functions_local_to_a_translation_unit_in_cpp.md index 1cb8480..ae04631 100644 --- a/articles/2013-04-27_declaring_functions_local_to_a_translation_unit_in_cpp.md +++ b/articles/2013-04-27_declaring_functions_local_to_a_translation_unit_in_cpp.md @@ -2,21 +2,27 @@ In a current project of mine I defined the following function marked with the inline hint without declaring it in a header file: - inline bool checkStorageSection(const void* keyBuffer) { - return (StorageSection::Edge == readNumber( - reinterpret_cast(keyBuffer)+0 - )); - } +~~~ +inline bool checkStorageSection(const void* keyBuffer) { + return (StorageSection::Edge == readNumber( + reinterpret_cast(keyBuffer)+0 + )); +} +~~~ +{: .language-cpp} This function was not defined in one but in multiple translation units - each instance with the same signature but a slightly different comparison contained in the function body. I expected these functions to be local to their respective translation unit and in the best case to be inlined into their calling member methods. While debugging another issue that seemed to be unrelated to these functions I noticed a strange behaviour: The calls in the member methods that should have linked to their respective local function definition all linked to the same definition in a different translation unit (the one displayed above). A quick check in GDB using the _x_-command to display the function addresses confirmed this suspicion: - // Function address in translation unit A - 0x804e718 : 0x83e58955 +~~~ +// Function address in translation unit A +0x804e718 : 0x83e58955 - // Function address in translation unit B - 0x804e718 : 0x83e58955 +// Function address in translation unit B +0x804e718 : 0x83e58955 +~~~ +{: .language-gdb} The address _0x804e718_ was the address of the function definition in translation unit "A" in both cases. At first I suspected that the cause was probably that both definitions were located in the same namespace, but excluding them from the enclosing namespace declaration did not fix the problem. diff --git a/articles/2013-10-06_notizen_zu_cpp_und_unicode.md b/articles/2013-10-06_notizen_zu_cpp_und_unicode.md index 6685687..fbc6d63 100644 --- a/articles/2013-10-06_notizen_zu_cpp_und_unicode.md +++ b/articles/2013-10-06_notizen_zu_cpp_und_unicode.md @@ -11,7 +11,10 @@ Getreu der auf [UTF-8 Everywhere](http://www.utf8everywhere.org/) hervorgebracht Grundsätzlich stellt es auf der Plattform meiner Wahl - Linux mit Lokalen auf _en\_US.UTF-8_ - kein Problem dar, UTF-8 enkodierte Strings in C++ Programmen zu verarbeiten. Den Klassen der C++ Standard Library ist es, solange wir nur über das reine Speichern und Bewegen von Strings sprechen, egal ob dieser in UTF-8, ASCII oder einem ganz anderen Zeichensatz kodiert ist. Möchten wir sicher gehen, dass ein in einer Instanz von _std::string_ enthaltener Text tatsächlich in UTF-8 enkodiert wird und dies nicht vom Zeichensatz der Quelldatei abhängig ist, reicht es dies durch voranstellen von _u8_ zu definieren: - std::string test(u8"Hellø Uni¢ød€!"); +~~~ +std::string test(u8"Hellø Uni¢ød€!"); +~~~ +{: .language-cpp} Der C++ Standard garantiert uns, dass ein solcher String in UTF-8 enkodiert wird. Auch die Ausgabe von in dieser Form enkodierten Strings funktioniert nach meiner Erfahrung - z.T. erst nach setzen der Lokale mittels _std::setlocale_ - einwandfrei. Probleme gibt es dann, wenn wir den Text als solchen näher untersuchen oder sogar verändern wollen bzw. die Ein- und Ausgabe des Textes in anderen Formaten erfolgen soll. Für letzteres gibt es eigentlich die _std::codecvt_ Facetten, welche aber in der aktuellen Version der GNU libstdc++ noch [nicht implementiert](http://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.2011) sind. Wir müssen in diesem Fall also auf externe Bibliotheken wie beispielweise [iconv](https://www.gnu.org/software/libiconv/) oder [ICU](http://site.icu-project.org/) zurückgreifen. Auch die in der C++ Standard Library enthaltenen Templates zur String-Verarbeitung helfen uns bei Multibyte-Enkodierungen, zu denen auch UTF-8 zählt, nicht viel, da diese mit dem _char_ Datentyp und nicht mit Code-Points arbeiten. So liefert _std::string_ beispielsweise für einen UTF-8 enkodierten String, welcher nicht nur von dem in einer Code-Unit abbildbaren ASCII-Subset Gebrauch macht, nicht die korrekte Zeichenanzahl. Auch eine String-Iteration ist mit den Standard-Klassen nur Byte- und nicht Code-Point-Weise umsetzbar. Wir stehen also vor der Entscheidung eine weitere externe Bibliothek zu verwenden oder Programm-Intern vollständig auf UTF-32 zu setzen. @@ -23,34 +26,11 @@ Um zumindest für rein lesende Zugriffe auf UTF-8 Strings nicht gleich eine Bibl UTF-8 enkodiert die aktuell maximal 21 Bit eines Unicode Code-Points in bis zu vier Code-Units mit einer Länge von je einem Byte. Die verbleibenden maximal 11 Bit werden dazu verwendet, Anfangs- und Fortsetzungs-Bytes eines Code-Points zu kennzeichnen und schon in der ersten Code-Unit zu definieren, in wie vielen Code-Units das aktuelle Symbol enkodiert ist. -

- - - - - - - - - - - - - - - - - - - - - - - - - -
PayloadStruktur
70xxxxxxx
11110xxxxx 10xxxxxx
171110xxxx 10xxxxxx 10xxxxxx
2111110xxx 10xxxxxx 10xxxxxx 10xxxxxx
-

+| Payload | Struktur | +| 7 | `0xxxxxxx` | +| 11 | `110xxxxx 10xxxxxx` | +| 17 | `1110xxxx 10xxxxxx 10xxxxxx` | +| 21 | `11110xxx 10xxxxxx 10xxxxxx 10xxxxxx` | In obenstehender Tabelle ist die in [RFC3629](http://tools.ietf.org/html/rfc3629) definierte Struktur der einzelnen Code-Units zusammen mit der jeweiligen Anzahl der Payload-Bits dargestellt. Anhand der Tabelle können wir erkennen, dass die Rückwärtskompatibilität zu ASCII dadurch gewährleistet wird, dass alle Code-Points bis @@ -58,30 +38,36 @@ einschließlich 127 im ersten Byte dargestellt werden können. Sobald in der ers Zur Erkennung und Umformung der UTF-8 Code-Units verwenden wir in der _UTF8::CodepointIterator_-Klasse die folgenden, in stark typisierten Enums definierten, Bitmasken: - enum class CodeUnitType : uint8_t { - CONTINUATION = 128, // 10000000 - LEADING = 64, // 01000000 - THREE = 32, // 00100000 - FOUR = 16, // 00010000 - }; - - enum class CodePoint : uint8_t { - CONTINUATION = 63, // 00111111 - TWO = 31, // 00011111 - THREE = 15, // 00001111 - FOUR = 7, // 00000111 - }; +~~~ +enum class CodeUnitType : uint8_t { + CONTINUATION = 128, // 10000000 + LEADING = 64, // 01000000 + THREE = 32, // 00100000 + FOUR = 16, // 00010000 +}; + +enum class CodePoint : uint8_t { + CONTINUATION = 63, // 00111111 + TWO = 31, // 00011111 + THREE = 15, // 00001111 + FOUR = 7, // 00000111 +}; +~~~ +{: .language-cpp} Bei tieferem Interesse lässt sich die Implementierung der UTF-8 Logik in der Quelldatei [codepoint_iterator.cc](https://github.com/KnairdA/CodepointIterator/blob/master/src/codepoint_iterator.cc) nachlesen. Zusätzlich zu den in GoogleTest geschriebenen [Unit-Tests](https://github.com/KnairdA/CodepointIterator/blob/master/test.cc) sehen wir im folgenden noch ein einfaches Beispiel zur Verwendung des `UTF8::CodepointIterator` mit einem [Beispieltext](http://www.columbia.edu/~fdc/utf8/) in altnordischen Runen: - std::string test(u8"ᛖᚴ ᚷᛖᛏ ᛖᛏᛁ ᚧ ᚷᛚᛖᚱ ᛘᚾ ᚦᛖᛋᛋ ᚨᚧ ᚡᛖ ᚱᚧᚨ ᛋᚨᚱ"); +~~~ +std::string test(u8"ᛖᚴ ᚷᛖᛏ ᛖᛏᛁ ᚧ ᚷᛚᛖᚱ ᛘᚾ ᚦᛖᛋᛋ ᚨᚧ ᚡᛖ ᚱᚧᚨ ᛋᚨᚱ"); - for ( UTF8::CodepointIterator iter(test.cbegin()); - iter != test.cend(); - ++iter ) { - std::wcout << static_cast(*iter); - } +for ( UTF8::CodepointIterator iter(test.cbegin()); + iter != test.cend(); + ++iter ) { + std::wcout << static_cast(*iter); +} +~~~ +{: .language-cpp} Die Dereferenzierung einer Instanz des Iterators produziert den aktuellen Code-Point als _char32\_t_, da dieser Datentyp garantiert vier Byte lang ist. Die Ausgabe eines solchen UTF-32 enkodierten Code-Points ist mir allerdings leider nur nach dem Cast in _wchar\_t_ gelungen. Dieser wird trotzdem nicht als Dereferenzierungs-Typ verwendet, da die Länge nicht fest definiert ist, sondern abhängig von der jeweiligen C++ Implementierung unterschiedlich sein kann. Dies stellt jedoch kein größeres Problem dar, da der Iterator für die interne Betrachtung von Strings und nicht zur Konvertierung für die Ausgabe gedacht ist. diff --git a/articles/2013-11-03_mapping_binary_structures_as_tuples_using_template_metaprogramming.md b/articles/2013-11-03_mapping_binary_structures_as_tuples_using_template_metaprogramming.md index 24c000e..a3af421 100644 --- a/articles/2013-11-03_mapping_binary_structures_as_tuples_using_template_metaprogramming.md +++ b/articles/2013-11-03_mapping_binary_structures_as_tuples_using_template_metaprogramming.md @@ -17,30 +17,33 @@ differences in endianness and In-place modification of the structure fields. To be able to easily work with structure definitions using template metaprogramming I am relying on the standard libraries [_std::tuple_](http://en.cppreference.com/w/cpp/utility/tuple) template. - template - class BinaryMapping { - public: - BinaryMapping(uint8_t* const buffer): - buffer_(buffer) { - TupleReader::read(this->tuple_, buffer); - } - - template - decltype(*std::get(Tuple{})) get() { - return *std::get(this->tuple_); - } - - template (Tuple{}))> - void set(const Value value) { - *std::get(this->tuple_) = value; - } - - private: - uint8_t* const buffer_; - Tuple tuple_; - - }; +~~~ +template +class BinaryMapping { + public: + BinaryMapping(uint8_t* const buffer): + buffer_(buffer) { + TupleReader::read(this->tuple_, buffer); + } + + template + decltype(*std::get(Tuple{})) get() { + return *std::get(this->tuple_); + } + + template (Tuple{}))> + void set(const Value value) { + *std::get(this->tuple_) = value; + } + + private: + uint8_t* const buffer_; + Tuple tuple_; + +}; +~~~ +{: .language-cpp} This implementation of a template class _BinaryMapping_ provides _get_ and _set_ template methods for accessing values in a given binary buffer using the mapping provided by a given Tuple template argument. The most notable element of this class is the usage of the _decltype_ keyword which was introduced in C++11. This keyword makes it easier to declare types @@ -52,27 +55,30 @@ as its return type is also dependent on the template arguments. As you may have noticed the above template class is not complete as I have not included a implementation of the _TupleReader::read_ method which does the actual work of mapping the binary buffer as a tuple. This mapping is achieved by the following recursive template methods: - struct TupleReader { - template - static inline typename std::enable_if< - Index == std::tuple_size::value, void - >::type read(Tuple&, uint8_t*) { } - - template - static inline typename std::enable_if< - Index < std::tuple_size::value, void - >::type read(Tuple& tuple, uint8_t* buffer) { - std::get(tuple) = reinterpret_cast< - typename std::tuple_element::type - >(buffer+Offset); - - read(Tuple{})))>( - tuple, buffer - ); - } - }; +~~~ +struct TupleReader { + template + static inline typename std::enable_if< + Index == std::tuple_size::value, void + >::type read(Tuple&, uint8_t*) { } + + template + static inline typename std::enable_if< + Index < std::tuple_size::value, void + >::type read(Tuple& tuple, uint8_t* buffer) { + std::get(tuple) = reinterpret_cast< + typename std::tuple_element::type + >(buffer+Offset); + + read(Tuple{})))>( + tuple, buffer + ); + } +}; +~~~ +{: .language-cpp} Template metaprogramming in C++ offers a Turing-complete language which is fully executed during compilation. This means that any problem we may solve during the runtime of a _normal_ program may also be solved during compilation using template metaprogramming techniques. This kind of programming is comparable to functional programming as we have to rely on recursion and pattern @@ -100,19 +106,22 @@ is always available as a template argument. The classes _TupleReader_ and _BinaryMapping_ are enough to map a binary structure as a _std::tuple_ instantiation like in the following example where I define a _TestRecord_ tuple containing a pointer to _uint64\_t_ and _uint16\_t_ integers: - typedef std::tuple TestRecord; +~~~ +typedef std::tuple TestRecord; - uint8_t* buffer = reinterpret_cast( - std::calloc(10, sizeof(uint8_t)) - ); +uint8_t* buffer = reinterpret_cast( + std::calloc(10, sizeof(uint8_t)) +); - BinaryMapping mapping(buffer); +BinaryMapping mapping(buffer); - mapping.set<0>(42); - mapping.set<1>(1337); +mapping.set<0>(42); +mapping.set<1>(1337); - std::cout << mapping.get<0>() << std::endl; - std::cout << mapping.get<1>() << std::endl; +std::cout << mapping.get<0>() << std::endl; +std::cout << mapping.get<1>() << std::endl; +~~~ +{: .language-cpp} ### Endianness @@ -120,75 +129,84 @@ As you may remember this does not take endianness into account as I defined as a _BinaryMapping_ template class which worked, but led to problems as soon as I mixed calls to _get_ and _set_. The resulting problems could of course have been fixed but this would probably have conflicted with In-place modifications of the buffer. Because of that I chose to implement endianness support in a separate set of templates. - struct BigEndianSorter { - template - static void write(uint8_t*, const Key&); +~~~ +struct BigEndianSorter { + template + static void write(uint8_t*, const Key&); - template - static Key read(uint8_t* buffer); - }; + template + static Key read(uint8_t* buffer); +}; +~~~ +{: .language-cpp} To be able to work with different byte orderings I abstracted the basic operations down to _read_ and _write_ template methods contained in a _struct_ so I would be able to provide separate implementations of these methods for each type of endianness. The following template specialization of the _write_ method which does an In-place reordering of a _uint64\_t_ should be enough to understand the basic principle: - template <> - void BigEndianSorter::write(uint8_t* buffer, const uint64_t& number) { - *reinterpret_cast(buffer) = htobe64(number); - } +~~~ +template <> +void BigEndianSorter::write(uint8_t* buffer, const uint64_t& number) { + *reinterpret_cast(buffer) = htobe64(number); +} +~~~ +{: .language-cpp} As soon as I had the basic endianness conversion methods implemented in a manner which could be used to specialize other template classes I was able to build a generic implementation of a serializer which respects the structure defined by a given _std::tuple_ instantiation: - template - struct Serializer { - template - static inline typename std::enable_if< - Index == std::tuple_size::value, void - >::type serialize(uint8_t*) { } - - template - static inline typename std::enable_if< - Index < std::tuple_size::value, void - >::type serialize(uint8_t* buffer) { - ByteSorter::template write(Tuple{})) - >::type>( - buffer+Offset, - *reinterpret_cast::type>( - buffer+Offset - ) - ); - - serialize(Tuple{})))>( - buffer - ); - } - - template - static inline typename std::enable_if< - Index == std::tuple_size::value, void - >::type deserialize(uint8_t*) { } - - template - static inline typename std::enable_if< - Index < std::tuple_size::value, void - >::type deserialize(uint8_t* buffer) { +~~~ +template +struct Serializer { + template + static inline typename std::enable_if< + Index == std::tuple_size::value, void + >::type serialize(uint8_t*) { } + + template + static inline typename std::enable_if< + Index < std::tuple_size::value, void + >::type serialize(uint8_t* buffer) { + ByteSorter::template write(Tuple{})) + >::type>( + buffer+Offset, *reinterpret_cast::type>( buffer+Offset - ) = *ByteSorter::template read< - typename std::tuple_element::type - >(buffer+Offset); - - deserialize(Tuple{})))>( - buffer - ); - } - }; + ) + ); + + serialize(Tuple{})))>( + buffer + ); + } + + template + static inline typename std::enable_if< + Index == std::tuple_size::value, void + >::type deserialize(uint8_t*) { } + + template + static inline typename std::enable_if< + Index < std::tuple_size::value, void + >::type deserialize(uint8_t* buffer) { + *reinterpret_cast::type>( + buffer+Offset + ) = *ByteSorter::template read< + typename std::tuple_element::type + >(buffer+Offset); + + deserialize(Tuple{})))>( + buffer + ); + } +}; +~~~ +{: .language-cpp} It should be evident that the way both the _serialize_ and _deserialize_ template methods are implemented is very similar to the _TupleReader_ implementation. In fact the only difference is that no actual _std::tuple_ instantiation instance is touched and instead of setting pointers to the buffer we are only reordering the bytes of each section of the buffer corresponding to @@ -198,34 +216,37 @@ a tuple element. This results in a complete In-place conversion between differen At last I am now able to do everything I planned in the beginning in a very compact way using the _Serializer_, _TupleReader_ and _BinaryMapping_ templates. In practice this now looks like this: - typedef std::tuple TestRecord; +~~~ +typedef std::tuple TestRecord; - uint8_t* buffer = reinterpret_cast( - std::calloc(10, sizeof(uint8_t)) - ); +uint8_t* buffer = reinterpret_cast( + std::calloc(10, sizeof(uint8_t)) +); - BinaryMapping mapping(buffer); +BinaryMapping mapping(buffer); - mapping.set<0>(42); - mapping.set<1>(1001); +mapping.set<0>(42); +mapping.set<1>(1001); - Serializer::serialize(buffer); +Serializer::serialize(buffer); - uint8_t* testBuffer = reinterpret_cast( - std::calloc(10, sizeof(uint8_t)) - ); +uint8_t* testBuffer = reinterpret_cast( + std::calloc(10, sizeof(uint8_t)) +); - std::memcpy(testBuffer, buffer, 10); +std::memcpy(testBuffer, buffer, 10); - Serializer::deserialize(testBuffer); +Serializer::deserialize(testBuffer); - BinaryMapping testMapping(testBuffer); +BinaryMapping testMapping(testBuffer); - std::cout << testMapping.get<0>() << std::endl; - std::cout << testMapping.get<1>() << std::endl; +std::cout << testMapping.get<0>() << std::endl; +std::cout << testMapping.get<1>() << std::endl; - std::free(buffer); - std::free(testBuffer); +std::free(buffer); +std::free(testBuffer); +~~~ +{: .language-cpp} The above coding makes use of all features provided by the described templates by first setting two values using _BinaryMapping_ specialized on the _TestRecord_ tuple, serializing them using _Serializer_ specialized on the _BigEndianSorter_, deserializing the buffer back to the host byte ordering and reading the values using another _BinaryMapping_. diff --git a/articles/2014-01-05_disabling_methods_in_implicitly_instantiated_class_template_specializations_in_cpp.md b/articles/2014-01-05_disabling_methods_in_implicitly_instantiated_class_template_specializations_in_cpp.md index 55da8fd..610254e 100644 --- a/articles/2014-01-05_disabling_methods_in_implicitly_instantiated_class_template_specializations_in_cpp.md +++ b/articles/2014-01-05_disabling_methods_in_implicitly_instantiated_class_template_specializations_in_cpp.md @@ -5,16 +5,19 @@ During my current undertaking of developing a [template based library](https://g The specific scenario I am talking about is disabling the _serialize_ and _deserialize_ template methods in all specializations of the _[Tuple](https://github.com/KnairdA/BinaryMapping/blob/master/src/tuple/tuple.h)_ class template whose _Endianess_ argument is not of the type _UndefinedEndian_. My first working implementation of this requirement looks as follows: - template < - typename CustomOrder, - typename Helper = Endianess - > - inline typename std::enable_if< - std::is_same::value, - void - >::type serialize() { - Serializer>::serialize(this->tuple_); - } +~~~ +template < + typename CustomOrder, + typename Helper = Endianess +> +inline typename std::enable_if< + std::is_same::value, + void +>::type serialize() { + Serializer>::serialize(this->tuple_); +} +~~~ +{: .language-cpp} As we can see I am relying on the standard libraries `std::enable_if` template to enable the method only if the _Helper_ template argument equals the type _UndefinedEndian_. One may wonder why I am supplying the `std::enable_if` template with the argument _Helper_ instead of directly specifying the class template argument _Endianess_. This was needed because of the following paragraph of the ISO C++ standard: @@ -27,16 +30,19 @@ I defined a additional _Helper_ argument which defaults to the class template's The code supplied above works as expected but has at least two flaws: There is an additonal template argument whose sole purpose is to work around the C++ standard to make _[SFINAE](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error)_ possible and the return type of the method is obfuscated by the use of the `std::enable_if` template. Luckily it is not the only way of achieving our goal: - template < - typename CustomOrder, - typename = typename std::enable_if< - std::is_same::value, - void - >::type - > - inline void serialize() { - Serializer>::serialize(this->tuple_); - } +~~~ +template < + typename CustomOrder, + typename = typename std::enable_if< + std::is_same::value, + void + >::type +> +inline void serialize() { + Serializer>::serialize(this->tuple_); +} +~~~ +{: .language-cpp} In this second example implementation we are moving the `std::enable_if` template from the return type into a unnamed default template argument of the method. This unnamed default template argument which was not possible prior to the C++11 standard reduces the purpose of the `std::enable_if` template to selectively disabling method specializations. Additionally @@ -44,15 +50,18 @@ the return type can be straight forwardly declared and the _Helper_ template arg But during my research concerning this problem I came up with one additional way of achieving our goal which one could argue is even better than the second example: - template - inline void serialize() { - static_assert( - std::is_same::value, - "Endianess must be UndefinedEndian to use serialize<*>()" - ); - - Serializer>::serialize(this->tuple_); - } +~~~ +template +inline void serialize() { + static_assert( + std::is_same::value, + "Endianess must be UndefinedEndian to use serialize<*>()" + ); + + Serializer>::serialize(this->tuple_); +} +~~~ +{: .language-cpp} This implementation of the _serialize_ method renounces any additonal template arguments and instead uses a declaration called `static_assert` which makes any specializations where the statement `std::is_same::value` resolves to false ill-formed. Additionally it also returns a helpful message to the compiler log during such a situation. diff --git a/articles/2014-07-11_mapping_arrays_using_tuples_in_cpp11.md b/articles/2014-07-11_mapping_arrays_using_tuples_in_cpp11.md index ae46d92..db55e00 100644 --- a/articles/2014-07-11_mapping_arrays_using_tuples_in_cpp11.md +++ b/articles/2014-07-11_mapping_arrays_using_tuples_in_cpp11.md @@ -6,51 +6,57 @@ While it is possible to unpack a `std::tuple` instance into individual predefine Extracting array elements obviously requires some way of defining the appropriate indexes and mapping the elements using a tuple blueprint additionally requires this way to be statically resolvable as one can not pass a dynamic index value to `std::tuple_element`. So the first step to fullfilling the defined requirements involved the implementation of a template based index or sequence type. - template - struct Sequence { - typedef Sequence type; - }; - - template < - std::size_t Size, - std::size_t Index = 0, - std::size_t... Current - > - struct IndexSequence { - typedef typename std::conditional< - Index < Size, - IndexSequence, - Sequence - >::type::type type; - }; +~~~ +template +struct Sequence { + typedef Sequence type; +}; + +template < + std::size_t Size, + std::size_t Index = 0, + std::size_t... Current +> +struct IndexSequence { + typedef typename std::conditional< + Index < Size, + IndexSequence, + Sequence + >::type::type type; +}; +~~~ +{: .language-cpp} This is achieved by the [`IndexSequence` template](https://github.com/KnairdA/InputXSLT/blob/master/src/support/type/sequence.h) above by recursively specializing the `Sequence` template using static recursion controlled by the standard libraries template metaprogramming utility template `std::conditional`. This means that e.g. the type `Sequence<0, 1, 2, 3>` can also be defined as `IndexSequence<4>::type`. Now all that is required to accomplish the goal is instantiating the sequence type and passing it to a variadic member template as [follows](https://github.com/KnairdA/InputXSLT/blob/master/src/function/base.h): +~~~ +[...] +this->callConstructDocument( + parameters, + locator, + typename IndexSequence::type() +) +[...] +template +inline xalan::XalanDocument* callConstructDocument( + const XObjectArgVectorType& parameters, + const xalan::Locator* locator, + Sequence +) const { [...] - this->callConstructDocument( - parameters, - locator, - typename IndexSequence::type() - ) - [...] - template - inline xalan::XalanDocument* callConstructDocument( - const XObjectArgVectorType& parameters, - const xalan::Locator* locator, - Sequence - ) const { - [...] - return this->document_cache_->create( - static_cast(this)->constructDocument( - valueGetter.get - >::type>(parameters[Index])... - ) - ); - } + return this->document_cache_->create( + static_cast(this)->constructDocument( + valueGetter.get + >::type>(parameters[Index])... + ) + ); +} +~~~ +{: .language-cpp} As we can see a `IndexSequence` template specialization instance is passed to the variadic `callConstructDocument` method to expose the actual sequence values as `Index`. This method then resolves the `Index` parameter pack as both the array and `std::tuple` index inside the calls to the `valueGetter.get` template method which is called for every sequence element because of this. What this means is that we are now able to implement non-template `constructDocument` methods inside XSLT external function implementations such as [FunctionTransform](https://github.com/KnairdA/InputXSLT/blob/master/src/function/transform.h). The values passed to these methods are automatically extracted from the argument array and converted into their respective types as required. -- cgit v1.2.3