C++Guns – RoboBlog blogging the bot

15.08.2014

polymorphic objects can live on the stack

Filed under: Allgemein — Tags: — Thomas @ 19:08

Polymorphie kennt ihr ja, Grundzüge von OO;
Im groben geht das ja so


class Base {
  public:
  virtual void func() {
     cout << "Base\n";
  }  
};

class Derived : public Base {
  public:
  virtual void func() {
    cout << "Derived\n";
  }  
};

int main() {
  Base *p = new Derived();
  p->func();  // print "Derived"
}

Ich habe ein Pointer vom Type Base und dennoch wird die Funktion von Derived aufgerufen.
Das hier funktioniert allerdings auch:


int main() {  
  // We dont need to create the object on the heap with new
  // Base *p = new Derived();
  
  // Allocate this object on the stack
  Derived d;   
  Base *p = &d;
  p->func(); // prints "Derived" and not "Base"
}

Objekte auf dem Stack werden wesentlich schneller allokiert als auf dem Heap. Das müsste ein deutlichen Geschwindigkeitsvorteil für kleine und kurzlebende Objekte sein.

24.07.2014

Welcher Code ist "schöner" ? ;)

Filed under: Allgemein — Tags: , — Thomas @ 14:07

Kurzes Programm, welches überpüft ob der Benutzer eine Variable "resolution" definiert hat. Wenn ja, wird versucht die Benutzereingabe in eine Zahl umzuwandeln. Wenn nein, wird ein default Wert von 1 angenommen.

Variante 1:


const QString resolution = options().text("resolution");
bool userResolution = false;
// check if user defined cellsize
qreal newCellSize = 1;
if(!resolution.isEmpty()) {            
  userResolution = true;
  bool ok;
  const qreal userCellSize = resolution.toDouble(&ok);
  if(ok) {
    newCellSize = userCellSize;
  }
  else {
    qDebug() << "Cant parse user defined resolution:" << resolution;
  }
}

Die Variable userResoltion ist nicht als const declariert und könnte überschrieben werden. Machen wir sie read-only!

Variante 2:


const QString resolution = options().text("resolution");
const bool userResolution = !resolution.isEmpty();
// check if user defined cellsize
qreal newCellSize = 1;
if(!resolution.isEmpty()) {            
  bool ok;
  const qreal userCellSize = resolution.toDouble(&ok);
  if(ok) {
    newCellSize = userCellSize;
  }
  else {
    qDebug() << "Cant parse user defined resolution:" << resolution;
  }
}

Der Code sieht noch etwas aufgebläht aus. Trennen wir den normalen Programm Path und den Error Path, der durchlaufen wird, wenn die Benutzereingabe ungültig ist.

Variante 3:


qreal toDouble(const QString& s) throw(Exception) {
    bool ok;
    qreal x = s.toDouble(&ok);
    if(!ok)
        throw Exception("Cant convert to double" + s);
    return x;
}

...

const QString resolution = options().text("resolution");
const bool userResolution = !resolution.isEmpty();
// check if user defined cellsize
qreal newCellSize;
try {
      newCellSize = toDouble(resolution);
}catch(...) {
      newCellSize = 1.0;
      if(!resolution.isEmpty())
           qDebug() << "Cant parse user defined resolution:" << resolution;
}

Nun möchten wir die Variable newCellSize auch noch read-only haben, da sie ein Parameter ist und niemals mehr geändert werden darf. Dafür lagern wir nun die komplette Logik für das Parser der Benutzereingabe in eine Funktion aus.

Variante 4:


qreal toDouble(const QString& s, qreal defaultValue = 0.0) {
    if(s.isEmpty())
        return defaultValue;

    bool ok;
    qreal x = s.toDouble(&ok);
    if(!ok) {
        qDebug() << "Cant convert to double" + s;
        return defaultValue;
    }
    return x;
}

...

const QString resolution = options().text("resolution");
const bool userResolution = !resolution.isEmpty();
// check if user defined cellsize
const qreal newCellSize = toDouble(resolution, 1.0);

