SucheBlog abonnierenVerwaltung des BlogsKategorienKontaktMarkus Brückner |
Tuesday, 18. November 2008Pylons und Elixir + Session-HandlingWer mal entspannt Webapplikationen entwickeln will/muss, dem sei Pylons ans Herz gelegt. Nettes Framework, noch einige kleinere Ecken, aber schonmal sehr angenehm zum Entwickeln geeignet. Meist liegt hinter einer Webapplikation ja ein entsprechendes Datenmodell in Form einer Datenbank. Um die bequem ansteuern zu können (SQL von Hand zu coden ist mittlerweile definitiv nicht mehr schön) bringt Pylons eine Integration für SQLAlchemy mit. Nun ist SQLAlchemy flexibel und mächtig, dafür aber manchmal etwas komplexer als nötig. Für viele Anwendungsfälle reichen die Möglichkeiten, die Elixir mitbringt (Elixir ist eine auf SQLAlchemy aufsetzende Bibliothek, die einen deklarativen Stil zur Erstellung des Datenmodells ermöglicht). Mit diesem Trio kann man schonmal recht viel Interessantes tun. Blöd nur, wenn man ein wenig faul bei der Einbindung der verschiedenen Module ist. Dann kann es nämlich dazu kommen, dass plötzlich folgende Fehlermeldung auftritt, wenn man versucht was in der Nutzersession im Pylons zu speichern: TypeError: 'ScopedSession' object is unsubscriptable Der Datentyp deutet es an: das session-Objekt von Pylons ist unter diesem Namen nicht verfügbar. Der Fehler ist simpel: im Datenmodell der Applikation stand mal from elixir import * (um Schreibarbeit bei den Elixir-Datentypen zu sparen). Im Controller wiederum wurde das Datenmodell mit from app.model import * importiert. Tja... Elixir definiert dummerweise ein eigenes session-Objekt, welches durch diese Import-Kette in den Namensraum der Applikation gerät und dort dann das eigentliche Pylons-Objekt überschreibt. Als Lösung bleiben drei Möglichkeiten: man kann den Import im Datenmodell in import elixir ändern und allen Datentypen ein elixir. voransetzen, die benötigten Elixirtypen mittels from elixir import Field, .... im Datenmodell importieren (und dabei keinesfalls session mit reinnehmen) oder in der Applikation das Datenmodell mittels import app.model as m und dann jedem Zugriff auf Elemente des Modells ein m. voranstellen. Danach hat man wieder wie gewünscht Zugriff auf seine Session und kann Daten darin speichern und daraus lesen. Monday, 20. October 2008QWidget auf minimal notwendige Größe bringenManchmal kann man sich auch einen Wolf suchen. Ziel war: ein Widget in dem sich mehrere Children befinden (QLineEdit, Checkboxen, QTableView) immer auf die minimal notwendige Größe zu bringen. Das gilt speziell dann, wenn der QTableView unsichtbar wird. Mit etwas um die Ecke denken geht's: widget->setMaximumHeight(0). (Im dem Fall ging es nur um die Höhe). Muss man erstmal drauf kommen... Update: Unsinn gelöscht. Man schaue sich mal QWidget::adjustSize() an... Thursday, 28. August 2008PyQt4 und QAbstractItemViewNach zweitägiger Suche: Python und QT können manchmal sehr hässlich zusammenstoßen, wenn es um die Lebensdauer von Objekten geht. Folgender Code:
class MainWindow(QMainWindow):
[...]
def initTreeView(self, top_level_cats):
model = CategoryModel(top_level_cats)
self.treeview.setModel(model)
[...]
Sieht an sich erstmal unverdächtig aus: ein Model wird erzeugt (wie auch immer geartet) und dem Ein winzig kleine Änderung des Codes bringt die Lösung. Hier fast die gleiche Funktion:
class MainWindow(QMainWindow):
[...]
def initTreeView(self, top_level_cats):
self.model = CategoryModel(top_level_cats)
self.treeview.setModel(self.model)
[...]
Der einzige Unterschied: statt einer lokalen Variable ist Eigentlich logisch. Wenn man's einmal weiß... Sunday, 24. August 2008Eclipse Ganymede und SubversionDer Titel sagt es schon: es geht um Eclipse Ganymede und Subversion. Beides meiner Meinung fantastische Tools zur Softwareentwicklung, in Kombination allerdings gerade etwas zickig. Das Problem: bei Eclipse Europa (3.3) wurde meist für die Subversion-Integration das Plugin Subclipse verwendet. Für Ganymede (3.4) gibt's das allerdings noch nicht. Dafür gibt es ein neues Plugin direkt im Ganymede-Repository: Subversive. Dieses Plugin soll wohl bald standardmäßig in Eclipse integriert werden. Es ist quasi deckungsgleich mit dem schon bestehenden CVS-Plugin und integriert sich sehr gut in die Oberfläche. So schick das Plugin ist: mir scheint, die Eclipse-Jungs und -Mädels haben bei der Zusammenstellung von Ganymede gepennt. Dummerweise ist nämlich der eigentliche Team-Provider integriert, die Library zum Zugriff auf die SVN-Repositories allerdings nicht. Die darf man sich zu Fuß nachinstallieren. Dabei darf man sich dann auch noch entscheiden, welche man nimmt. Ich hab mich letztlich für SVNKit entschieden, weil mir die Idee gefällt, allen Kram gleich in Java abzuwickeln, statt externe Libs einbinden zu müssen. Dummerweise begrüßt mich mein Eclipse bei der Installation von SVNKit mit folgenden Zeilen: Cannot find a solution where both Match[requiredCapability: org.eclipse.equinox.p2.iu/org.eclipse.team.svn.feature.group/[0.7.1.I20080612-1500,0.7.1.I20080612-1500]] and Match[requiredCapability: org.eclipse.equinox.p2.iu/org.eclipse.team.svn.feature.group/[0.7.2.I20080801-1500,1.0.0)] can be satisfied. Unsatisfied dependency: [org.polarion.eclipse.team.svn.connector.svnkit.feature.group 2.0.2.I20080801-1500] requiredCapability: org.eclipse.equinox.p2.iu/org.eclipse.team.svn.feature.group/[0.7.2.I20080801-1500,1.0.0) Unsatisfied dependency: [org.polarion.eclipse.team.svn.connector.svnkit.feature.group 2.0.2.I20080801-1500] requiredCapability: org.eclipse.equinox.p2.iu/org.polarion.eclipse.team.svn.connector.feature.group/0.0.0 Unsatisfied dependency: [org.polarion.eclipse.team.svn.connector.svnkit15.feature.group 2.0.2.I20080801-1500] requiredCapability: org.eclipse.equinox.p2.iu/org.eclipse.team.svn.feature.group/[0.7.2.I20080801-1500,1.0.0) Unsatisfied dependency: [org.polarion.eclipse.team.svn.connector.svnkit15.feature.group 2.0.2.I20080801-1500] requiredCapability: org.eclipse.equinox.p2.iu/org.polarion.eclipse.team.svn.connector.feature.group/0.0.0 Unsatisfied dependency: [org.polarion.eclipse.team.svn.connector.feature.group 2.0.2.I20080801-1500] requiredCapability: org.eclipse.equinox.p2.iu/org.eclipse.team.svn.feature.group/[0.7.2.I20080801-1500,1.0.0) Ja, diese Fehlermeldung ist auch im Original so unlesbar. Nach längerem Suchen bin ich in einem Blog auf die Lösung gestoßen: Seit 3. August möchte SVNKit eine neuere Version von Subversive haben (oder andersrum, so 100% schlau bin ich noch nicht draus geworden). Die ist dummerweise nicht in den Ganymede-Repositories, sondern muss unter http://download.eclipse.org/technology/subversive/0.7/update-site/ extra bezogen werden. Diese URL packt man in die Update-Site, muss sie dann noch einmalig via "Manage Sites..." aktivieren und schon kann man sich ein neueres Subversive installieren, was dann auch mit SVNKit spielen mag. Schade, dass die Standardintegration in Ganymede so kaputt ist. Bis zum Nachfolger dauert es ja doch ein wenig und bis dahin sind wahrscheinlich schon einige an Subversion und Eclipse verzweifelt und benutzen glatt wieder CVS *brrrr*... Update: Wie Deka in den Kommentaren angemerkt hat scheint es mit Subclipse 1.4 wohl doch einen Release für Ganymede zu geben. Sunday, 4. March 2007Installation von Cheetah auf UbuntuInstalliert man Pylons nach Anleitung mittels easy_install auf einem Ubuntu Feisty Fawn, dann bricht der Compilerlauf von Cheetah (welches als Abhängigkeit mit installiert wird) unter Umständen irgendwann mit folgender Meldung ab: src/_namemapper.c:15:58: error: Python.h: No such file or directory Das liegt daran, daß die passenden Headerfiles nicht in /usr/include/ liegen, sondern in /usr/include/python2.5 (bspw.), wo sie natürlich vom Compiler erstmal nicht gefunden werden. Ein CFLAGS='-I/usr/include/python2.5' easy_install Pylons behebt das Problem. Friday, 16. February 2007Geek-WitzeGrad von Achim geschickt bekommen: Random Number. Wer's nicht versteht: macht nix. Ich find's lustig. :)
Geschrieben von namenlos
in Computer, Fundstücke, Programmieren
um
14:21
| Kommentare (0)
| Trackbacks (0)
Saturday, 10. February 2007Recursive-descent Parser in PythonIch hatte mal wieder etwas Langeweile und wollte ein wenig mehr Python lernen. Also hab ich mich mal damit beschäftigt, wie das mit Parsing in Python so aussieht. Ich bin ja in C++ von boost::Spirit recht angetan und hab sowas ähnliches für Python gesucht. Das, was dem noch am nächsten kommt, ist pyparsing. Leider hat dieses einen gravierenden (aus meiner Sicht) Nachteil: es scheint nicht möglich zu sein, den Parser dazu zu bringen, Whitespaces NICHT zu ignorieren. Dummerweise brauchte ich das aber für die selbstgestellte Aufgabe an einigen Stellen... Wie der geneigte Informatiker dann so ist, schreibt er sich den Kram halt selbst und lernt noch was dabei. Rausgekommen ist yeanpypa (bevor einer fragt: YEt ANother PYthon PArser framework. Hat da wer gelacht?!), ein kleines Framework, mit dem sich eine EBNF-Grammatik fast 1:1 in Python-Code übertragen läßt. Die Schnittstelle ist zum Teil bei pyparsing und zum Teil bei boost::Spirit abgeschaut. Das ganze dürfte noch einige Fehler haben, aber erscheint mir bisher schon ganz brauchbar. Der Code steht wieder mal unter einer public domain Lizenz, kann also vollkommen frei verwendet werden. Natürlich freue ich mich über eine kurze Nachricht, wenn jemand damit irgendwas tut. So, zuguterletzt noch die passenden Links: Tuesday, 28. November 2006Secure NetworkingNachdem ich heute seit langer Zeit mal wieder im Blog von Andreas Bogk gestöbert hab, bin ich über ein sehr interessantes Paper von ihm und Hannes Mehnert zum Thema "Secure Networking" gestolpert. Grob gesagt isses zwar (natürlich) wieder eins der Dylan-Propagandapaper (er mag die Sprache halt wirklich), aber dieses ist echt sehr nett. Ich hoffe doch, daß das Framework, was sie da vorstellen, sich weiterentwickelt. Tuesday, 18. July 2006In den Fuß schießen mit C++Die letzten drei Tage hab ich einen Fehler aus der Kategorie "In den Fuß schießen mit C++" gesucht. Da gibt man sich nun schon alle Mühe, verwendet die gut getesteten Datenstrukturen der STL und trotzdem grinst einen ein ums andere Mal ein Segmentation Fault an. *gnaa* Folgender Code (vereinfacht, das Original ist in eine Klasse eingebettet):
#include <iostream>
#include <list>
std::list<int> bla;
std::list<int> const getBla() {
return bla;
}
int main() {
bla.push_back(10);
for (std::list<int>::const_iterator i=getBla().begin(); i!=getBla().end(); ++i) {
std::cerr << *i << std::endl;
}
}
getBla() ist im Original ein Getter für ein Klassenmember. Eigentlich ein gängiges Konstrukt. Der Crash passiert in der Schleife bei der Dereferenzierung des Iterators i. (Im Original war das etwas komplizierter, aber das ist hier nebensächlich.). Nach einigem Rumprobieren stelle ich fest, daß die Schleife zweimal durchlaufen wird, obwohl bla ja eigentlich nur ein Element enthält. Die Dereferenzierung des Iterators knallt im zweiten Durchlauf der Schleife (logisch, der zeigt dann ja irgendwo ins Nirvana). Die Ursache des Fehlers liegt in einem fehlenden Zeichen in der Deklaration von getBla(). Eigentlich müßte das wie folgt aussehen (der Unterschied ist der Einfachheit halber rot markiert):
[...]
std::list<int> const &getBla() {
[...]
Standardmäßig übergibt C++ Parameter und Rückgabewerte by value, d.h. es wird eine Kopie des Wertes auf dem Stack gemacht. Das ganze ist von C geerbt. Mit der Einführung von abstrakten Datentypen kann so eine Kopie natürlich richtig teuer werden, weswegen zusätzlich die Möglichkeit der Übergabe by reference eingeführt wurde. Gekennzeichnet wird solch eine Referenz durch ein & hinter dem Datentyp. Dann wird eben statt der Kopie nur ein Verweis übergeben. Kann ungeheuer Zeit und Speicherplatz sparen. Wie führt das nun in dem Code zum Crash? Nun dafür muss man sich mal den Kopf der for-Schleife näher anschauen:
[...]
for (std::list<int>::const_iterator i=getBla().begin(); i!=getBla().end(); ++i) {
[...]
Im rot markierten Teil wird der Iterator i initialisiert und zeigt auf das erste Element der von getBla() zurückgelieferten Liste – einer Kopie der Originalliste, die durch die Rückgabe angefertigt wird. Im blau markierten Teil wird der Iterator vor jedem Schleifendurchlauf mit dem Endezeiger der von getBla() zurückgelieferten Liste verglichen – einer anderen Kopie, die ebenfalls wieder durch die Rückgabe by value entsteht. Nun, da der Iterator ursprünglich von der ersten Kopie an hochzählt, kann er nie den Endezeiger der zweiten erreichen, der komplett woanders liegt. Also bricht die Schleife nicht ab, wie es korrekt wäre, sondern läuft ein zweites mal durch. Durch das Hochzählen zeigt der Iterator nun hinter das Ende der ersten Kopie, per Definition also ins Nichts. *BUMM* Ändert man den Rückgabetype von getBla() auf eine Referenz, so bekommt man bei jedem Aufruf dieselbe Liste (nämlich bla selbst). Und siehe da, dann funktioniert's! Also merke: Der Rückgabewert eines Getters ist grundsätzlich eine konstante Referenz!*grmpf* (Konstant übrigens deswegen, damit einem nicht irgendein Genie plötzlich die eigenen Daten unterm Hintern wegändert.) Friday, 31. March 2006Inline-Doku at its best...Ein besonders schönes Stück Dokumentation aus einem Codestück, an dem ich gerade sitze: /*---------------------------------------------------------------------------*/ Nein, er ist übrigens nicht von uns. Wednesday, 4. January 2006Zu lange arbeiten schadetGrad im ICQ: ******** 16:45:51: puuh, jetzt hab ich mich aber selber verarscht Hihi... P.S: Weil ich weiß, daß er/sie/es es liest: es ist im allgemeinen keine Gute Idee (TM) schlüsselwörter von C++ in C zu verwenden... Wednesday, 21. December 2005A-Gen 0.1.2 released
Vorhin eben habe ich mal wieder ein paar kleinere Probleme an A-Gen behoben und eine neue Version veröffentlicht. Jetzt sollte das Package wieder unter Debian unstable und Ubuntu installieren und der Code mit GCC 4.x kompilieren. Damit ist Version 0.1 soweit am Ende ihrer Entwicklung. Welche Features Version 0.2 genau bieten wird, weiß ich noch nicht. Auf jeden Fall soll die Möglichkeit zum automatischen Aufsplitten der Index-Seite kommen.
Sunday, 18. December 2005Template-Spielereien die x-teDie schon vor längerer Zeit mal vorgestellten Templatespielereien sind irgendwann tatsächlich mal in ein paar Projekten benutzt worden. Aus verschiedenen Gründen kam plötzlich die Anforderung: "Mach den Logger doch mal bitte thread-safe!". Äh, ja... Konkret sollte der Ausdruck logger << LEVEL_DEBUG << "Eine Debug-Nachricht." << endl; nicht unterbrechbar sein, damit sich in der Ausgabe keine Nachrichten überlappen. Das ist nicht ganz so trivial, wie es scheint, denn in Wirklichkeit ist das nicht ein einziger Ausdruck, sondern dort stehen insgesamt 3 mehr oder minder unabhängige Teilausdrücke. Formt man dieses Zeile in die klassische Aufrufform um, so sieht das so aus (mal die Version der freien Operatoren, die nicht Element eines Typs sind, verwendet): operator<<(operator<<(operator<<(logger, LEVEL_DEBUG), "Eine Debug-Nachricht."), endl);. Man sieht also, daß hier dreimal ein operator<< aufgerufen wird, dessen Rückgabewert als Parameter für weitere Aufrufe verwendet wird. Um nun die gesamte Zeile thread-safe zu bekommen, muss der innerste Aufruf (welcher zuerst ausgeführt wird) einen Mutex locken und der äußerste muss ihn freigeben. Ersteres ist einfach: da das erste Element einer Zeile immer ein Level sein soll, kann man das locken des Mutex in diesen verlagern. Die zweite Anforderung ist schon schwieriger. Es ist für einen Operator nicht feststellbar, daß er der äußerste (und damit letzte einer Zeile) ist. Nun könnte man festlegen, daß jede Zeile mit endl angeschlossen werden muss und dann bei Empfang dieses Modifiers den Mutex wieder freigeben. Aus demselben Grund, wieso ein Operator nicht feststellen kann, daß er der letzte ist, kann man allerdings die Regel mit dem endl nicht erzwingen. Es ist zwar möglich, zu verhindern, daß auf ein endl noch etwas folgt, allerdings kann nicht erzwungen werden, daß überhaupt eines auftauchen muss. Um nun zu vermeiden, daß sich der Programmierer um diese internen Details kümmern muss, kann man die Lebenszeitregeln von C++-Objekten nutzen. Dazu modifiziert man die Ausgabeoperatoren so, daß der Typ von logger direkt nur noch Loglevel-Typen als Parameter akzeptiert. So erzwingt man, daß das erste einer Zeile ein Level ist. Der Ausgabeoperator verpackt den übergebenen Logger dann in ein temporäres Objekt und liefert dieses zurück. Außerdem lockt er den Mutex. Die folgenden Ausgabeoperatoren nehmen als ersten Parameter dieses temporäre Objekt, führen ihre Ausgabeoperation auf den enthaltenen Logger aus und liefern das Objekt wieder zurück. Auch der letzte Operator in der Zeile liefert dieses Objekt zurück. Da es von niemandem mehr entgegen genommen wird, wird es vernichtet. Dabei wird sein Destruktor aufgerufen, welcher den gelockten Mutex wieder freigibt. Auf diese Weise ist die gesamte Zeile vor Unterbrechung geschützt. Um den Logger nun portabel zu halten, sollte das schützende Mutex-Objekt nicht Element des Loggers sein. Leider ist Multithreading meist mit der Verwendung systemspezifischer Bibliotheken verbunden, so daß der Logger dadurch auf ein bestimmtes System festgelegt würde. Um das zu vermeiden, verwendet man ein Proxy-Objekt, welches eine Schnittstelle Lock() und Unlock() anbietet. Dieses nutzerdefinierte Objekt kapselt den eigentlichen, systemspezifischen Mutex. Dieses Objekt (bzw. genaugenommen dessen Typ) wird dem Logger in Form eines Templateparameters übergeben. Dieser ruft die entsprechenden Funktionen dann bei Bedarf auf. Auf diese Weise bleibt der eigentliche Logger-Code unabhängig vom zugrundeliegenden Threadframework. Diese skizzierten Ansätze habe ich im ursprünglichen Logger implementiert, den Code noch etwas aufgeräumt und auf einer eigenen Seite (Vorsicht: Englisch) abgelegt. Dort findet sich auch die Schnittstellendokumentation und etwas übersichtlichere Hinweise zur Benutzung. Saturday, 1. October 2005Bereichsgeprüfte Datentypen in C++Nach all dem privaten, nichttechnischen Kram mal wieder etwas technisches. Schließlich muss man den Unmengen von Lesern (so 3-5) ja auch was bieten. Statt Sex, Drugs and Rock'n'Roll gibt es mal wieder C++ und Templates. Aber von vorn... In einem Anfall von Langeweile habe ich mich mal ein wenig nach Links zur Programmiersprache Ada umgeschaut. Diese wird generell als die Sprache der Wahl zur Entwicklung sicherer Software gehandelt, da sie viele eingebaute Möglichkeiten bietet, Korrektheit im Programm sicherzustellen. Ein nettes Feature, welches mir aufgefallen ist sind bereichsgeprüfte Datentypen. Man kann in Ada einen numerischen Datentyp in folgender Form definieren:
type Time_Of_Day is range 0..86399; Das definiert einen Typ Time_Of_Day, der Werte von 0 bis 86399 annehmen kann. Das hat mich dazu inspiriert, mal wieder ein wenig mit Templates rumzuspielen um sowas in C++ nachzubauen. Ziel war es, möglichst wenig Speicherplatz pro bereichsgeprüfter Variable zu verwenden und möglichst wenig Laufzeitoverhead zu haben. Grundsätzlich kann man einen bereichsgeprüften Datentyp auf zwei Arten implementieren: einmal können die Grenzen als Membervariablen in jedem Objekt abgelegt sein und zum anderen können diese als Templateparameter übergeben werden. Die Entscheidung zwischen beiden Varianten ist eine Abwägung zwischen Codegröße und Datengröße. Die Templatevariante führt bei vielen verschiedenen Typen mit unterschiedlichen Grenzen zu einer erheblichen Vergrößerung des Quellcodes, da für jedes Paar von Grenzen eine Templateinstanz angelegt wird. Auf der anderen Seite führt die Haltung der Grenzen im Objekt selbst bei vielen Objekten zum einem erheblichen Speicherbedarf, da nun jedes Objekt Platz für die Werte der Grenzen braucht. Da es das Ziel war, möglichst wenig Speicher für die Objekte zu verwenden, habe ich mich für die Templatevariante entschieden. Das Template wird wie folgt verwendet: RangeChecked<int, 0, 10> var(5). Hiermit wird eine Variable var mit dem Interval [0,10] angelegt und auf den Wert 5 initialisiert. Da sich 5 doch recht klar in [0,10] befindet, geht das auch gut. Befände sich der Initialisierungswert außerhalb, würde eine Exception vom Typ RangeError geworfen. Das Template stellt verschiedene Operationen für numerische Datentypen (+, -, /, = und so weiter) zur Verfügung. Zu beachten ist hierbei, da immer nur die Zuweisungen geprüft sind. Überschreitet also der Wert eines Ergebnistyps innerhalb einer Gleichung kurzzeitig den Bereich, so passiert nichts, da erst der Zuweisungsoperator prüft. val = 10 + 5 - 7; wäre im obigen Beispiel also kein Problem, da das Endergbenis bei der Zuweisung wieder in [0,10] liegt. Eine Zuweisung Variablen unterschiedlicher Intervalle ist auch möglich, solange der zugewiesene Wert im Intervall des Ziels liegt. Als Templateparameter kommen prinzipiell numerische Typen in Betracht. Jedoch gibt es hier eine Ausnahme: Fließkommazahlen können keine Verwendung finden. Der Grund hierfür liegt in den Regeln des C++-Standards. Dieser spricht folgendes zu non-type parameters: 3 A non-type template-parameter shall have one of the following (optionally cv-qualified) types: Fließkommazahlen als Templateparameter sind also nicht erlaubt (hauptsächlich der erste Teil mit den integral types ist interessant. Eine genaue Begründung dafür kann ich nicht geben. Der Versuch einer Diskussion findet sich in de.comp.lang.iso-c++. Vorherrschende Meinung scheint zu sein, daß sich bei der Instanziierung von Templates mit Fließkommaparametern gewisse Probleme mit der Genauigkeit der Zahlendarstellung ergeben. Das Problem der nicht exakten Darstellbarkeit vieler Fließkommanzahlen in Computern wird bei Vergleichen in Programmen typischerweise dadurch umgangen, daß zwei Werte als gleich definiert werden, wenn sie innerhalb einer Schwelle Epsilon liegen. So vermeidet man Probleme durch Rundungsfehler etc. Im Fall von Templateparametern ist das allerdings nicht so einfach. Dort gibt es nur Gleichheit oder Ungleichheit. So könnte eine etwas groteske Situation entstehen: für folgendes Template existieren die beiden Instanziierungen a und b: template <double d> class Test
{
};
Test<1.0> a;
Test<0.7+0.3> b;
Sind diese beiden Instanzen vom selben Typ? Der gesunde Menschenverstand würde sagen "Ja, denn 0.3+0.7==1.0.". Ein Compiler kann das eventuell anders sehen. Da weder 0.3, noch 0.7 auf gängigen Architekturen exakt binär darstellbar sind, könnte es durch Rundungsfehler dazu kommen, daß statt 1.0 eben nur 0.9999999 entsteht. Verdammt nah dran an der 1.0, aber eben nicht gleich. Eine Verwendung von b an einer Stelle, wo Test<1.0> erwartet wird müßte also scheitern. In dem verlinkten Thread werden einige Ansätz diskutiert, wie dieses Problem zu lösen sei (symbolisch rechnen, nur Literale als Parameter erlauben). Alle diese Ansätze haben gewisse Probleme (Aufwand, Unterschiede zwischen verschiedenen Compilern etc.). Man darf also gespannt sein, ob sich das Standardisierungskommitee dieses Problems annimmt und wenn ja, wie die Lösung aussieht. So, nach all dem Geblubber noch schnell das Template zum Runterladen: RangeChecked.hxx. Der Code ist wieder public domain. Die Verwendung ist im Kopf der Datei nochmal erklärt. Tuesday, 5. July 2005Hurra, mein erstes eigenes SF/freshmeat-Projekt!
Endlich hab ich es geschafft, mal ein Tool zu veröffentlichen! Ich bin ja fast von mir selbst begeistert. A-Gen ist ursprünglich aus der Not heraus entstanden, daß alle Webalbumgeneratoren, die ich so gefunden habe entweder zu unflexibel oder zu kompliziert zu benutzen waren oder dynamischen Content voraussetzten (typischerweise PHP oder sowas). Also hab ich angefangen, mir was eigenes zusammenzuhacken, was jetzt irgendwie etwas ausartet und irgendwann mal die Weltherrscha^W^Wden Albummarkt beherrschen soll :)
(Seite 1 von 2, insgesamt 20 Einträge)
» nächste Seite
|