Der erste Teil mit funktionalen Anforderungen ist
hier .
Behauptet als Programmiersprachen mit Blick auf Zuverlässigkeit.
Alphabetisch - Aktiv Oberon, Ada, BetterC, IEC 61131-3 ST, Safe-C.
Sofort ist der Haftungsausschluss (Entschuldigung) keineswegs eine Kampagne „Alles auf der linken Seite“, und die Überprüfung ist eher akademisch - die Sprache verfügt möglicherweise nicht nur über eine aktiv unterstützte moderne Entwicklungsumgebung, sondern sogar über einen Compiler für Ihre Plattform.
Auf der anderen Seite gibt es für die fraglichen Sprachen Open-Source-Compiler, und mit dem aktuellen Stand der Softwareentwicklung können Sie mit Interesse und nicht zu komplizierter Syntax einen persönlichen Compiler erstellen und mit Hintergrundbeleuchtung und Parser in eine Art Eclipse integrieren.
Als Indikator für die Klarheit der Sprache wählte ich die Umsetzung von Dijkstras berühmter Multithread-Aufgabe über Speisephilosophen. Die Umsetzung erfolgt in den Lehrbüchern zur Sprache und in den Foren, was meine Arbeit erleichtert hat - es bleibt nur noch Anpassung. Zum Beispiel enthält ein kürzlich veröffentlichter
Habr-Artikel über modernes C ++ eine Implementierung in C ++ 17 zum Vergleich.
Aktiver Oberon (2004)
Es wurde mit Blick auf die Erfahrungen von Pascal, Modula, früheren Oberons seit 1988, Java, C #, Ada sowie praktische Erfahrungen in der Anwendung erstellt. Es hat eine Implementierung in Form von
OS A2 , die als Laufzeit über * nix oder Windows fungieren kann.
Quellen A2 und der Compiler für den Link .
Es gibt auch ein
Oberon2 to C Compiler (OOC) -Projekt
, das nicht an die Oberon-Umgebung gebunden ist. Dies ist ein etwas anderer Dialekt, die Unterschiede werden unten beschrieben.
Das Hauptmerkmal von Oberon ist die außergewöhnliche Kürze der Spezifikation. Dies sind 16 Seiten auf der Basis von Oberon-2 plus 23 Seiten auf der Active-Erweiterung mit mehreren Threads.
Einfache und klare Syntax, die offensichtliche Fehler ausschließt.
Bezeichner unterscheiden zwischen Groß- und Kleinschreibung.
OOP mit Objekten auf dem Heap mit dem Garbage Collector (GC).
Es unterscheidet sich von seinen Vorgängern durch die bekanntere OOP-Syntax in Form von Instance.Method (früher Method (Instance)) und Unterstützung für Multithreading mit Synchronisationsprimitiven.
In der OOP-Implementierung gibt es kein dynamisches Dispatching, was leicht zu einer Situation führen kann. Sie haben vergessen, die Verarbeitung für einen neuen Typ hinzuzufügen.
Streams können Priorität zugewiesen werden und High / Realtime werden nicht durch GC unterbrochen. Zeichenfolgen in Form von UTF-8-Arrays.
Rantime (Oberon System) bietet interessante Möglichkeiten zum Neustart einer fehlgeschlagenen Prozedur / eines fehlgeschlagenen Moduls / Threads im Falle eines Laufzeitfehlers - Speicheradressierung oder beispielsweise ganzzahliger Überlauf.
Der Nachteil ist das Fehlen von RAII und die bequeme Fehlerbehandlung - alles über Rückkehrcodes, mit Ausnahme der folgenden Option.
Oberon-2 OOC
Es ist bequemer für Experimente, da Oberon kein Betriebssystem benötigt - es wird in ANSI C kompiliert und es gibt keine Interoperabilitätsprobleme. Unterschiede zur Active-Version - es gibt keine integrierte Multithreading-Sprache - stattdessen gibt es ein Modul für die Arbeit mit PThreads, aber UTF16, hierarchische Modularität und ein Systemmodul für die Arbeit mit Ausnahmen.
Modul 3
Es gibt auch einen Verwandten aus einem etwas anderen Entwicklungszweig in Form von Modula-3. Es wurde auf der Basis von Oberon im Gegensatz zur überentwickelten Ada geschaffen.
Die Implementierung ist hier .
Im Vergleich zu Active Oberon werden Generika und Ausnahmen hinzugefügt. Es gibt Bibliotheken für die praktische Arbeit mit Unicode, GUI und sogar Postgress. Vereinfachte Integration mit C. Andere Multithreading-Semantik. RAII als WITH (ähnlich wie in C #).
Aber es scheint, dass die Entwicklung von Modula 3 im Jahr 2010 gestoppt wurde.
Haftungsausschluss. Nachdem ich WinAOS gestartet hatte, stieß ich aus heiterem Himmel auf TRAPs (auch bekannt als Abort / Stacktrace oder Laufzeitfehler) - selbst der Task-Manager funktioniert nicht richtig, und obwohl das System / die Laufzeit nicht abstürzte -, sondern nur die Anwendung, hatte ich einen gewissen Zweifel, dass die Zuverlässigkeit von der Sprache bestimmt wird Programmierung = (
Außerdem ist AOC mit seinem Entwicklungsansatz ausreichend eigenständig.
Quelle für SpeisephilosophenMODULE Philo; IMPORT Semaphores := Example8, Out; CONST NofPhilo = 5; VAR fork: ARRAY NofPhilo OF Semaphores.Semaphore; i: LONGINT; TYPE Philosopher = OBJECT VAR first, second: LONGINT; PROCEDURE & Init(id: LONGINT); BEGIN IF id # NofPhilo-1 THEN first := id; second := (id+1) ELSE first := 0; second := NofPhilo-1 END END Init; PROCEDURE Think; BEGIN Out.Int(first); Out.String(".... Think...."); Out.Ln; END Think; PROCEDURE Eat; BEGIN Out.Int(first); Out.String(".... Eat...."); Out.Ln; END Eat; BEGIN LOOP Think; fork[first].P; fork[second].P; Eat; fork[first].V; fork[second].V END END Philosopher; VAR philo: ARRAY NofPhilo OF Philosopher; BEGIN FOR i := 0 TO NofPhilo DO NEW(fork[i], INTEGER(i)); NEW(philo[i], i); END; END Philo. Philo.Philo1 ~
Ada (1980, letzter gültiger Standard 2016)
Eigentlich gibt es auf den ersten Blick alles, was ich möchte.
Und noch ein bisschen mehr - es gibt Zahlen mit exakten Gleitkommaberechnungen. Beispielsweise gibt es einen Echtzeit-Thread-Scheduler, einen Cross-Thread-Austausch und eine formal überprüfte Teilmenge der SPARK-Sprache. Und vieles mehr.
Ich denke, wenn Adas Zuverlässigkeit eine verdammt gehörnte braucht, wird sie mit Anweisungen zum Anrufen in einer schwierigen Situation versehen =)
Implementierung -
GNUTaya Ada entwickelt, ISO / IEC-standardisiert.
Der Standard bietet die Implementierung mit GC, wird jedoch für kompilierte Optionen häufig nicht implementiert. Manuelle Speicherverwaltung ist erforderlich - und hier sind Programmiererfehler möglich. Die Sprache ist jedoch auf die Verwendung des Standardstapels ausgerichtet, und es gibt das Konzept verwalteter Typen mit Destruktoren. Sie können auch Ihre GC-Implementierung, automatische Freigabe oder Referenzzählung für jeden Datentyp definieren.
Das Ada-Referenzhandbuch 2012 enthält 950 Seiten.
Adas Nachteil ist neben der Komplexität die übermäßige Ausführlichkeit, die jedoch aus Gründen der Lesbarkeit konzipiert wurde. Aufgrund der Spezifität des Sprachsicherheitsmodells ist die Integration in fremde Bibliotheken schwierig.
Die
Ada-ru-Site enthält einen guten Artikel zur Rezension - den ersten Link.
Quelle für Speisephilosophen BetterC (Dlang-Teilmenge 2017, Original D - 2001, D 2.0 - 2007)
Die modernste Umsetzung der betrachteten. Die vollständige Beschreibung der Sprache ist ziemlich lang - 649 Seiten -
siehe die ursprüngliche Website .
Eigentlich ist dies die D-Sprache, jedoch mit Einschränkungen beim Schalter -betterC. Warum so ?!
Weil die Standardbibliothek D Phobos ist, die von Alexandrescu entwickelt wurde und sich als sehr gerissen herausstellte und vollständig auf Vorlagen basiert. Der Schlüssel zu diesem Thema ist, dass Phobos hinsichtlich des Speicherverbrauchs nicht kontrollierbar ist.
Die wichtigsten Dinge, die im BetterC-Modus verloren gehen, sind Multithreading, GC, Strings, Klassen (Strukturen bleiben - sie sind in der Funktionalität eng - nur auf dem Stack) und Ausnahmen (RAII und try-finally bleiben).
Es ist jedoch möglich, einen Teil des Programms in vollständigem D und den kritischen Teil in D-BetterC zu schreiben. Es gibt auch eine Systemattributfunktion, um die Nichtverwendung gefährlicher Effekte zu steuern: pure
safe @nogc.
Begründung des Regimes durch den Schöpfer der Sprache.
Und
dann der Squeeze - was abgeschnitten wird und was noch verfügbar ist.
Zeichenfolgen sind in Phobos enthalten - und Versuche, sie in BetterC zu verwenden, führen zu höllischen Fehlern bei der Instanziierung von Vorlagen bei elementaren Operationen wie der Ausgabe einer Zeichenfolge an die Konsole oder der Verkettung. Und im Voll-D-Modus sind auch die Zeilen auf dem Heap unveränderlich, daher führen Operationen mit ihnen zu Speicherunordnung.
Ich musste mich mehrmals über Fehler im Compiler beschweren. Was jedoch für eine Sprache, die in ihrer Komplexität mit C ++ konkurriert, nicht überraschend ist. Bei der Vorbereitung des Artikels musste ich auch auf 4 Fehler stoßen - zwei traten beim Versuch auf, dlangide mit einem neuen Compiler zu erstellen, und einige beim Portieren des Philosophenproblems (z. B. Absturz bei Verwendung von Beginthreadex).
Der Modus wurde erst kürzlich angezeigt und Fehler, die durch die Einschränkung des BetterC-Modus verursacht wurden, werden bereits in der Verknüpfungsphase ausgegeben. Um im Voraus zu erfahren, welche Merkmale der Sprache genau zugeschnitten sind, muss dies häufig aus erster Hand geschehen.
Quelle für Speisephilosophen Zum Vergleich ist die Quelle voll D.Auf der Rosette sehen Sie auch Optionen für andere Sprachen.
IEC 61131-3 ST (1993, neueste Norm 2013)
Eine Nischenprogrammiersprache für Mikrocontroller. Der Standard beinhaltet 5 Programmieroptionen, aber das Schreiben einer Anwendung, beispielsweise in Kontaktplanlogik, ist immer noch ein Abenteuer. Daher konzentrieren wir uns auf eine Option - strukturierten Text.
Der Text der Norm GOST R IEC 61131-3-2016 - 230 Seiten.
Es gibt Implementierungen für PC / x86 und ARM - und für kommerzielle
Zwecke. Die bekannteste davon ist
CODESYS (oft auch mit unterschiedlichen Namen lizenziert) und
Open-Beremiz- Broadcast über C.
Da es eine Integration mit C gibt, ist es durchaus möglich, die für die angewandte Programmierung erforderlichen Bibliotheken zu verbinden. Andererseits wird in diesem Bereich akzeptiert, dass die Logik separat rotiert und nur als Datenserver für ein anderes Programm oder System dient - eine Schnittstelle mit einem Operator oder einem DBMS, das bereits auf irgendetwas geschrieben werden kann - ohne Echtzeitanforderungen oder sogar vorübergehend im Allgemeinen ...
Multithread-Programmierung für ein Anwenderprogramm ist vor relativ kurzer Zeit erschienen - in Mikrocontrollern war dies vorher nicht erforderlich.
Typguss ist meist nur explizit (entspannt im neuesten Standard). Die Überlaufkontrolle ist jedoch implementierungsabhängig.
In der neuesten Ausgabe des Standards erschien OOP. Die Fehlerbehandlung wird von benutzerdefinierten Interrupt-Handlern durchgeführt.
Wir können sagen, dass es keine dynamische Speicherzuordnung für den Benutzer gibt. Dies ist historisch geschehen - die vom Mikrocontroller verarbeitete Datenmenge ist von oben immer konstant begrenzt.
Quelle (nicht verifiziert) Philo_2: Philosopher; Philo_3: Philosopher; Philo_4: Philosopher; Philo_5: Philosopher; END_VAR RESOURCE Station_1 ON CPU_1 TASK Task_1 (INTERVAL := T#100MS, PRIORITY := 1); TASK Task_2 (INTERVAL := T#100MS, PRIORITY := 1); TASK Task_3 (INTERVAL := T#100MS, PRIORITY := 1); TASK Task_4 (INTERVAL := T#100MS, PRIORITY := 1); TASK Task_5 (INTERVAL := T#100MS, PRIORITY := 1); PROGRAM Life_1 WITH Task_1: Philo_1(Name := 'Kant', 0, 1, Forks); PROGRAM Life2 WITH Task_2: Philo_2(Name := 'Aristotel', 1, 2, Forks); PROGRAM Life3 WITH Task_3: Philo_3(Name := 'Spinoza', 2, 3, Forks); PROGRAM Life4 WITH Task_4: Philo_4(Name := 'Marx', 3, 4, Forks); PROGRAM Life5 WITH Task_5: Philo_5(Name := 'Russel', 4, 0, Forks); END_RESOURCE END_CONFIGURATION FUNCTION_BLOCK Philosopher; USING SysCpuHandling.library; VAR_INPUT Name: STRING; Left: UINT; Right: UINT; END_VAR VAR_IN_OUT Forks: USINT; END_VAR VAR Thinking: BOOL := TRUE; Hungry: BOOL; Eating: BOOL; HaveLeftFork: BOOL; TmThink: TON; TmEating: TON; END_VAR TmThink(In := Thinking; PT := T#3s); TmEating(In := Eating; PT := T#5s); IF Thinking THEN Thinking := NOT TmThink.Q; Hungry := TmThink.Q; ELSIF Hungry IF HaveLeftFork IF SysCpuTestAndSetBit(Address := Forks, Len := 1, iBit := Right, bSet := 1) = ERR_OK THEN Hungry := FALSE; Eating := TRUE; ELSE RETURN; END_IF ELSIF IF SysCpuTestAndSetBit(Address := Forks, Len := 1, iBit := Left, bSet := 1) = ERR_OK THEN HaveLeftFork := TRUE; ELSE RETURN; END_IF END_IF ELSIF Eating IF TmEating.Q THEN Thinking := TRUE; Eating := FALSE; HaveLeftFork := FALSE; SysCpuTestAndSetBit(Address := Forks, Len := 1, iBit := Right, bSet := 0); SysCpuTestAndSetBit(Address := Forks, Len := 1, iBit := Left, bSet := 0); END_IF END_IF END_FUNCTION_BLOCK
Safe-C (2011)
Experimentelles C mit der Entfernung gefährlicher Chips und der Hinzufügung von Modularität und Multithreading.
ProjektseiteBeschreibung von ca. 103 Seiten. Wenn Sie die Unterschiede zu C hervorheben -
sehr wenig, ungefähr 10 .
Das Arbeiten mit Arrays und Zeigern ist ein sicherer, dynamischer Speicher mit automatischer Referenzzählung - mit doppelten Freigabeprüfungen und baumelnden Links.
Die Standardbibliothek verfügt über einen minimalen Satz von Funktionen für die GUI, Multithreading und Netzwerkfunktionen (einschließlich eines http-Servers).
Aber - diese Implementierung ist nur für Windows x86. Obwohl der Compiler- und Bibliothekscode geöffnet ist.
Im Rahmen einer anderen Forschungsaufgabe habe ich ein Webserver-Layout zusammengestellt, das Daten von IoT-Sensoren sammelt: ein 75-KB-Executive-Modul und einen Teilspeichersatz von <1 MB.
Quelle für Speisephilosophen from std use console, thread, random; enum philos (ushort) { Aristotle, Kant, Spinoza, Marx, Russell, }; const int cycles = 10; const ushort NUM = 5; uint lived = NUM; packed struct philosopher // 32-bit { philos name; byte left, right; } philosopher philo_body[NUM]; SHARED_OBJECT forks[NUM]; void philosopher_life(philosopher philo) { int age; for (age = 0; age++ < cycles; ) { printf("%s is thinking\n", philo.name'string); delay((uint)rnd(1, 100)); printf("%s is hungry\n", philo.name'string); enter_shared_object(ref forks[philo.left]); enter_shared_object(ref forks[philo.right]); printf("%s is eating\n", philo.name'string); delay((uint)rnd(1, 100)); leave_shared_object(ref forks[philo.right]); leave_shared_object(ref forks[philo.left]); } printf("%s is leaving\n", philo.name'string); InterlockedExchange(ref lived, lived-1); } void main() { philos i; assert philosopher'size == 4; philo_body[0] = {Aristotle, 0, 1}; philo_body[1] = {Kant, 1, 2}; philo_body[2] = {Spinoza, 2, 3}; philo_body[3] = {Marx, 3, 4}; philo_body[4] = {Russell, 0, 4}; for (i = philos'first; i <= philos'last; i++) { assert run philosopher_life(philo_body[(uint)i]) == 0; } while (lived > 0) sleep 0;
Schließlich - eine
zusammenfassende Tabelle zur Einhaltung der funktionalen Anforderungen.
Sicherlich habe ich etwas verpasst oder falsch interpretiert - also korrigieren Sie es.
Quellen aus dem Artikel über Github .