Der wesentliche Code ist jetzt nur noch 4 Zeilen lang, statt 15 Zeilen.
Er ist auf das wesentliche reduziert, verständlicher und gegen das versehentliche überschreiben der Variablen geschützt.
Nachteile gibt es allerdings auch. Die Fehlermeldung wenn das Konvertieren fehl schläng ist nicht anpassbar. Mit einem zustäzlichen Funktionsargument könnte man das aber nachbessern.

28.05.2014

Qt - Approximate median without sort / storing an extra copy of the data

Filed under: Allgemein — Tags: , — Thomas @ 20:05

Es gibt viele Möglichkeiten den Median zu berechnen. Eine wäre die Daten zu sortieren und dann das mittlere Element zu wählen.
Dazu muss man aber eine Kopie der Daten speichern, was ungünstig ist, wenn der Arbeitsspeicher schon fast voll ist. Jaaa, heutzutage hat jeder Unmengen an Arbeitsspeicher. Es gibt aber Menschen die haben "NUR" 2GB RAM und die sind durch die aufgeblasenen Standardprogramme (Browser, Office, Mail, Clould, Solzial) schon zu 85% belegt. Und wenn man dann noch etwas Arbeiten will und sich ein paar Datensätze visualisiert, können schon 50MB mehr zum Überlaufen des RAMs führen ;)

(50MB entsprechen ca. 7Millionen double Variabeln)

Es wäre doch toll, wenn man den Median berechnen könnte, ohne den Datensatz zu verändern oder zu kopieren!

Eine Runde googlen:
http://stackoverflow.com/questions/638030/how-to-calculate-or-approximate-the-median-of-a-list-without-storing-the-list/638713#638713

Ich hab diesen Algorithmus mal in Qt implementiert und ein wenig verändert. Bei einer grade Anzahl von Eingangsdaten berechnet er ursprüngliche Algorithmus den falschen Median. Ich habe es jetzt so geändert, dass immer das oberste der beiden mittleren Elemente im Array zurückgegeben wird. Das kann man irgendwann auch noch mal verbessern.

Noch ein Wort zur Laufzeit. Die Anzahl der Iterationen betrug bei meinem Tests meist so um die 50. Das muss eigentlich auch so sein, denn wir haben es hier mit einer binären Suche zu tun. Denn bei jeder Iteration wird der Zahlenbereich, der mit einer double precision floating number Variable dargestellt werden kann, in 2 Hälften zerlegt. So tanzt der approximierte Median immer um den echten Median herrum, bis die Zahlengenauigkeit fein genug ist.
Mit double Precision bekommt man etwa 16 Ziffern Genauigkeit. Und der 2er Logarithmus von 50 ergibt eine Zahl mit 16 Ziffern. Das passt doch :D Man muss also immer 50mal den Zahlenbereich aufteilen um an den Median zu kommen.

Bei jeder dritten Iteration bekommt man also etwa eine Ziffer an Zahlengenauigkeit dazu. Gibt man sich mit nur 8 Ziffern Genauigkeit zufrieden, braucht man auch nur 25 Iterationen.

Wir haben also einen Agorithmus dessen Laufzeit linear mit der größe des Datensatzes wächst O(n*50). Das ist doch super! Ganz anders als die Variante welches erst die Daten sortiert O(n*log(n)). In der Praxis bringt einem das aber erstmal nichts an Laufzeit. Denn wie groß muss der Datensatz werden, damit der log(n) = 50 wird? 1,125,899,906,842,624 ! Also erst ab einer Billiarden Zahlen hätten wir ein Laufzeitvorteil. Dazu bräuchte man aber auch 8000 Terrabyte an Speicherplatz :D

Gibt man sich in Qt/C++ eine Zahl in der Konsole aus, bekommt man in der Regel nur 6 Ziffern angezeigt. Das entspricht 18Iterationen. Also log(n) = 18 für n = 262,144 Hier sieht die Welt schon besser aus. Schon ab 260,000 Zahlen bekomen wir ein Laufzeitvorteil.

Wir haben also ein Algorithmus, der keinen zusätzlichen Speicherplatz braucht und auch noch in der Laufzeit einstellbar ist. Gibt man sich nur mit wenigen relevanten Ziffern des approximierten Medians zufrieden, ist die Laufzeit schon bei kleinen Datenmengen besser als die des Sortierverfahrens.

