static-Eigenschaften in JavaScript

Standard

Ich spiele gerade ein wenig mit Node.js herum. Es dauerte eine Weile, bis ich verstanden habe, wie man in JavaSrcipt static Eigenschaften einer Klasse abbilden kann. Der Hintergrund war, dass ich Werte für Konfigurationen der Applikation in einer Klasse speichern wollte, um sie überall im Programm-Code zur Verfügung zu haben. In C++ hätte ich das so abgebildet, indem ich eine Klasse definiere, deren Eigenschaften „static“ sind. Wo immer ich dann im Code eine solche Klasse initialisiere, sind ihre Werte die selben.

In JavaScript lassen sich Klassen-Eigenschaften jedoch nicht mit „static“ deklarieren. Stattdessen arbeitet man mit so genannten „prototype“. Jedes SavaScript hat eine Eigenschaft „prototype“. Wird in diesen Namensraum eine Eigenschaft angelegt, gilt sie für alle Objekte die von der selben Klassen-Type sind. Zur Verdeutlichung ein kurzes Beispiel:

var AppConfig = function AppConfig(){};
AppConfig.prototype.filename = "config-file.json"
AppConfig.prototype.db = {
    host: "localhost",
    port: "9999"
};

var app_config = new AppConfig();
module.exports = app_config;

Zunächst wird mit der ersten Zeile ein Constructor für die Klasse „AppConfig“ definiert. Das würde in C++ so ausgedrückt.

class AppConfig {
    public:
         AppConfig(){};
}

Der nächste Schritte fügt der Klasse eine neue Static-Eigenschaft hinzu. Das wiederum würde in C++ etwa so aussehen:

class AppConfig {
    public:
        AppConfig(){};
        static std::string filename;
};

AppConfig::AppConfig(): filename(„config-file.json“){ }

In der Zeile darunter sieht man, wie komplexere Daten-Strukturen angelegt werden. In der vorletzten Zeile wird die Klasse instantiiert. Die letzte Zeile ist ein Konstrukt das benötigt wird, um die Klasse in einem Node.js-Projekt zu verwenden. Mit dieser Zeile kann die Klasse überall hin importiert werden:

var app_config = require('./app_config.js');

Wo immer eine Instanz dieser Klasse verwendet wird, haben deren Eigenschaften die gleichen Werte (jedenfalls alle Prototype-Eigenschaften). Auf diese Weise kann man sich das mühsame Durchreichen von Werten über Methoden-Parameter ersparen.

Buchtip: JavaScript für C++-Programmierer

Standard

cover_javascript-oop Vermutlich bin ich nicht der Einzige, der aus der C++-, Python- oder Java-Welt kommt und das Konzept von JavaScript für objektorientierte Programmierung verwirrend findet. Warum sich C++-Entwickler überhaupt mit JavaScript beschäftigen könnten, habe ich bereitsl HIER thematisiert. Im Urlaub las ich ein Buch gelesen, das bei mir ein Aha-Erlebnis ausgelöst hat: „JavaScript objektorientiert: Verständlicher, flexibler, effizienter programmieren„, von Nicholas Zakas; erschienen 2014 im dpunkt.verlag. Es erklärt sehr gut die Stärken von JavaScript. Auch wenn C++ eine  Multiparadigmen-Sprache ist, lassen sich einige Sachen in JavaScript doch eleganter umsetzen, als in C++. Deshalb halte ich die Kombination aus C++ und JavaScript tatsächlich für sinnvoll in Node.js. Das Buch zeigt an leicht verständlichen Beispielen, was mit JavaScript möglich  und was best practices ist. Andere Beispiele zeigen, was zwar möglich, aber besser zu unterbleiben ist (Anti-Pattern). Der winzigste Wermutstropfen ist der Preis. Die gerade mal 120 Seiten kosten stolze 19,95 Euro. Zum Vergleich: Das „JavaScript kurz & gut“ vom O’Reilly Verlag hat 277 Seiten zum Preis von 12 Euro.

MVC-Architektur in Tntnet (Variante III.)

Standard

In der Vergangenheit habe ich bereits zwei Varianten (hier und hier), wie man mit Tntnet ein MVC-Konzept umsetzen kann, vorgestellt. Heute zeige ich eine dritte Möglichkeit.

Diese nutzt eine Template-Engine namens „NLTemplate“. Der Projekt-Code ist kompakt und überschaubar, herunterzuladen bei GitHub. Installation und  Benutzung sind ausreichend dokumentiert, daher gehe ich hier nicht weiter darauf ein, sondern beschreibe lediglich die Benutzung in Tntnet.

