vorteile einer virtual machine?
-
mathik schrieb:
eine VM muss auch für diese Plattformen irgendwie entwickelt und kompiliert werden. Und es gibt ja genug gute Compiler für diese Plattformen. Und wenn ich für den Kunden für jede Plattform eine Binärversion anbiete, was hat man da für einen Nachteil?
n gewisser zeitaufwand wenn du updates für 20 plattformen erstellen musst
-
BWL-Manager schrieb:
@mathik: Warum schwer, wenn es auch einfacher geht? Bei der Entwicklung VM ist jemand anderes verantwortlich. Du sparst jede Menge Zeit und kosten.
Bei den anderen Punkten das selbe. Warum das Rad neu erfinden und viel Geld in die Entwciklung stecken?
Im Buiness läuft es halt immer nach dem selben Schema: Man muss den breakeven zwischen Kosten und Nutzen finden. Genau diesen Punkt haben Sun und MS erkannt. Eine VM erleichtert dem Programmierer die Arbeit ungemein.
ich bechaupte doch nicht, dass man das alles selber machen sollte, weil es dann viel besser ist. Ich finde sogar Java toll, weil diese Sprache eine solche umfangreiche Bibliothek hat, GC und Reflection anbietet. Ich würde sie allerdings noch toller finden, wenn Java-Programme auch ohne die VM laufen würden, und somit schnellere Startzeiten (Wegfall von JIT) und weniger Speicher fressen würden.
-
Vorteile einer virtuellen Maschine:
Plattformunabhängigkeit: Programme für eine virtuelle Maschine laufen auf allen realen Maschinen, für die die virtuelle Maschine implementiert ist. Sie kann dadurch Architekturtransparenz schaffen.
dynamische Optimierung ist möglich
-
Wollte ich Dir auch nicht unterstellen. Aber gerade eine VM macht es einem so einfach(s. Optimizer's Beispiele).
Die langen Ladezeiten stören primär auch nur bei Desktopanwendungen. Ich pers. mag allerdings auch Binärcode lieber, aber man macht das was der Arbeitgeber möchte.
-
mathik schrieb:
Optimizer schrieb:
- Ich kann prozessorspezifisch optimieren.
- Compile once, run anywhere gewinnt heute an Bedeutung durch verschiedene Prozessorarchitekturen. Überlegt mal:
IA64, x86_64 und x86 mal Windows, Linux gibt schon mal 6 verschiedene Kombinationen.eine VM muss auch für diese Plattformen irgendwie entwickelt und kompiliert werden. Und es gibt ja genug gute Compiler für diese Plattformen. Und wenn ich für den Kunden für jede Plattform eine Binärversion anbiete, was hat man da für einen Nachteil?
Ich habs doch grad aufgezählt, dass du gut und gerne auf 6 Versionen kommen kannst. Und es ist auch nicht völlig ausgeschlossen, wenn auch nicht das größte Problem, dass du vielleicht mal ein Problem mit einer von den 6 Versionen hast, was du sonst nicht hast.
Optimizer schrieb:
- Sicherheit: Ich kann ein und das selbe Programm mit verschiedenen Sicherheitseinstellungen laufen lassen, ohne es dauernd überwachen zu müssen. Ich muss nicht bei jeder Anforderung nach einem Socket-Handle prüfen, ob das Programm das darf. Stattdessen start ich das Programm mit "no sockets!" und der JIT-Compiler pflanzt dann im native Code überall SecurityExceptions rein.
ja ok, aber wieso wird dafür dieser Bytecode benötigt? Man könnte doch auch den bereits vorhandenen native code mit SecurityExceptions versehen.
Es geht darum, das Programm mit verschiedenen Einstellungen zu starten und du kannst den native Code nicht dynamisch anpassen. Lies es bitte nochmal.
Optimizer schrieb:
- Ich kann Code flexibler gestalten. Klassen dynamisch zur Laufzeit hinzuladen. Runtime-Reflection. Das sind Dinge, die immer mehr an Bedeutung gewinnen. Diese Flexibilität krieg ich ohne JIT-Compilierung fast nicht hin.
Dieser ganzer "Schnickschnack" kann doch auch im native Code gemacht werden.
Klassen, die dynamisch nachgeladen werden sollen, könnten z.B. in dynamische Bibliotheken, wie DLLs, ausgelagert werden. Man bräuchte eine standardisierte Schnittstelle dafür.Nein, in dem Maße, wie es mit Java und .Net möglich ist, kriegst du das nicht mal annähernd hin. Ich sag nur: Runtime-Reflection. Ohne VM verflucht schwer und dynamisch Klassen hinzuzuladen, wie willst du das bitte bei native code machen? Ja, du brauchst ne standardisierte Schnittstelle dafür, *thumbs up*. Da bin ich gespannt, wie die aussehen soll. Vergiss es, fast nicht tragbarer Aufwand.
JIT-compilierter Code ist von Natur aus flexibler, das kannst du nicht wegdiskutieren. Du kannst für dich entscheiden ob du das willst und brauchst, aber dein fest kompilierter Native Code ist faktisch nicht so flexibel.
-
Hi
Artchi schrieb:
...
Hem, du hast dich eben gerade unglaubwürdig gemacht. Warum muß die .NET VM von MS kommen?Das will mir nicht in mein Gehirn rein. C# und das .NET Baseframework sind in der ECMA zertifiziert: ein freier Standard, und somit kann und SOLL nicht nur MS die VM und das Framework auf anderen Plattformen implementieren. Ist also wie mit C++: jeder darf .NET implementieren. (nur WindowsForms und ein paar andere .NET-Teile sind nicht in der ECMA)
...
ich geb zu ich hab noch nichts mit .Net gemacht. ich weis nur das es ein Basisframework von MS gibt und das für linux MONO entwickelt wird. Soweit ich das mal mitbekommen hat war MS federführen am .Net beteiligt. bzw hat das dan versucht es als standard festschreiben zu lassen. Mit mal wieder so schöhnen ausnahmen das ms sachen nur bei ms laufen und wo anders nicht.
Ich hab das gefühl MS den Standard nur als Alibi und wegen des Marketings gemacht hat.
Wer würde auf .Net umsteigen, wenn nur MS Compiler und Framework liefern dürfte? Und man dafür auch noch bezahlen müsste. ist ja dan so ähnlich wie mit Delphi von Borland.[EDIT]
Weiter Vorteil für VMs. sie lassen sich ja unabhängig von der Anwendung warten. Somit kann auch eine Anwendung noch mit sicherheit patchen versorgt werden obwohl die Anwendung gar nicht mehr weiterentwickelt wird. ( sun hate ja mal nen fehler in der JRE 1.4.? ) währen das jetzt alles Nativ drin, hätte der Anwender nicht mal ne nöglichkeit so ein sicherheitsloch zu stopfen. Woher weis er was du verwendet hast, woher soll er das nötige update bekommen.gruss
-
Ein echter Vorteil (zumindest von .net) ist ein vereinheitlichtes Klassenmodell zwischen verschidenen Sprachen (habe ich gerade "C DLLs in VB" gehört? *graus*). Das hat zwar primär nichts mit der VM zu tun, ist aber doch ein nettes Feature. Wenn .NET nur ein *wirklich* offener Standard wäre, ohne das MS wegen Sachen wie Mono mit den Patent - Ketten rasselt ...
-
Optimizer schrieb:
mathik schrieb:
Dieser ganzer "Schnickschnack" kann doch auch im native Code gemacht werden.
Klassen, die dynamisch nachgeladen werden sollen, könnten z.B. in dynamische Bibliotheken, wie DLLs, ausgelagert werden. Man bräuchte eine standardisierte Schnittstelle dafür.Nein, in dem Maße, wie es mit Java und .Net möglich ist, kriegst du das nicht mal annähernd hin. Ich sag nur: Runtime-Reflection. Ohne VM verflucht schwer und dynamisch Klassen hinzuzuladen, wie willst du das bitte bei native code machen? Ja, du brauchst ne standardisierte Schnittstelle dafür, *thumbs up*. Da bin ich gespannt, wie die aussehen soll. Vergiss es, fast nicht tragbarer Aufwand.
JIT-compilierter Code ist von Natur aus flexibler, das kannst du nicht wegdiskutieren. Du kannst für dich entscheiden ob du das willst und brauchst, aber dein fest kompilierter Native Code ist faktisch nicht so flexibel.Ich hab mal nach Reflection gesucht, da ich nicht in Java eingearbeitet bin und bin auf folgenden Link gestossen:
http://java.sun.com/docs/books/tutorial/reflect/
(Fals dieser Link nicht das beschreibt was du unter Runtime-Reflection verstehst dann entschuldige ich mich im Voraus)Ich seh da nichts was übermässig schwer mit nativ Code zu machen wäre, natürlich nur mit ein wenig Unterstüzung des Compilers und kompatiblen ABIs.
* Determine the class of an object.
typeid & dynamic_cast
* Get information about a class's modifiers, fields, methods, constructors, and superclasses
* Find out what constants and method declarations belong to an interface.Kann der Compiler alles in den VTable packen.
* Create an instance of a class whose name is not known until runtime.
Das Factory Pattern unter der Compilerhaube.
* Get and set the value of an object's field, even if the field name is unknown to your program until runtime.
Offset und Type kann im VTable nachgeschlagen werden wenn der Compiler diese Information da reintut.
* Invoke a method on an object, even if the method is not known until runtime.
Kann man auch noch im Vtable nachschlagen. Alles was man braucht ist eine Map die den Funktionsnamen mit dem Funktionspointer assoziiert.
Hier kann man allerdings auch noch ein wenig optimiren indem man, nachdem man das erste mal den Namen nachgeschlagen hat, den Funktionsaufruf während der Laufzeit überschreiben um dann nächstes mal direkt die richtige Funktion aufzurufen. Der Compiler muss nur genügend NOPs während der Übersetzung reinpacken damit keine Sprünge durcheinander kommen. Wenn die Anzahl der Parameter bekannt ist, ist das auch kein Problem. Fals die Anzahl nicht bekannt ist kann man ein Arraypointer übergeben.
* Create a new array, whose size and component type are not known until runtime, and then modify the array's components.
Das ginge indem man std::vector von einer abstrakten Basisklasse erben lassen würde und in jeden VTable eine Funktion hinzufügen würde die ein std::vector für diese Klasse erstellt.
Jetzt mal ganz davon abgesehen, dass ich das meiste eher als Spielerei einstuffe als wirklich nützlich, ist nichts davon ohne VM unmöglisch oder wirklich schwerer als eine Implementirung ohne VM. Vieles würde ich sogar schwieriger mit einer VM die den Code in nativ Code übersetzt finden.
Der angebliche Vorteil der Anpssungfähigkeit des Bytecodes hink auch, denn wer hat gesagt, dass man dafür eine VM braucht? Man kann den Bytecode doch einfach in einem Installationsprogram in nativ Code übersetzen. Sehr ähnlich wird das doch schon heute mit sehr vielen open source Programmen gemacht, nur eben dass hier der Sourcecode zur Verfügung steht und kein Bytecode.
Weiter Vorteil für VMs. sie lassen sich ja unabhängig von der Anwendung warten. Somit kann auch eine Anwendung noch mit sicherheit patchen versorgt werden obwohl die Anwendung gar nicht mehr weiterentwickelt wird. ( sun hate ja mal nen fehler in der JRE 1.4.? ) währen das jetzt alles Nativ drin, hätte der Anwender nicht mal ne nöglichkeit so ein sicherheitsloch zu stopfen. Woher weis er was du verwendet hast, woher soll er das nötige update bekommen.
DLLs kann man auch austauschen, das heist Fehler in einer Biblothek können auch behoben werden. Ein Program in einer VM laufen zu lassen nur weil dem Compiler vielleicht ein Übersetzungsfehler unterläuft ist zwar ein Vorteil, allerings ein sehr schwacher Vorteil der Nachteile wie längere Ladezeit im Normalfall nicht aufwiegt.
Die hauptsächlichen Vorteile einer VM sind die gleichen wie die von jedem Interpreter. Der Code muss nicht installiert werden und kann daher sofort nachdem er zur Verfügung steht benutzt werden, sprich der Vorteil liegt bei vernetzten Rechnern von unterschiedlicher Platform die Code austauschen. Bei Desktoprechnern beschränkt das sich zum größten Teil auf Applets, Javascript und Ähnliches.
Man beachte bitte, dass viele Vorteile von Java Programmen weder etwas mit Java ansich, noch irgendetwas mit einer VM (wobei letzteres oft sogar ein Nachteil ist) zu tun haben. Java hat eine breitgefecherte Standard Bibliotek und wird auch ansonsten viel von Größen der IT Welt wie Sun gefördert. Das sind die wahren Vorteile von Java. Das gleiche gilt natürlich auch für .net.
Genau sowenig ist die Tatsache, dass viele Daus wahrscheinlich make als sehr schwer zu bedienen empfinden, ein teschniches Problem sondern eines einer bestimmten Implementirung. Hätte make ein GUI Interface wie viele Windows Installer, was teschnisch überhaupt kein Problem ist, würde das bereits viel bringen.
Desweiteren sind auch Portirungsproblem von C/C++ Programmen von Platform zu Platform oder Compiler zu Compiler ein Nachteil gegenüber Java allerdings kein Nachteil gegenüber einer VM. Wenn jede Java VM ihre eigenen Erweiterungen und ihre eigenen Biblioteken hätte dann würden, trotz VM, die gleichen Probleme herschen wie derzeit in der C/C++ Welt.
Viele Sprachen die eine VM haben Vorteile allerdings diese sind in der Regel nicht auf die VM zurück zu führen.
-
Ben04 schrieb:
Ich seh da nichts was übermässig schwer mit nativ Code zu machen wäre, natürlich nur mit ein wenig Unterstüzung des Compilers und kompatiblen ABIs.
* Determine the class of an object.
typeid & dynamic_cast
Das ist keine Reflection. Das Programm kennt natürlich seine Klassen, da ist nichts tolles dran. Interessant wird's erst, wenn du dynamisch Klassen hinzulädst. Ich kann z.B. ne IDE schreiben und dafür ne Plugin-Schnittstelle anbieten.
Ich les dann alle .class-Dateien in meinem plugins-Verzeichnis aus, die jeder Programmier nachträglich durch einfaches reinkopieren verändern oder hinzufügen kann. Für jedes Plugin erstelle ich einen Menüeintrag, nimm alle öffentlichen Methoden als Untermenü her und ruf sie nur noch auf, wenn der User den Eintrag auswählt.
Also (Pseudocode):Bestimme Dateinamen aller .class Dateien ladeKlase(dateiname) Alle öffentlichen Methoden getten erstelle dafür menüeinträge, die bewirken: invoke(methode)
Du kannst jetzt natürlich auch dein eigenes Format für Plugins entwerfen. Kannst irgendne config-Datei auslesen und dynamisch auf irgendwelche C-Funktionen in einer beiliegenden DLL linken. Dann hast du genau für diesen speziellen Anwendungsfall eine Lösung, die definitiv weniger schön ist.
* Get information about a class's modifiers, fields, methods, constructors, and superclasses
* Find out what constants and method declarations belong to an interface.Kann der Compiler alles in den VTable packen.
Genau, für Klassen, die er kennt, kann er das tun. Aber nicht für Klassen, die erst zur Laufzeit hinzugeladen werden.
* Create an instance of a class whose name is not known until runtime.
Das Factory Pattern unter der Compilerhaube.
Höh? Du weißt doch zur Compilierzeit noch nicht mal, was die Klasse des zu erstellenden Objekts für Konstruktoren hat. Da bin ich ja mal gespannt. Wie du ohne JIT-Compilierung dann noch eine Factory erstellen kannst.
* Get and set the value of an object's field, even if the field name is unknown to your program until runtime.
Offset und Type kann im VTable nachgeschlagen werden wenn der Compiler diese Information da reintut.
Was für ne VTable? Es gibt keine VTable für Klassen, die du nicht compilierst. Und zur Laufzeit kriegst du die Infos nicht mehr, außer du kannst zur Laufzeit noch was reincompilieren -> JIT-compilierung
* Invoke a method on an object, even if the method is not known until runtime.
Kann man auch noch im Vtable nachschlagen. Alles was man braucht ist eine Map die den Funktionsnamen mit dem Funktionspointer assoziiert.
Wie gesagt - es gibt keine VTable.
Hier kann man allerdings auch noch ein wenig optimiren indem man, nachdem man das erste mal den Namen nachgeschlagen hat, den Funktionsaufruf während der Laufzeit überschreiben um dann nächstes mal direkt die richtige Funktion aufzurufen. Der Compiler muss nur genügend NOPs während der Übersetzung reinpacken damit keine Sprünge durcheinander kommen. Wenn die Anzahl der Parameter bekannt ist, ist das auch kein Problem. Fals die Anzahl nicht bekannt ist kann man ein Arraypointer übergeben.
Wie, wo was? Der Compiler kann zur Laufzeit in deinem Fall nichts mehr machen. Du kannst nur Methoden aufrufen
- deren Name du zur Compilierzeit kennst
- deren Signatur du zur Compilierzeit kennst* Create a new array, whose size and component type are not known until runtime, and then modify the array's components.
Das ginge indem man std::vector von einer abstrakten Basisklasse erben lassen würde und in jeden VTable eine Funktion hinzufügen würde die ein std::vector für diese Klasse erstellt.
Auch wenn der Elementtyp und -größe erstmal zur Compilierzeit unbekannt ist? Ne. Wie willst du das machen? Du weißt ja noch nicht mal, ob dein dynamisch hinzugeladener Typ einen Standardkonstruktor hat.
Ich glaube, du hast nicht ganz bedacht, was Laufzeit-Reflection bedeutet. Du hast erstmal keine VTable und kannst sie erst zur Laufzeit erstellen, weil du vorher die Klassendefinition nicht hast. Du brauchst immer irgendein für deine Fälle angepasstes Format, oder von mir aus auch eins, was sehr flexibel ist, aber trotzdem wirst du nicht Klassen von anderen einfach reinladen können, wenn die anderen UND du dafür nicht spezielle Dinge vereinbaren.
btw. jetzt einen nativen JIT-Compiler schreiben zählt jetzt nicht.
-
Ben04 schrieb:
Man beachte bitte, dass viele Vorteile von Java Programmen weder etwas mit Java ansich, noch irgendetwas mit einer VM (wobei letzteres oft sogar ein Nachteil ist) zu tun haben. Java hat eine breitgefecherte Standard Bibliotek und wird auch ansonsten viel von Größen der IT Welt wie Sun gefördert. Das sind die wahren Vorteile von Java. Das gleiche gilt natürlich auch für .net.
...
Desweiteren sind auch Portirungsproblem von C/C++ Programmen von Platform zu Platform oder Compiler zu Compiler ein Nachteil gegenüber Java allerdings kein Nachteil gegenüber einer VM. Wenn jede Java VM ihre eigenen Erweiterungen und ihre eigenen Biblioteken hätte dann würden, trotz VM, die gleichen Probleme herschen wie derzeit in der C/C++ Welt.Viele Sprachen die eine VM haben Vorteile allerdings diese sind in der Regel nicht auf die VM zurück zu führen.
Genau auf den Punkt gebracht. Das ist genau das, was ich meine!
@Optimizer
ich kann nicht nachvollziehen, warum man für Laufzeit-Reflection eine VM braucht. Man könnte auch jede Klasse native in eine .class Datei mit den ganzen Reflection-Informationen (wie Funktionssignaturen, Konstruktoren, etc.) packen. Dann generiert halt der Compiler für jede Klasse eine getClass():Class Methode. Diese Class hat dann die ganzen Reflection-Informationen, die man in Java auch hat. Dann kannste auch "ladeKlasse(dateiname)" aufrufen und mit den ganzen gettern die Informationen aus der .class Datei holen. (öffnet mal mit einem Editor eine .class Datei!)In C++ ist (Laufzeit)-Reflection deswegen schwierig zu realisieren, weil es hierfür einfach keine Compiler-Unterstützung und keinen Standard fürs dynamische Laden gibt.
Ich bin ehrlich gesagt immer noch nicht von den Vorteilen einer VM überzeugt worden (außer bei den Server- und Applet-Geschichten). Der ganze andere Schnickschnack (Reflection, "SecurityExceptions") könnte meiner Meinung nach auch ohne eine VM realisiert werden.
Ich programmiere gerne in Java, weil es dort einfach eine geile Bibliothek für fast alles gibt.
Allerdings finde ich, dass Java noch erfolgreicher wäre, wenn es dort keine VM gäbe, weil dadurch die Anwendungen viel Speicher fressen und lange Startzeiten durch JIT haben. Die ganzen "Wartungssachen" sind für mich auch wirklich kein Argument. Ich denke jeder Anbieter kann es in Kauf nehmen, für jede Plattform eine native Version zu kompilieren (nicht neu zu schreiben!).
-
mathik schrieb:
@Optimizer
ich kann nicht nachvollziehen, warum man für Laufzeit-Reflection eine VM braucht. Man könnte auch jede Klasse native in eine .class Datei mit den ganzen Reflection-Informationen (wie Funktionssignaturen, Konstruktoren, etc.) packen.Tut mir leid, wenn ich etwas blöd frage: Was ist eine native .class - Datei? Ist das Teil ausführbar? Wenn ja, dann ist es ja fein, und ich muss es nicht mehr zur Laufzeit compilieren. Enthält es alle Reflection-Informationen, die ich brauche? Fein.
Dann generiert halt der Compiler für jede Klasse eine getClass():Class Methode. Diese Class hat dann die ganzen Reflection-Informationen, die man in Java auch hat. Dann kannste auch "ladeKlasse(dateiname)" aufrufen und mit den ganzen gettern die Informationen aus der .class Datei holen. (öffnet mal mit einem Editor eine .class Datei!)
Bedenke dies:
class Base { virtual void methode() = 0; }; int main() { Base* x; cout << "Welche Klasse möchten Sie hinzuladen?"; Class MyDerived; cin >> MyDerived; x = new MyDerived(); // Ich sag jetzt mal, das hat nen Standard-Konstruktor x->methode(); }
Oh verdammt. Das wird jetzt schwierig. Also das MyDerived hast du nicht zur Verfügung, während du main() compilierst. Mit irgendwelchen krassen Datenstrukturen kriegst du das vielleicht noch hin, die Methode dynamisch richtig zu binden. Aber das ist ja nicht das Ende der Fahnenstange.
Jetzt kann ich noch ein paar weitere Klassen hinzuladen. Jedesmal ändert sich die ganze dynamische Bindung. Das x->methode(); kann nach jedem hinzuladen einen anderen Effekt haben. Jedesmal muss sich das Verhalten von dynamic_cast und typeid() ändern. Und das Ganze ohne Neucompilierung des Hauptprogramms. Selbst wenn du das noch sauber hinkriegst, bist du jetzt schon um Längen inperformanter als Java, was einfach neu compiliert und immer noch völlig simpel dynamisch bindet. Und jetzt gehen die Probleme erst los.
Jetzt schaut dann die .class Datei so aus:
class Derived : Base { void foo(); };
Und ich stelle fest, ja, die implementiert gar nicht meine abstrakte Methode. Das gibt Ärger. Du hast keine Möglichkeit, dass zur Kompilierzeit festzustellen, musst jetzt in dein Programm also nen halben Compiler einbauen, um zur Laufzeit festzustellen, ob das dann überhaupt geht. Damit ist es schon fraglich, ob du dich jetzt überhaupt an meine kleine Einschränkung gehalten hast, die da lautete, keine JIT-Compiler in das Programm einzubauen.
Das Ganze kann noch unendlich viel komplizierter werden, wenn Mehrfachvererbung hinzukommt. Vergiss auch nicht, dass du durch das hinzuladen neuer Klassen, vorhandenen, eigentlich statischen Code ändern musst.
class Fraction { // Implementiert nicht op<< aber ein op(double) }; int main() { Type type; ... // Klasse Fraction laden Type* a new Type(5, 2); cout << *a; // Gibt ein double aus. }
Ich darf aber statt Fraction auch was anderes laden, oder?
Jetzt laden wir die Klasse Fraction2 hinzu, die den op<< aber implementiert.
Jetzt muss statt dem op(double) der op<< aufgerufen werden, der nicht mal Element der Klasse selber ist, sondern global.
Was wir jetzt haben ist keine dynamische Bindung, auch keine "extended super complicated dynamische Bindung", sondern völlig anderes Verhalten. Und das machst du mir ohne Compiler.Ne, ich bin ja eigentlich schon fast geneigt zu sagen, dass die Sprache C++ schon an sich fast zu kompliziert für solche Späße ist. Aber dann ohne JIT-Compiler noch... kann ich mir nicht vorstellen.
In C++ ist (Laufzeit)-Reflection deswegen schwierig zu realisieren, weil es hierfür einfach keine Compiler-Unterstützung und keinen Standard fürs dynamische Laden gibt.
Rate mal, wieso.
Ich bin ehrlich gesagt immer noch nicht von den Vorteilen einer VM überzeugt worden (außer bei den Server- und Applet-Geschichten). Der ganze andere Schnickschnack (Reflection, "SecurityExceptions") könnte meiner Meinung nach auch ohne eine VM realisiert werden.
Kann jeder zu stehen wie er will. Ich sehe in einer VM viele Vorteile und Nachteile, die allesamt im Laufe der Zeit behoben werden könnten. Ist aber nicht meine Absicht, dich zu überzeugen. Ich will dir nur zeigen, dass es Dinge gibt, die ohne VM nicht gehen, was ja der Topic war.
Ich programmiere gerne in Java, weil es dort einfach eine geile Bibliothek für fast alles gibt.
Allerdings finde ich, dass Java noch erfolgreicher wäre, wenn es dort keine VM gäbe, weil dadurch die Anwendungen viel Speicher fressen und lange Startzeiten durch JIT haben.Vielleicht bist du noch nicht tief genug in Java eingestiegen. Ein paar wirklich geile Features, wegen den unter anderem Java so geschätzt wird, sind ohne VM nicht möglich. Das geht schon los beim dynamischen Laden von Datenbank-Treibern *an mein Praktikum mit Schaudern denk*.
Du musst Java so nehmen wie es ist und ich finde es gut so.
-
Optimizer schrieb:
Kann der Compiler alles in den VTable packen.
Genau, für Klassen, die er kennt, kann er das tun. Aber nicht für Klassen, die erst zur Laufzeit hinzugeladen werden.[/quote]
Und die DLL wurde etwa nicht von Compiler übersetzt? Es ist ja nicht so, dass alle VTables in der exe Datei liegen müssen.Optimizer schrieb:
Höh? Du weißt doch zur Compilierzeit noch nicht mal, was die Klasse des zu erstellenden Objekts für Konstruktoren hat. Da bin ich ja mal gespannt. Wie du ohne JIT-Compilierung dann noch eine Factory erstellen kannst.
Für jede Klasse generiert der Compiler eine Funktion die eine Instance dieser Klasse erstellt. Nun legt er eine Map die den Namen der Klasse mit der Funktion assoziirt. Wenn du nun eine Dll lädst hat diese auch so eine Map. Während des Laden müssen sie nur zusammen gefügt werden.
Optimizer schrieb:
Hier kann man allerdings auch noch ein wenig optimiren indem man, nachdem man das erste mal den Namen nachgeschlagen hat, den Funktionsaufruf während der Laufzeit überschreiben um dann nächstes mal direkt die richtige Funktion aufzurufen. Der Compiler muss nur genügend NOPs während der Übersetzung reinpacken damit keine Sprünge durcheinander kommen. Wenn die Anzahl der Parameter bekannt ist, ist das auch kein Problem. Fals die Anzahl nicht bekannt ist kann man ein Arraypointer übergeben.
Wie, wo was? Der Compiler kann zur Laufzeit in deinem Fall nichts mehr machen. Du kannst nur Methoden aufrufen
- deren Name du zur Compilierzeit kennst
- deren Signatur du zur Compilierzeit kennstWer sagt, dass nur der Compiler ist der Code schreibt? Das Program kann ja selber an sich umschreiben.
In etwa so hatte ich mir das vorgestellt (Pseudo ASM)
loopup_name: pop eax ;Schlag Funktions Pointer nach und pack ihn in eax ;Generire den Code um die Funktion in eax aufzurufen und speicher ihn in eax pop ebx mov [ebx] eax pop eax ;füll alles von [eax] bis [ebx] mit NOPs auf jmp ebx ;Irgendwo im Code schreib_von: push schreib_bis push schreib_von push name jmp lookup_name nop nop schreib_bis:
Natürlich muss hierführ das Program schreibenden Zugriff auf seinen Code haben. Wenn das nicht der Fall ist, bleibt noch die Möglichkeit einen statischen Funktionspointer einzusetzen. Der Compiler setzt ihn auf loopup_name und diese Funktion setzt ihn dann auf etwas anderes. Da der Aufruf ja über einen Funktionspointer stattfindet kann man den Aufruf jeder Zeit woanders enden lassen.
Optimizer schrieb:
Du hast erstmal keine VTable und kannst sie erst zur Laufzeit erstellen, weil du vorher die Klassendefinition nicht hast. Du brauchst immer irgendein für deine Fälle angepasstes Format, oder von mir aus auch eins, was sehr flexibel ist, aber trotzdem wirst du nicht Klassen von anderen einfach reinladen können, wenn die anderen UND du dafür nicht spezielle Dinge vereinbaren.
Da bin ich ganz deiner Meinung, allerdings wie bereits gesagt kann man auch VTables aus einer DLL nachladen und in eine VTable kann sämtliche Informationen über einen Type speichern die bei der Übersetzung bekannt sind.
mathik schrieb:
Die ganzen "Wartungssachen" sind für mich auch wirklich kein Argument. Ich denke jeder Anbieter kann es in Kauf nehmen, für jede Plattform eine native Version zu kompilieren (nicht neu zu schreiben!).
Mit make ähnlichen Tools muss der Anbieter noch nicht mal nativ Versionen anbieten. Man müsste sich auf eine einheitlich Sprache einigen in der man Anwendungen verbreitet, das kann auch soetwas wie Java Bytecode. Dann stellt jedes OS ein Tool zur Verfügung mit der man den Code in nativ Code übersetzt und ganz nebenbei das Program auch noch installiert. Man hat somit sämtliche Vorteile der Wartbarkeit jedoch keine Nachteile der VM. Daus werden sich die Funktionsweise die heutigen Windows Installationsprogrammen ähnelt mit Sicherheit freuen. Man muss ihnen ja nicht sagen, dass der Compiler läuft, also machen sie sich auch keinen Kopf dadrüber
.
Optimizer schrieb:
class Base { virtual void methode() = 0; }; int main() { Base* x; cout << "Welche Klasse möchten Sie hinzuladen?"; Class MyDerived; cin >> MyDerived; x = new MyDerived(); // Ich sag jetzt mal, das hat nen Standard-Konstruktor x->methode(); }
Oh verdammt. Das wird jetzt schwierig. Also das MyDerived hast du nicht zur Verfügung, während du main() compilierst. Mit irgendwelchen krassen Datenstrukturen kriegst du das vielleicht noch hin, die Methode dynamisch richtig zu binden. Aber das ist ja nicht das Ende der Fahnenstange.
Also ich sehe die Schwierigkeit nicht? Wie bereits oben gesagt braucht der Compiler nur eine map<string, void*(*)(...)> in die EXE und in die DLL zu packen. Beim laden der Dll werden dann beide zusammen gefügt. Dann muss der Compiler aus
x = new MyDerived();
nur ein
x = constructor_map["MyDerived"];
machen. Vielleicht noch ein paar Tests machen, wie zum Beispiel ob so eine Klasse geladen ist und ob sie auch von Base erbt und dann wenn nötig eine Exception werfen.
Das einzige was nicht geht ist eine dynamisch hinzugeladene Klasse auf den Stack zu packen. Aber wofür gibt es std::auto_ptr? Einfach unter die Compilerhaube und der Programmierer wird es nie merken. MS macht das auch so in C++/CLI.
Allerdings frag ich mich wirklich wo du soetwas brauchen willst? Jedes Pluginsystem ist anders und ich kann mir nicht vorstellen, dass du mit einem so allgemeinem Anstatz glücklich wirst.
Optimizer schrieb:
Jetzt kann ich noch ein paar weitere Klassen hinzuladen. Jedesmal ändert sich die ganze dynamische Bindung. Das x->methode(); kann nach jedem hinzuladen einen anderen Effekt haben. Jedesmal muss sich das Verhalten von dynamic_cast und typeid() ändern.
Alte Klasse aus der Map rauswerfen und neue hinzufügen. dynamic_cast mit Hilfe einer virtuellen is_castable(type_info&) Methode implementiren. typeid spiegelt nur Laufzeit Informationen. Allerdings wenn du soetwas brauchst musst du nur eine zweite Map anlegen und dann nach gleichen Schema vorgehen wie um ich es bei der Erschaffung der einer Instance gezeigt habe.
Optimizer schrieb:
Und das Ganze ohne Neucompilierung des Hauptprogramms.
Würd ich auch nicht wollen. Wenn man erst am Code rumschreiben geht, dann wird es erst kompliziert. Ist aber alles machbar.
Optimizer schrieb:
Selbst wenn du das noch sauber hinkriegst, bist du jetzt schon um Längen inperformanter als Java, was einfach neu compiliert und immer noch völlig simpel dynamisch bindet. Und jetzt gehen die Probleme erst los.
Ich sehe nicht wieso das mit einer VM so performant sein soll. Das Nachschlagen in einer Map ist nicht langsam und wie oben gezeigt muss man es ja nur einmal tun. Unter Java ist das ja auch nicht kostenlos, der Code muss neu überstetz werden und wenn man dann noch optimiert sind das zwar einmalige Kosten aber sehr große in der das gesamte Program einfriert. Oder willst du das ganze threadsafe machen? Also in dem einen Thread läuft der Compiler und in einem anderen wird auf Eingaben des Users reagiert. Und was ist mit Instancen von Klassen die es nach dem neu kompilieren gar nicht mehr gibt? Was mit Funktionspointern (wenn es sowas in Java gibt) die nun ins Leere zeigen? Also ich glaub nicht, dass das so einfach geht wie du dier das vorstellst. Bitte um Aufklärung.
Optimizer schrieb:
class Fraction { // Implementiert nicht op<< aber ein op(double) }; int main() { Type type; ... // Klasse Fraction laden Type* a new Type(5, 2); cout << *a; // Gibt ein double aus. }
Ich gehe mal davon aus, dass dieses "Type" eigentlich ein "Fraction" sein soll und zwischen "a" und "new" noch ein "=" gehört.
Optimizer schrieb:
Ich darf aber statt Fraction auch was anderes laden, oder?
Jetzt laden wir die Klasse Fraction2 hinzu, die den op<< aber implementiert.
Jetzt muss statt dem op(double) der op<< aufgerufen werden, der nicht mal Element der Klasse selber ist, sondern global.
Was wir jetzt haben ist keine dynamische Bindung, auch keine "extended super complicated dynamische Bindung", sondern völlig anderes Verhalten. Und das machst du mir ohne Compiler.Das Zauberwort heist double Dispatching. Und man kann jeden buildin Operator auch durch einen Funktionsaufruf ersetzen.
Sebst Templates kannst mit einer nachgeladenen Klasse spezialisieren. Man braucht zunächst wieder eine Map die den Namen der Klasse als Schlüssel hat. Jetzt schlägt man einen Klassen VTable mit sämtlichen Methoden der Klasse nach. Alles was kein Operator muss wieder in einer weiteren Map nachgeschlagen werden. So nun spezialisert der Compiler während des Übersetzen für einen Type der alle Methoden und Operatoren auf so ein Klassen VTable umlenkt. Wenn du willst kannst du auch den compilierten Template Code kopieren und dann die Funktionsaufrufe wie oben gezeigt nachschlagen. Du erhälst zwar keinen Code der bis in den letzten Winkel micro optimiert ist allerdings ganz brauchbar ist und das viel schneller als wenn du den Code neuübersetzrn würdest.
Optimizer schrieb:
In C++ ist (Laufzeit)-Reflection deswegen schwierig zu realisieren, weil es hierfür einfach keine Compiler-Unterstützung und keinen Standard fürs dynamische Laden gibt.
Rate mal, wieso.
Hmm, vielleicht weil es viel Arbeit ist und wenig bringt? So in etwa wie mit export.
Meiner Meingung nach ist es Schwachsinn auf das statische Interface dynamisch zurück greifen zu wollen. Reflexion ist nützlich solang es dazu diehnt Klassen zu benutzen von denen man nur die Basisklasse kennt, hier nimmt es einem zum Beispiel ab das Factory Pattern implementiren zu müssen. Allerdings sobald man auf Funktionen zurückgreifen will deren Existenz du während der Übersetzung noch nicht mal ansatzweise geahnt hat, dann hast du ein dickes Denkproblem. Sollten solche Machenschaften bei Java die Regel sein (wovon ich nicht ausgehe), dann fange ich an zu verstehen wieso soviele Leute Java hassen.
-
Ben04 schrieb:
Optimizer schrieb:
class Fraction { // Implementiert nicht op<< aber ein op(double) }; int main() { Type type; ... // Klasse Fraction laden Type* a new Type(5, 2); cout << *a; // Gibt ein double aus. }
Ich gehe mal davon aus, dass dieses "Type" eigentlich ein "Fraction" sein soll und zwischen "a" und "new" noch ein "=" gehört.
Die zweite Annahme ist korrekt, die erste nicht. Es soll ja wirklich ein beliebiger Typ erstmal sein, mit Konstruktor(int, int).
Optimizer schrieb:
Ich darf aber statt Fraction auch was anderes laden, oder?
Jetzt laden wir die Klasse Fraction2 hinzu, die den op<< aber implementiert.
Jetzt muss statt dem op(double) der op<< aufgerufen werden, der nicht mal Element der Klasse selber ist, sondern global.
Was wir jetzt haben ist keine dynamische Bindung, auch keine "extended super complicated dynamische Bindung", sondern völlig anderes Verhalten. Und das machst du mir ohne Compiler.Das Zauberwort heist double Dispatching. Und man kann jeden buildin Operator auch durch einen Funktionsaufruf ersetzen.
Du ignorierst völlig, worauf ich hinauswollte. Es geht darum, dass das Nachladen einer Klasse zur Laufzeit das Verhalten von bereits vorhandenem Code ändern kann.
Aber das ist ja für dich kein Problem:
Wer sagt, dass nur der Compiler ist der Code schreibt? Das Program kann ja selber an sich umschreiben.
Vielleicht noch ein paar Tests machen, wie zum Beispiel ob so eine Klasse geladen ist und ob sie auch von Base erbt und dann wenn nötig eine Exception werfen.
Und so lange du nicht anerkennst, dass du mit sowas einen halben Compiler in dein Programm einbaust, wirst du kein Problem damit haben.
Ein "paar Tests" beinhaltet ja vermutlich auch das Prüfen auf mehrdeutige Aufrufe, insbesondere im Zusammenhang mit Mehrfachvererbung ebenso wie in Kombination mit Templates und anderen extrem komplizierten Tests, die auch die statischen Compiler noch nicht seit langem alle standardkonform durchführen.Du unterschätzt das Problem. Das Problem ist, dass du dich auf nichts mehr verlassen kannst. Während cout << a; im ersten Moment noch eine Memberfunktion operator(double) aufrufen kann, kann es im nächsten Moment schon eine globale Funktion sein. Das kannst du nicht mit irgendwelchen Datenstrukturen so toll flexibel gestalten, dazu musst vorhandenen, bereits compilierten Code wieder ändern. Und das macht dein Programm, was einen halben Compiler in sich hat, natürlich auch.
Und die DLL wurde etwa nicht von Compiler übersetzt? Es ist ja nicht so, dass alle VTables in der exe Datei liegen müssen.
Das hab ich ja nicht gesagt. Aber du kannst trotzdem nicht die VTable in die DLL flacken und dann dynamisch hinzuladen, weil die VTable in die Umgebung passen muss, in die sie hineingeladen wird.
Wenn du die Basisklasse A mit einer abstrakten Methode hast und B implementiert sie, dann lädst du C : B dynamisch hinzu. Wie sieht die VTable von C aus? Richtig, C muss die Methode von B nehmen. Wenn jetzt aber A die Methode implementiert und B sie nicht redefiniert, muss C die Methode von A nehmen. C kennt A und B zu seiner Kompilierzeit nicht, weil das die schnucklige Klasse ist, die ich seperat erstellen und in meine DLL tun will. Du kannst die VTable nicht erstellen, ohne das Umfeld zu kennen.Ich sehe nicht wieso das mit einer VM so performant sein soll. Das Nachschlagen in einer Map ist nicht langsam und wie oben gezeigt muss man es ja nur einmal tun. Unter Java ist das ja auch nicht kostenlos, der Code muss neu überstetz werden und wenn man dann noch optimiert sind das zwar einmalige Kosten aber sehr große in der das gesamte Program einfriert. Oder willst du das ganze threadsafe machen? Also in dem einen Thread läuft der Compiler und in einem anderen wird auf Eingaben des Users reagiert.
Wenn du keine Annahmen machst, kannst du nichts optimieren. Völlig klar. Wenn ich in Java ne Klasse hinzulade und dann neu kompilieren kann, kann ich die neuen Annahmen nutzen. Ein paar Teile kann man sicher auch ohne JIT-Compiler implementieren, die du dann aber extrem flexibel halten musst. Und viele Teile gehen gar nicht, wie oben beschrieben.
Das mit den Threads ist überhaupt kein Problem (in C++ auch nicht, da gibt's das ja nicht). Dazu muss man nur wissen, wie der JIT-Compiler überhaupt aufgerufen wird. Das geht meistens über ein paar mark-Bits, ob die Funktion schon compiliert wurde, oder auch über nen Funktionszeiger, der dann nicht auf die Funktion zeigt, sondern auf eine Routine, die den JIT aufruft. Wenn das mehrere Threads machen, fangen sie sich halt alle darin und sind blockiert. Dass Threads blockiert sind, die gerade eine zu compilierende Funktion aufrufen, ist ja auch klar. Die anderen können aber weiterlaufen.
Wie auch immer, wenn ich ne Klasse hinzulade, muss ich eigentlich nur ein paar Bits/Zeiger zurücksetzen und sonst läuft alles weiter wie bisher. Sobald die Funktion wieder aufgerufen wird, ist sofort klar, dass sie neu compiliert werden muss. Die Kunst ist höchstens noch, zuverlässig die Funktionen zu bestimmen, die neu compiliert werden müssen. Beim maximalen Nerv-Faktor setz ich halt alle zurück.Allerdings sobald man auf Funktionen zurückgreifen will deren Existenz du während der Übersetzung noch nicht mal ansatzweise geahnt hat, dann hast du ein dickes Denkproblem. Sollten solche Machenschaften bei Java die Regel sein (wovon ich nicht ausgehe), dann fange ich an zu verstehen wieso soviele Leute Java hassen.
Nein, das ist einfach nur dein Denkproblem. Es gibt Sachen, die möchte man an manchen Stellen so flexibel halten und das bricht dir jetzt nur ohne JIT-Compilierung das Genick, weshalb du es nicht magst :p . Wozu sollte Reflection gut sein, wenn ich schon weiß, was das Teil für Methoden hat?
Natürlich wird man sich ab und zu was dabei denken. Man wird sagen, mich interessieren die setter und getter und die haben sich an eine bestimmte Namenskonvention zu halten. Das wird z.B. bei Beans und JSP-Custom-Tags so gehandhabt. Prinzipiell kann aber alles auf einen zukommen.
-
also ich hab den beitrag zwar nicht 100%ig verfolgt,
will aber trotzdem mal meinen senf dazu abgebenich programmiere viel mit c++ und c#. die reflection
ist zwar ohne zweifel ein maechtiges werkzeug, dennoch
sollte man das ganze nicht in den himmel loben.durch die reflection geht die ganze zur compilierzeit
moegliche typ sicherheit verloren.wenn du plugins laedts wuerde ich das ueber eine klassen
schnittstelle machen nicht ueber reflection.wird zuviel mit reflection gearbeitet verhaelt sich der code
eher wie eine typenlose skriptsprache wie javascript, php etc.
wo du erst zur ausfuerhrungszeit merkst, dass es methode xyz
fuer klasse abc gar nicht gibt.deshalb: reflection is ne super sache aber nur einsetzen,
wenn alle anderen methode nicht anwendbar sind.
-
Optimizer schrieb:
Und so lange du nicht anerkennst, dass du mit sowas einen halben Compiler in dein Programm einbaust, wirst du kein Problem damit haben.
Jeder virtuelle Aufruf kann das Verhalten von altem Code beeinflussen, sogar brechen, und wenn oft genug eingesetzt völlig verändern. Wenn du jeden virtuelle Aufruf als Minicompiler bezeichnest, und jeden Minicompiler als VM dann sagst du, dass selbst ASM einen VM hat da man ja mit Hilfe von Funktionspointer einen virtuellen Aufruf machen kann. Ich habe ja auch eine alternative, leicht imperformatere Lösung gegeben die nicht am Code rumschreiben geht.
Als Compiler bezeichne ich etwas das den bereits existirenden Code wegwirft und den Sourcecode (oder auch Bytecode) neu übersetzt. Das ist bei meinem Ansatz aber keineswegs der Fall.
Optimizer schrieb:
Ein "paar Tests" beinhaltet ja vermutlich auch das Prüfen auf mehrdeutige Aufrufe, insbesondere im Zusammenhang mit Mehrfachvererbung ebenso wie in Kombination mit Templates und anderen extrem komplizierten Tests, die auch die statischen Compiler noch nicht seit langem alle standardkonform durchführen.
Du kommst vom Thema ab, das sind alles Problem die sich von der Sprache C++ her ergeben und haben nichts mit einer VM zu tun. Allerdings werde ich trotzdem auf deine Aussage eingehen.
Mehrdeutige Aufrufe entstehen dadurch, dass der Type des übergebenem Werts nicht dem des Parameters entspricht und es mehere Verwandlungsmöglichkeiten gibt. Wenn jede Klasse virtuelle Verwandlungsoperatoren mit bring und jede Funktion die ihr Verhalten ändern kann nach allen Parametern dispatcht wird, ist das Problem gelöst. Natürlich wäre es besser sämtliche implicite Verwandlungen abzuschaffen.
Wenn du darauf hinaus willst, dass sich Klassenhierarchien verändern können und deshalb das Verhalten von dynamic_cast sich verändert, dann siehst du nicht was mit klassenweiten virtuellen Funktion möglich ist. Jede Klasse kriegt eine is_castable Funktion, diese baut dann auf der Funktion ihrer Eltern auf. Ändern die Eltern ändert sich das Verhalten. Das einzige was durchaus zum Problem werden könnte sind Crosscasts, aber da Java ja eh keine Mehrfachvererbung hat.
Optimizer schrieb:
Das hab ich ja nicht gesagt. Aber du kannst trotzdem nicht die VTable in die DLL flacken und dann dynamisch hinzuladen, weil die VTable in die Umgebung passen muss, in die sie hineingeladen wird.
Hier wäre es viellecht interessant mich selbst zu zitieren
ben04 schrieb:
Ich seh da nichts was übermässig schwer mit nativ Code zu machen wäre, natürlich nur mit ein wenig Unterstüzung des Compilers und kompatiblen ABIs.
Und, dass dies nicht der Fall ist, ist ein Vorteil von Java und nicht einer VM, wie ich auch bereits gesagt habe.
Nein, das ist einfach nur dein Denkproblem. Es gibt Sachen, die möchte man an manchen Stellen so flexibel halten und das bricht dir jetzt nur ohne JIT-Compilierung das Genick, weshalb du es nicht magst :p .
Na wenn du das sagst.
Irgenwie klingt mir das nach Machenschaften wie:
int foo1=3; int foo2=1; int foo3=4; int foo4=2; int a=2; int b; execute_code("b = foo%i", a);
Ich hab vor ein paar Jahren sehr oft mit einer interpretierten Sprache gearbeitet, die soetwas erlaubt hat. Meine Erinnerungen daran sind durchgehend schlecht und dabei denke ich noch nicht einmal an all die Sicherheitslücken die sich damit bei so einem Pluginsystem reihenweise auftun.
Desweitern ist dein Pluginsystem auch bei weitem nicht annähernt so flexibel wie du dir das vorstellst. Bedenke jede Klasse ist global zugänglich und somit hat
Base*a=new MyLoadedClass;
alle Probleme die mit globalen Variablen zusammenhängen. Du kannst bespielsweise nur ein Plugin zur gleichen Zeit laden. Was das für Probleme bei Pluginsystem wie zum Beispiel von Firefox mit sich bringt brauch ich ja wohl nicht zu erklären.
Wozu sollte Reflection gut sein, wenn ich schon weiß, was das Teil für Methoden hat?
Ja eben, darauf will ich hinaus. Und wenn du
MyLoadedClass*a = new MyLoadedClass; a->foo();
schreibst dann weist du ja, dass es die Klasse MyLoadedClass gibt und, dass sie eine Methode foo hat. Also wieso nicht gleich:
typedef Base*(*Func)(); Func create = (Func)GetProcAddress(..., "CreateMyLoadedClass"); Base*a = create(); a->foo();
Der Syntax ist vielleicht nicht schön aber der hat ja nichts mit einer VM zu tun. Allerdings ist die zweite Version flexibler als die erste. In der ersten kann es nur eine MyLoadedClass geben. In der Zweiten kann man problemlos mehrere DLLs laden und dann das jeweilige "CreateMyLoadedClass" laden.
Wie bereits gesagt mit solch einem allgemeinem Ansatz wirst du nicht glücklich werden.
-
Ben04 schrieb:
Optimizer schrieb:
Und so lange du nicht anerkennst, dass du mit sowas einen halben Compiler in dein Programm einbaust, wirst du kein Problem damit haben.
Jeder virtuelle Aufruf kann das Verhalten von altem Code beeinflussen, sogar brechen, und wenn oft genug eingesetzt völlig verändern. Wenn du jeden virtuelle Aufruf als Minicompiler bezeichnest, und jeden Minicompiler als VM dann sagst du, dass selbst ASM einen VM hat da man ja mit Hilfe von Funktionspointer einen virtuellen Aufruf machen kann. Ich habe ja auch eine alternative, leicht imperformatere Lösung gegeben die nicht am Code rumschreiben geht.
Als Compiler bezeichne ich etwas das den bereits existirenden Code wegwirft und den Sourcecode (oder auch Bytecode) neu übersetzt. Das ist bei meinem Ansatz aber keineswegs der Fall.
Doch! Muss ich jetzt zum dritten mal dieses Beispiel hier erwähnen?
int main() { Type type; ... // Klasse laden Type* a = new Type(5, 2); cout << *a; }
Hier ist überhaupt nichts mit dynamischer Bindung. N.I.C.H.T.S.
Type könnte nen op<< implementieren. Der ist schon mal global und wird mit Sicherheit nicht dynamisch gebunden. Type könnte aber auch keinen op<< implementieren, sich aber dafür implizit in was umwandeln lassen, was den op<< implementiert. Sense. Nix dynamische Bindung. Du musst die main-Funktion neu compilieren, wenn du nen anderen Typen hast. Es gibt keinen Weg drum herum.Wenn jede Klasse virtuelle Verwandlungsoperatoren mit bring [...]
*lol* in jeden Typ, auch die man dynamisch hinzulädt, oder?
Natürlich wäre es besser sämtliche implicite Verwandlungen abzuschaffen.
Das einzige was durchaus zum Problem werden könnte sind Crosscasts, aber da Java ja eh keine Mehrfachvererbung hat.
Deshalb meine Aussage, für C++ ist sowas mit JIT-Compiler schon schwer genug.
Optimizer schrieb:
Das hab ich ja nicht gesagt. Aber du kannst trotzdem nicht die VTable in die DLL flacken und dann dynamisch hinzuladen, weil die VTable in die Umgebung passen muss, in die sie hineingeladen wird.
Hier wäre es viellecht interessant mich selbst zu zitieren
ben04 schrieb:
Ich seh da nichts was übermässig schwer mit nativ Code zu machen wäre, natürlich nur mit ein wenig Unterstüzung des Compilers und kompatiblen ABIs.
Und, dass dies nicht der Fall ist, ist ein Vorteil von Java und nicht einer VM, wie ich auch bereits gesagt habe.
Hallo? Was hat das bitte damit zu tun? Ich sage:
Wenn du dynamisch Klassen hinzulädst, muss du die VTable zur Laufzeit erstellen. Begründung mit Beispiel im vorherigen Post. Da nützt dir irgendein zauberhafte kompatibles API gar nichts. Da nützt dir nur ein Compiler"-ähnliches" Werkzeug was.So lange du nicht auf diesen beiden Punkte mit ihren Beispielen konkret eingehst, nehme ich dir beim besten Willen nicht ab, dass dynamisches hinzuladen von Klassen und die damit verbundene Laufzeit-Reflection ohne JIT-Compiler möglich ist.
Irgenwie klingt mir das nach Machenschaften wie: [...]
Du kannst immer genügend Beispiele finden, wo sowas Nachteile hat. Tatsache ist: Es ist manchmal nützlich und es wird manchmal angewendet. Egal, ob du es magst. Ich möchte auch eigentlich gar nicht jetzt feststellen ob Reflection toll ist oder nicht. ^^ Topic
-
Optimizer schrieb:
Vergiss auch nicht, dass du durch das hinzuladen neuer Klassen, vorhandenen, eigentlich statischen Code ändern musst.
C/C++ Code:
class Fraction
{
// Implementiert nicht op<< aber ein op(double)
};int main()
{
Type type;
... // Klasse Fraction ladenType* a new Type(5, 2);
cout << *a; // Gibt ein double aus.
}
C/C++ Code:
class Fraction
{
// Implementiert nicht op<< aber ein op(double)
};int main()
{
Type type;
... // Klasse Fraction ladenType* a new Type(5, 2);
cout << *a; // Gibt ein double aus.
}
C/C++ Code:
class Fraction
{
// Implementiert nicht op<< aber ein op(double)
};int main()
{
Type type;
... // Klasse Fraction ladenType* a new Type(5, 2);
cout << *a; // Gibt ein double aus.
}Ich darf aber statt Fraction auch was anderes laden, oder?
Jetzt laden wir die Klasse Fraction2 hinzu, die den op<< aber implementiert.
Jetzt muss statt dem op(double) der op<< aufgerufen werden, der nicht mal Element der Klasse selber ist, sondern global.
Was wir jetzt haben ist keine dynamische Bindung, auch keine "extended super complicated dynamische Bindung", sondern völlig anderes Verhalten. Und das machst du mir ohne Compiler.Ne, ich bin ja eigentlich schon fast geneigt zu sagen, dass die Sprache C++ schon an sich fast zu kompliziert für solche Späße ist. Aber dann ohne JIT-Compiler noch... kann ich mir nicht vorstellen.
wenn ich eine DLL lade, brauch ich keine VM dafür und ich brauche auch nicht den statischen code zu ändern!
Es könnte doch in C++ genauso wie in Java realisiert werden:
//Diese Klasse ist in einer "CDLL (Class DLL)" //in einer Class DLL sind alle reflection infos im gleichen format wie in einer .class datei von java, nur im native code class MeineDynamischeKlasse { int x; public: MeineDynamischeKlasse(int a): x(a) {} int f(int b) { cout << "hallo " << x << b; } } public static void main() { //lade MeineDynamischeKlasse dynamisch nach // alle reflection infos mit den dazugehörigen adressen werden jetzt aus der CDLL geladen und in "dynamischeKlasse" gepackt. Class* dynamischeKlasse = Class::forName("MeineDynamischeKlasse"); //hole konstruktor mit dem parameter "int" Constructor* standardKonstruktor = dynamischeKlasse->getConstructor(Class::forName("int")): //hole die Methode "f" Method* fMethode = dynamischeKlasse->getMethod("f", Class::forName("int")); //erzeuge jetzt ein objekt vom typ MeineDynamischeKlasse und übergebe konstruktor die 10 Object* obj = standardKonstruktor->newInstance(10); //rufe die methode "f" auf. auf der konsole sollte jetzt "hallo 10 20" ausgegeben werden fMethode->invoke(obj, 20); }
das ist doch "Laufzeit-Reflection", oder? Wo muss man hier den bestehenden statischen Code ändern? Hier werden überall nur Zeiger bzw. Referenzen verwendet!
was du dort gepostet hast, mit dem << operator. sowas ist mit java reflection auch nicht möglich!
-
Wo muss man hier den bestehenden statischen Code ändern? Hier werden überall nur Zeiger bzw. Referenzen verwendet!
Ja, was ist hier? Dein Code jetzt? Super!
Und was ist mit meinem, legalen, standardkonformen C++ Code?was du dort gepostet hast, mit dem << operator. sowas ist mit java reflection auch nicht möglich!
Doch. Ich kann auch statische Funktionen (wie der operator<< eine wäre) mit Reflection getten und aufrufen.
-
Komisch, ich verstehe das Problem gar nicht. Ich sag ja nicht mal, dass man sowas unbedingt braucht. Ich sag auch nicht, dass es toll ist (auch wenn ich es trotztdem toll finden darf). Die Frage war doch nur "wofür braucht man ne VM", worauf ich sage, für JIT-Compilierung, außer ich bau nen Compiler in meine .exe ein, was natürlich Unsinn ist.
Das kann man doch akzeptieren, oder?
-
Optimizer schrieb:
Wo muss man hier den bestehenden statischen Code ändern? Hier werden überall nur Zeiger bzw. Referenzen verwendet!
Ja, was ist hier? Dein Code jetzt? Super!
Und was ist mit meinem, legalen, standardkonformen C++ Code?Dein Code sieht schön und einfach aus. Ich kann nachvollziehen, dass man dafür einen JIT braucht.
Allerdings ist dort die ganze Typprüfung futsch! Nicht mal in Java funktioniet das so einfach, und unsicher! Dort musst du dir alles schön selber über die Class besorgen! (siehe Beitrag oben)was du dort gepostet hast, mit dem << operator. sowas ist mit java reflection auch nicht möglich!
Doch. Ich kann auch statische Funktionen (wie der operator<< eine wäre) mit Reflection getten und aufrufen.[/quote]
globale Funktionen gibt es bei Java nicht.
BTW: es geht mir nicht darum, dass wir uns hier jetzt für C++ eine Reflection-API ausdenken. Mag seien, dass man für C++-reflection einen JIT benötigen würde. Will mir da auch echt kein Kopf drüber machen...
Meiner Meinung nach ist für Java-Reflection keine VM oder auch JIT notwendig.