Happy coding ;)


/*
Find Min and Max of the list containing N items through linear search and name them as HighValue and LowValue Let MedianIndex = floor(N/2)

1st Order Binary Search:

Repeat the following 4 steps until LowValue < HighValue.

    Get MedianValue approximately = ( HighValue + LowValue ) / 2

    Get NumberOfItemsWhichAreLessThanorEqualToMedianValue = K

    is K > MedianIndex ? then HighValue = MedianValue Else LowValue = MedianValue


  */
/*!
  Return the median. If data has even size, not the avg of the two numbers in the
  middel of the array is returned, but the upper one.

  This is an improved version from
  http://stackoverflow.com/questions/638030/how-to-calculate-or-approximate-the-median-of-a-list-without-storing-the-list/638713#638713

  This algorithm need in practice 50 iterations. Like a binary search around the true median.
  */
qreal experimenalMedian(const QVector &data) {
    if(data.isEmpty())
        return 0.0;

    QPair minmax = qMinMax(data);
    int medianIdx = qFloor(data.size()/2.0);
    qreal medianValue = (minmax.first + minmax.second)/2.0;

    while(minmax.first < minmax.second &&
          !fuzzyCompare(minmax.first, minmax.second)) {
        medianValue = (minmax.first + minmax.second)/2.0;
        int K = 0;
        for(int i=0; i < data.size(); i++) {
            if(data.at(i) <= medianValue) {
                K++;
            }
        }
        K > medianIdx ? minmax.second = medianValue : minmax.first = medianValue;
    }
    return medianValue;
}


07.05.2013

safer code

Filed under: Allgemein — Tags: , — Thomas @ 21:05

Was ist das Problem mit folgenden Code?


    Element *ele                // uninitialisierter pointer;
    while(iter->hasNext()) {    // schleife kann auch nicht ausgefuehrt werden
        ele = iter->next();
        ...
    }
    qDebug() << ele;            // segfault
 

Wir nehmen mal an, dass qDebug() hier normalerweise nicht stehen würde. Man wollte sich aber das Element ausgeben lassen und hat vergessen die Debug Ausgabe wieder zu löschen.
Problem ist, dass die Variable ele durch qDebug dereferenziert wird, obwohl der Pointer ins Nirvana zeigen kann, wenn die Schleife nicht ausgeführt wird.
Der erste Fehler ist den Pointer nicht zu initialisieren. Man hätte ihn auf 0 (nulptr) setzen können. Das hätte den segfault aber nicht verhindert, aber das Debuggen erleichtert.
Der eigentliche Fehler ist, dass ele ausserhalb der Schleife deklariert wurde. Das Element darf nur innerhalb der Schleife existieren, da es auch nur dort gebraucht wird. Wie gesagt gäbe es im Normalfall auch keine Debug Ausgabe mit qDebug().

Leider gibt der Compiler in diesem Fall auch kein Warning aus (gcc 4.7 -Wall -Wextra)

Hier der richtige Code. Der Scope von ele wurde verkleinert und der Pointer sofort mit einer gültigen Adresse belegt.


    while(iter->hasNext()) {
        Element *ele = iter->next();
       qDebug() << ele;
    }
 

26.04.2013

C++ Guns - explicit keyword und warum man es braucht

Filed under: Allgemein — Tags: , — Thomas @ 08:04

Stell dir vor, du willst die Qt Klasse QPolygonF erweitern damit man noch zusätzlich eine z Koordiante abspeichern kann.
Du erstellst also eine Klasse Polygon die von QPolygonF public erbt.
Dafür hast du dir eine Klasse Point3D erstellt, die eben x,y,z speichert. Und damit man zu Qt's QPointF Klasse kompatibel bleibt, die ja nur 2D arbeitet, kann man ein Point3D aus einem QPointF erstellen. Die Z Koordiante wird entsprechend auf 0 gesetzt.

Nun fängst du an die Klasse Polygon zu implementieren und schreibst eine push_back(Point3D) Funktion. Die x,y Koordinate wird weiter an QPolygonF::push_back() gegeben und den Z Anteil wird irgendwie in Polygon gespeichert.