Auf Eines sei aber noch vorher hingewiesen : das Template-Engine läuft, wenn es das angegebene Template-File nicht laden kann, in einem segment foult. Das bringt den gesamten Application-Server von Tntnet zum Absturz. Deshalb habe ich den Code für mich geforkt und angepasst. Jetzt wirft bei mir die Template-Engine Exception, wenn sich das Template-File nicht öffnen lässt. Der Tntnet-Application-Server kann damit sauber umgehen.

Durch die Verwendung von NLTemplate ist die Trennung zwischen View- und Controller-Code noch stärker, als es mit Bordmitteln von Tntnet möglich ist. Die Template-Files enthalten keinerlei interpretierten Code oder irgend welche „Magic“ mehr. Der zweite Vorteil, den man aber auch als Nachteil sehen kann, ist dass die Template-Files zur Laufzeit geladen werden. Das bedeutet, dass man die Template-Files ändern kann ohne den kompletten Code neu übersetzen zu müssen. Der dritte Vorteil ist, dass man den Precompiler ecppc nicht mehr braucht. Das vereinfacht den Build-Prozess.

Hier zu meinem Beispiel : es gibt zwei Template-Files. „site_skeletal.nlt“ ist das Template, dass die Seiten-Struktur abbildet, die auf jeder Seite gleich bleibt.

<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>{{ site_title }}</title>
    </head>
    <body>
        {{ main_content}}
    </body>
</html>

Der Bereich „{{ main_content}}“ wird ersetzt durch den individuellen Teil der Seite. Die zweite Template-Datei sieht so aus:

<h1>Use the NLTemplate system</h1>
You can generate items with loops:
<ul>
{% block town_items %}
    <li>Title: {{ town_name }}</li>
{% endblock %}
</ul>
You can use this template system without a pre compiler.

Dieser Inhalt wird in das obere Tamplate eingefügt. Der Teil, der mit dem Schlüsselwort „block“ gekennzeichnet ist, kann in einer Schleife beliebig generisch wiederholt werden. Mit den doppelt geschweiften Klammern werden Schlüsselwörter gekennzeichnet, die durch einen beliebigen Inhalt ersetzt werden können.

Jetzt zum Controller-Code:

#include <tnt/ecpp.h>
#include <tnt/httprequest.h>
#include <tnt/httpreply.h>
#include <tnt/httpheader.h>
#include <tnt/http.h>
#include <tnt/data.h>
#include <tnt/componentfactory.h>
#include <stdexcept>
#include <ostream>
#include <vector>

#include <NLTemplate/NLTemplate.h>

log_define("component.alternativ_hello")

namespace
{
class _component_ : public tnt::EcppComponent
{
    _component_& main()  {
        return *this;
    }

  protected:
    ~_component_();

  public:
    _component_(const tnt::Compident& ci, const tnt::Urlmapper& um, tnt::Comploader& cl);
    unsigned operator() (tnt::HttpRequest& request, tnt::HttpReply& reply, tnt::QueryParams& qparam);
};

static tnt::EcppComponentFactoryImpl<_component_> Factory("alternativ_hello");

_component_::_component_(const tnt::Compident& ci, const tnt::Urlmapper& um, tnt::Comploader& cl)
  : EcppComponent(ci, um, cl){
}

_component_::~_component_(){}

unsigned _component_::operator() (tnt::HttpRequest& request, tnt::HttpReply& reply, tnt::QueryParams& qparam)
{
    std::vector<std::string> towns;
    towns = {"Hamburg", "Frankfurt", "München"};

    NL::Template::LoaderFile loader;
    NL::Template::Template nl_main_content( loader );
    nl_main_content.load( "src/hello/view/main_content.nlt" );
    nl_main_content.block( "town_items" ).repeat( towns.size() );
    for ( unsigned int i=0; i < towns.size() ; i++ ) {
        nl_main_content.block( "town_items" )[ i ].set( "town_name", towns[ i ] );
    }
    std::stringstream ss_main_content;
    nl_main_content.render( ss_main_content );

    NL::Template::LoaderFile skeletal_loader;
    NL::Template::Template nlTemplate( skeletal_loader );
    nlTemplate.load( "src/hello/view/site_skeletal.nlt" );
    nlTemplate.set( "site_title", "About NLTemplate" );
    nlTemplate.set( "main_content", ss_main_content.str() );

    nlTemplate.render( reply.out() );

    return HTTP_OK;
}

} // namespace

Ich lade hier die zwei Templates-Files:  erst das Template mit dem Haupt-Inhalt, das ich mit generischen Inhalt befülle. Dann lade ich das Template für die Seiten-Struktur und füge den Variablen Teil des Haupt-Inhaltes ein.

NLTemplate bietet auch die Möglichkeit, ein Template in ein anderes zu includieren. Ich mag den Ansatz jedoch nicht, weil dann das Seiten-Skelett-Tamplate in einen Kopf- und Fuß-Teil aufgetrennt werden muss. Wenn das Seiten-Skelett-Tamplate an Komplexität zunimmt, wird es recht schnell unübersichtlich.