Um an seine Daten wieder dran zu kommen gibt es mehere Möglichkeiten. Eine davon geht über die Funktion at(). Nun vergisst du aber gerade diese Funktion zu überladen. Kann schonmal passiert. QPolygonF (mit QVector) hat über 30 Funktionen die angepasst werden müssten. Noch dazu haben wir ja keine Zeit und der Compiler wird es auch nicht als Fehler werfen.

Hier die Datenstruktur. Unrelevate Teile hab ich weggelassen.

class Point3D {
public:
...
Point3D(const QPointF & point);
...
};

...
Point3D::Point3D(const QPointF &point)
: x(point.x()), y(point.y()), z(0) {
}

class PolyShape : public QPolygonF {
public:
void push_back ( const Point3D & node);
...
};


Hier mein kleines Testprogramm:

PolyShape poly;
Point3D pointIn(1.,2.,3.);
qDebug() << "put in " << pointIn.x << " "<< pointIn.y << " " << pointIn.z; poly.push_back(pointIn); Point3D pointOut = poly.at(0); qDebug() << "get out" << pointOut.x << " "<< pointOut.y << " " << pointOut.z;

Ich gebe also ein Punkt mit den Koordianten 1, 2, 3 in das Polygon ein und erwarte, dass der selbe Punkt wieder rauskommt.

Starting /home/kater/qtcode/test2/test2...
put in 1 2 3
get out 1 2 0

So ein Mist! Die Z Koordinate ist verschwunden! Aber ist ja auch kein Wunder. Da Polygon kein at() hat, wird die entsprechende Funktion von QPolygonF ausgerufen. Die speichert aber nur QPointF und keine z Koordiante.
Der Fehler passiert, wenn man ein QPointF einem Point3D zuweist. Durch den entsprechenden Konstructor in Point3D klappt das auch, aber wollten wir das so eigentlich?

Man kann mit dem Keyword explicit in diesem Fall einem Compilerfehler erzwingen.

explicit Point3D(const QPointF & point);

main.cpp:62: error: conversion from 'const QPointF' to non-scalar type 'Point3D' requested

Verwende explicit immer dort, wo der Konstructor nur ein Argument hat und es nicht der Copy Ctor ist.

Besser wäre es aber ein anderes Design zu finden. Jede Funktion von QPolygonF zu überladen klingt nicht so gut. Vllt. kommen in der Zukunft ein paar neue hinzu oder es fallen einige weg. Noch mehr Arbeit...

28.03.2013

Qt Source und kleine Perlen

Filed under: Allgemein — Tags: , , — Thomas @ 23:03

Ich habe in den letzten 3 Tagen viel Qt Source gelesen und da war schon viel Wirr und Krams dabei. Aber auch kleine Perlen wie diese Zeile in QRectF QPolygonF::boundingRect() const zeige

register const QPointF *pd = constData();
for (int i = 1; i < size(); ++i) { ++pd; }

Da kennt ein Performance-Fan das Keyword register. Ich war richtig überrascht und erfreut auch mal ein Stück optimierten Code zu finden.
Erklärung: das Keyword register sagt dem compiler, dass der Pointer bitte die ganze Zeit über in einem CPU Register bleiben soll. Die Registert sind ja die schnellsten Speicher die es in so einer CPU gibt, und da der Pointer in der nachfolgenden Schleife etliche mal benutzt wird, macht das auch Sinn.
Sogar das const keyword ist dabei. Das sagt dem Compiler, dass er diesen Pointer nicht in den Hauptspeicher zurück speicher muss, da er zu 100% nicht verändert worden ist. Und der Zugriff auf den Hauptspeicher ist im Vergleich zu dem Registert bestimmt 1000x langsamer.

++pd;
Selbst bei dieser recht unbedeuteten Zeile Code gibt es etwas zu sagen. Es gibt eine Menge Post im Internet die vermuten, dass ++i schneller sei als i++ [1] [2]. Aber der Compiler wird das schon optimieren. Jedenfalls weiß der Autor von QPolygonF::boundingRect() das, und ich finde es schön :)

[1] http://stackoverflow.com/questions/24853/what-is-the-difference-between-i-and-i
[2] http://stackoverflow.com/questions/24886/is-there-a-performance-difference-between-i-and-i-in-c