Dieses Beispiel funktioniert nur mit dem Master-Branch vom Tntnet-Projekt. In der nächsten Zeit werde ich versuchen das Beispiel auch mit dem 2.Xer-Zweig (dem offiziellen Release-Zweig) zum Laufen zu bekommen.

C++ mit node.js

Standard

Ich habe mich die letzten Wochen damit beschäftigt, auszuloten was mit der C++-Schnittstelle in Node.js (Genannt „Addon“) möglich ist.

Dazu habe ich ein kleines Ruby-on-Rails-Projekt mit Node.js reimplementiert. Ich war ziemlich begeistert, wie schnell man zu einem lauffähigen Prototypen kommt. Mit JavaScript habe ich mich anfangs schwer getan. Wenn man aus der Welt einer Multiparadigmen-Sprache wie C++ kommt kommt, fühlt sich JavaScript beengend an. Sicher ist dieses Callback-Konzept eine nette Geschichte. Jedoch lassen sich nicht alle Aufgaben mit diesem Ansatz am elegantesten lösen. Wer mal bei Google nach „node.js callback hell“ sucht, wird ein paar interessante Artikel finden. JavaScript hat aber unbestritten seine Fans, wahrscheinlich sogar mehr Fans als C++.

Nach einer Weile bin ich bei meinem Portierungsprojekt auf ein Problem gestoßen. Es betraf indirekt die fehlende Fähigkeit zu Nebenläufigkeit in Node.js. Nebenläufigkeit kann ziemlich kniffelig werden. Vielleicht war das auch der Grund warum man bei Node.js darauf verzichtet hat, übrigens genauso wie bei Ruby-on-Rails.

Eine Herausforderung war, dass mein Programm verschiedene Daten aus unterschiedlichen Quellen bezog. Da war die Konfiguration des Programms selbst, die sich im Betrieb ändern könnte und in einer Textdatei gespeichert würde. Die Änderungen sollten zur Laufzeit gelesen werden können, ohne die Applikation neu starten zu müssen. Eine weite Datenquelle sind die Verbindungsdaten der Telefonanlage, die abgefragt wird. Die dritte Datenquelle sind Zugverbindungen, die über eine externe Website eingeholt werden. Als Viertes wird vom Programm „fortune cookie“ noch ein „Spruch des Tages“ geholt.

Was ich natürlich nicht wollte, war das all diese Datenquellen mit jedem Browser-Request neu abgefragt würden. Ich präferierte den Weg, die Daten im RAM zu cachen. Deshalb mochte ich auch keine temporäre Daten verwenden, die ich immer wieder von der Festplatte auslese und damit unnötigen I/O-Operationen produziere. Darüber hinaus war der Refrash-Interval für alle Datenquellen unterschiedlich:

  • Die Konfiguration wollte ich ereignisbasiert auslesen, wenn sie geändert wurde
  • Die Telefonanlage sollte alle 5 Sekunden abfragt werden
  • Die Zugverbindungen sollten einmal in der Minute abgefragt werden
  • Der „fortune cookie“ sollte einmal am Tag erneuert werden