22.12.2012

Qt && Unicode

Filed under: Allgemein — Tags: , — Thomas @ 14:12

Dieses kleine Programm wandelt Text wie ".and." "&&" u.s.w. in Unicode Zeichen um.
Irgendwann kommt noch eine automatische Umformung zur konjunktive Normalform KNF

KNF1


    // input
    QString text = ui->lineEdit->text();

    // replace easy to write operator with unicode symbol
    // and
    text = text.replace("&&", QChar(8743));
    text = text.replace(".and.", QChar(8743), Qt::CaseInsensitive);
    // or
    text = text.replace("||", QChar(8744));
    text = text.replace(".or.", QChar(8744), Qt::CaseInsensitive);
    // negation
    text = text.replace("!", QChar(172));
    text = text.replace(".not.", QChar(172), Qt::CaseInsensitive);

    // output
    ui->lineEdit_out->setText(text);

Liste_der_Unicodeblöcke
Unicodeblock_Lateinisch-1,_Ergänzung
Unicodeblock_Mathematische_Operatoren

todo:
History der eingegebenen Formeln mit Ergebnis der Umrechnung. Wenn man eine Formel in der History anklickt, soll sie wieder im Eingabeformular erscheinen.

08.12.2012

Mehrere Object Files zusammenfassen

Filed under: Allgemein — Tags: , , — Thomas @ 13:12

Für eine .a lib:
ar -ru test.a 1.o 2.o

Für eine große .o Datei:
ld -r objectList -o destObject

06.10.2012

Installing gcc, g++ and gfortran 4.8 from source

Filed under: Allgemein — Tags: , , , — Thomas @ 16:10

First read http://gcc.gnu.org/install/
Make sure you have all Prerequisites installed!

Download all files to ~/opt/downloads
Install all packages to ~/opt/

Download gcc 4.8 from ftp://ftp.gwdg.de/pub/misc/gcc/snapshots/LATEST-4.8/ and unpack it.


wget ftp://ftp.gwdg.de/pub/misc/gcc/snapshots/LATEST-4.8/gcc-4.8-20120930.tar.bz2
tar xjf gcc-4.8-20120930.tar.bz2 
cd gcc-4.8-20120930

Download several necessary librarys into the gcc source directory and unpack them.
They will be built together with GCC.


wget ftp://ftp.gmplib.org/pub/gmp-5.0.5/gmp-5.0.5.tar.bz2
tar xjf gmp-5.0.5.tar.bz2
ln -s gmp-5.0.5 gmp

wget http://www.mpfr.org/mpfr-current/mpfr-3.1.1.tar.bz2
tar xjf mpfr-3.1.1.tar.bz2 
ln -s mpfr-3.1.1 mpfr

wget http://www.multiprecision.org/mpc/download/mpc-1.0.1.tar.gz
tar xzf mpc-1.0.1.tar.gz
ln -s mpc-1.0.1 mpc

Now configure and build


mkdir $HOME/opt/downloads/gcc-4.8-build
cd $HOME/opt/downloads/gcc-4.8-build
../gcc-4.8-20120930/configure --prefix=$HOME/opt/gcc-4.8 --enable-threads --enable-languages=c,c++,fortran
make
make install

Troubleshooting
/usr/bin/ld: cannot find crt1.o: No such file or directory
/usr/bin/ld: cannot find crti.o: No such file or directory

These files are usually locates in /usr/lib32/ and/or /usr/lib/x86_64-linux-gnu/
Add this to your .bashrc


export LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/:$LIBRARY_PATH

08.05.2012

Eclipse Code ausdrucken

Filed under: Allgemein — Tags: , , — Thomas @ 17:05

Da die Print Möglichkeiten aus Eclipse heraus ziemlich bescheiden sind, habe ich mal etwas gesucht und das gefunden[1] :

a2ps --verbose --landscape --columns=2 --rows=1 --line-numbers=5 --tabsize=2 --pretty-print \
--highlight-level=normal --sides=duplex --pro=color FILENAME

[1] http://stackoverflow.com/questions/252975/how-to-print-out-code

« Newer PostsOlder Posts »

Powered by WordPress