Über Google fand ich den Befehl „fs.watchFile(filename, [options], listener)“ (http://nodejs.org/api/fs.html#fs_fs_watchfile_filename_options_listener), um Änderungen an Dateien zu beobachten. Eine Änderung löst einen zu definierenden Callback aus. Also genau das, was ich für meine Konfiguration brauchte.

Ich suchte nach einem Befehl, mit dem ich ein Callback in einem bestimmten Intervall aufrufen lassen kann. Ich fand recht schnell die setInterval()-Methode und war fast begeitert, wie einfach man Probleme mit Node.js (bzw. JavaScript) lösen kann. Jedoch verhielt sich setInterval() nicht ganz so, wie ich es erwartete. Ich schrieb in mein Node.js-Code:


setInterval( getPhonData, (1000 * 5 ) );
setInterval( getTrainData, (1000 * 60 ) );
setInterval( getFortuneCookie, (1000 * 60 * 60 * 24) );

Jeder Callback wurde einmal aufgerufen und dann war Schluss. Wenn ich den t3n-Artikel vom 20.07.2014 schon gekannt hätte, wäre ich schneller darauf gekommen, was das Problem war. Mit jedem setInterval-Aufruf setzt man nicht einen neuen (eigenständigen) Timer sondern, man überschreibt den zuvor gesetzten. Somit kann man nur einen Timer haben. Man kann zwar ein bisschen mit Worker tricksen, die Möglichkeiten sind aber beschränkt. Es gibt keine vollwertigen Threads, die sich ihre Ressourcen teilen können.

Das war für mich der Anlass, mich an ein C++-Addon für Node.js zu versuchen. Ich wollte setInterval() als Multi-Threading-Variante implementieren. Um es gleich vorweg zu nehmen: ich bin gescheitert. Man findet im Web einige nette „Hallo-Welt“-Beispiele für Node.js-Addons in C++. Aber in einem Addon mehrere Threads zu starten und zu verwalten, ist dann doch was anderes, als ein „hello world“ auf die Konsole zu schreiben.

Zu den Faktoren, die bei der Aufgabenstellung hinderlich waren, zählt dass ich keine Node.js Mailingliste gefunden habe. Es gibt dutzende Foren und Mailinglisten für PHP und Ruby (deutsch/englisch), aber für Node.js sieht es mau aus. Es gibt eine Goole-Gruppe, die aber auch – dafür das sie die einzige ihrer Art ist – relativ wenig Aktivitäten hat. Die Fehlermeldungen von Node.js waren wenig hilfreich. Das Programm beendet sich einfach kommentarlos. Der Debugging-Modus hat mir auch kaum geholfen, denn man sieht nicht, was in dem Addon passiert.

Dabei ist mir noch mal deutlich geworden, das ein C++-Addon das sich übersetzen lässt, leider noch lange nicht lauffähig sein muss. Der Gluten-Code ist eine sehr lockere Bindung zwischen JavaScript und C++. Nur weil der GCC nicht meckert, muss das nicht heißen, dass JavaScript die C++-Klasse auch wirklich erreicht. Ein C++-Entwickler der wie ich, die Strenge eines C++-Compilers schätzt, wird in seiner Begeisterung etwas gedämpft werden.

Unangenehm fand ich auch, dass man gezwungen wird in der Wrapper-API Zeiger statt Referenzen zu verwenden, das ich wegen der damit verbundenen Risiken bei der Speicherverwaltung gerne vermeide. Richtige Typsicherheit gibt es natürlich auch nicht. Jeder übergebene Wert sollte deshalb besser überprüft und mit Fehlerbehandlungen versehen werden.

Es gab aber auch positive Erfahrungen. So funktionierte die C++11-Unterstützung problemlos, was die Thread-Programmierung erleichtert hätte, wenn es sie gegeben hätte. Auch das Node.js eigene Build-Tool (gyp) fand ich vom Konzept her nett. Mir gefiel besonders, dass die Konfiguration mit Json erfolgt und keine kryptische-esoterische Macro-Sprache gelernt werden muss.

Mein Gesamteindruck ist, dass die Schnittstelle zwischen JavaSript und C++ in Node.js für die JavaScrip-Programmierer wesentlich komfortabler ist als für die C++-Entwickler. Die JavaScript-Programmierer können das Interface (den Wrapper der C++-Klassen und Methoden) verwenden, wie sie es von ihrer Sprache gewohnt sind. Die C++-Entwickler müssen sich hingegen mit einem nicht selbsterklärenden Konzept mit wenig Dokummentation auseinandersetzen. Zudem liegt die gesamte Last für die API-Sicherheit bei den C++-Programmierern. Sie müssen dafür sorgen, dass Ausnahmen abgefangen werden sowie dass Typen und Werte geprüft werden. Das dürfte die Attraktivität von Node.js für C++-Entwickler leider senken.

CPPSP (C++ Server Pages) – Kurzvorstellung

Standard

CPPSP (C++ Server Pages) ist ein Projekt, das ich kürzlich entdeckt habe. CPPSP hat das Ziel, ein ASP.NET für C++ zu realisieren. CPPSP ist ein OpenSource-Projekt, das ohloh.net nach zu urteilen, 2011 entstanden ist und zwei Hauptentwickler hat. Die Produktivität scheint in den letzten Monaten etwas nachgelassen zu haben.

Das Haupt-Repository wirkt auf mich ein wenig wie "Kraut und Rüben". Ich habe auch eine Datei gefunden, die das Wt(Witty)-Frame-Work verwenden. Davon ist aber auf der Projekt-Seite nichts zu lesen.

Ich habe keine Mailing-Liste gefunden. Auch gibt es weder auf der Website noch im Source-Code eine Mail-Adresse, um mit den Entwicklern in Kontakt zu treten. In der FAQ wird geschrieben man solle den Issues-Tracker der Repository verwenden.

Wirkt alles nicht sehr überzeugend auf mich. Man kann mit MS-Produkten auch SAP.NET mit C++ verwenden. Mit Mono gibt es auch schon eine OpenSource-Implementierung, auch wenn dies nur C# unterstützt.

Ein weiteres Hinderns für den Erfolg dieses Projektes sehe ich in der mangelhaften Dokumentation. Es gibt quasi nur eine ABI-Doku, jedoch keine Einführung.


Creative Commons Lizenzvertrag