D (Programmiersprache) - D (programming language)
Paradigma | Multiparadigma : funktional , zwingend , objektorientiert |
---|---|
Entworfen von | Walter Bright , Andrei Alexandrescu (seit 2007) |
Entwickler | D Sprachstiftung |
Erstmals erschienen | 8. Dezember 2001 |
Stabile Version | |
Schreibdisziplin | Abgeleitet , statisch , stark |
Betriebssystem | FreeBSD , Linux , macOS , Windows |
Lizenz | Schub |
Dateinamenerweiterungen | .D |
Webseite | dlang |
Wichtige Implementierungen | |
DMD ( Referenzimplementierung ), GCC ,
GDC , LDC , SDC | |
Beeinflusst von | |
C , C++ , C# , Eiffel , Java , Python | |
Beeinflusst | |
Genie , MiniD, Qore , Swift , Vala , C++11 , C++14 , C++17 , C++20 , Go , C# und andere. | |
|
D , auch bekannt als Dlang , ist ein Multi-Paradigma System Programmiersprache erstellt von Walter Bright an Digital Mars und im Jahr 2001 veröffentlicht Andrei Alexandrescu trat das Design und Entwicklungsaufwand im Jahr 2007. Obwohl es als Reengineering entsteht C ++ , D ist eine eigene Sprache. Es hat einige Kernfunktionen von C++ neu gestaltet und gleichzeitig Merkmale anderer Sprachen, insbesondere Java , Python , Ruby , C # und Eiffel , gemeinsam genutzt .
Die Designziele der Sprache versuchten , die Leistung und Sicherheit kompilierter Sprachen mit der Ausdruckskraft moderner dynamischer Sprachen zu kombinieren . Idiomatischer D-Code ist im Allgemeinen so schnell wie äquivalenter C++-Code, aber auch kürzer. Die Sprache als Ganzes ist nicht speichersicher , enthält jedoch optionale Attribute zur Überprüfung der Speichersicherheit.
Typinferenz , automatische Speicherverwaltung und syntaktischen Zucker für gängige Typen schneller erlauben Entwicklung , während der Überprüfung der Grenzen , Design by Contract Funktionen und eine Gleichzeitigkeit -Aware Typ - System Hilfe verringert das Auftreten von Bugs .
Merkmale
D wurde mit Erkenntnissen aus der praktischen C++-Nutzung entwickelt und nicht aus einer rein theoretischen Perspektive. Obwohl die Sprache viele C- und C++-Konzepte verwendet, verwirft sie auch einige oder verwendet andere Ansätze (und Syntax), um einige Ziele zu erreichen. Als solches ist es nicht quellkompatibel (und beabsichtigt es auch nicht) mit C- und C++-Quellcode im Allgemeinen (einige einfachere Codebasen aus diesen Sprachen könnten mit Glück mit D funktionieren oder eine Portierung erfordern ). D wurde jedoch in seinem Design durch die Regel eingeschränkt, dass sich jeder Code, der sowohl in C als auch in D legal war, gleich verhalten sollte.
D hat einige Funktionen vor C++ erhalten, wie zum Beispiel Closures , anonyme Funktionen , Funktionsausführung zur Kompilierzeit , Bereiche, integrierte Containeriterationskonzepte und Typinferenz . D erweitert die Funktionalität von C++ durch die Implementierung von Design by Contract , Unit Testing , True Modules , Garbage Collection , First Class Arrays , Assoziative Arrays , Dynamic Arrays , Array Slicing , Nested Functions , Lazy Evaluation , Scoped (deferred) Codeausführung und eine überarbeitete Vorlagensyntax . Die C++- Mehrfachvererbung wurde durch die Einzelvererbung im Java-Stil mit Schnittstellen und Mixins ersetzt . Auf der anderen Seite, D's Erklärung, Statement und Ausdruck Syntax entspricht genau die von C ++.
D behält die Fähigkeit von C++, Low-Level-Programmierung durchzuführen, einschließlich Inline-Assembler , der die Unterschiede zwischen D und Anwendungssprachen wie Java und C# verkörpert . Mit dem Inline-Assembler können Programmierer maschinenspezifischen Assemblercode in Standard-D-Code eingeben, eine Methode, die von Systemprogrammierern verwendet wird, um auf die Low-Level-Funktionen des Prozessors zuzugreifen, die zum Ausführen von Programmen erforderlich sind, die direkt mit der zugrunde liegenden Hardware verbunden sind , wie Betriebssysteme und Gerätetreiber , sowie das Schreiben von Hochleistungscode (dh unter Verwendung von Vektorerweiterungen, SIMD ), der vom Compiler schwer automatisch generiert werden kann.
D unterstützt standardmäßig das Überladen von Funktionen und Operatoren sowie dynamische Arrays und assoziative Arrays. Symbole (Funktionen, Variablen, Klassen) können in beliebiger Reihenfolge deklariert werden - Vorwärtsdeklarationen sind nicht erforderlich. In ähnlicher Weise können Importe in fast beliebiger Reihenfolge durchgeführt werden und sogar bereichsbezogen sein (dh nur ein Modul oder einen Teil davon in eine Funktion, Klasse oder einen Unittest importieren). D verfügt über eine integrierte Unterstützung für Dokumentationskommentare, die eine automatische Dokumentationsgenerierung ermöglichen .
Programmierparadigmen
D unterstützt fünf Hauptprogrammierparadigmen :
Imperativ
Imperative Programmierung in D ist fast identisch mit der in C. Funktionen, Daten, Anweisungen, Deklarationen und Ausdrücke funktionieren genauso wie in C, und auf die C-Laufzeitbibliothek kann direkt zugegriffen werden. Auf der anderen Seite sind einige bemerkenswerte Unterschiede zwischen D und C im Bereich der imperativen Programmierung das foreach
Schleifenkonstrukt von D , das das Schleifen über eine Sammlung ermöglicht, und verschachtelte Funktionen , die Funktionen sind, die in einer anderen deklariert sind und auf die lokalen Variablen der einschließenden Funktion zugreifen können .
import std.stdio;
void main() {
int multiplier = 10;
int scaled(int x) { return x * multiplier; }
foreach (i; 0 .. 10) {
writefln("Hello, world %d! scaled = %d", i, scaled(i));
}
}
Objektorientierten
Die objektorientierte Programmierung in D basiert auf einer einzigen Vererbungshierarchie, wobei alle Klassen von der Klasse Object abgeleitet sind. D unterstützt keine Mehrfachvererbung; Stattdessen verwendet es Interfaces im Java-Stil , die mit den rein abstrakten Klassen von C++ vergleichbar sind, und mixins , das die allgemeine Funktionalität von der Vererbungshierarchie trennt. D erlaubt auch die Definition von statischen und finalen (nicht virtuellen) Methoden in Interfaces.
Schnittstellen und Vererbung in D unterstützen kovariante Typen für Rückgabetypen überschriebener Methoden.
D unterstützt die Typweiterleitung sowie optional benutzerdefinierten dynamischen Versand .
Klassen (und Interfaces) in D können Invarianten enthalten, die gemäß der Design-by-Contract- Methodik vor und nach dem Eintritt in öffentliche Methoden automatisch geprüft werden .
Viele Aspekte von Klassen (und Strukturen) können zur Kompilierzeit (eine Form der Reflexion mit ) und zur Laufzeit (RTII / ) automatisch introspektiert werden , um generischen Code oder die automatische Codegenerierung (normalerweise mit Techniken zur Kompilierzeit) zu erleichtern.
type traits
TypeInfo
Funktional
D unterstützt funktionale Programmierfunktionen wie Funktionsliterale , Closures , rekursiv unveränderliche Objekte und die Verwendung von Funktionen höherer Ordnung . Es gibt zwei Syntaxen für anonyme Funktionen, einschließlich einer Mehrfachanweisungsform und einer "Kurzschrift"-Einzelausdrucksnotation:
int function(int) g;
g = (x) { return x * x; }; // longhand
g = (x) => x * x; // shorthand
Es gibt zwei eingebaute Typen für Funktionsliterale, function
, das einfach ein Zeiger auf eine Stack-zugewiesene Funktion ist, und delegate
, das auch einen Zeiger auf die Umgebung enthält. Typrückschlüsse können mit einer anonymen Funktion verwendet werden. In diesem Fall erstellt der Compiler eine, es delegate
sei denn, er kann beweisen, dass ein Umgebungszeiger nicht erforderlich ist. Um eine Closure zu implementieren, platziert der Compiler ebenfalls eingeschlossene lokale Variablen nur bei Bedarf auf dem Heap (z. B. wenn eine Closure von einer anderen Funktion zurückgegeben wird und der Gültigkeitsbereich dieser Funktion verlässt). Bei der Verwendung von Typrückschlüssen fügt der Compiler auch Attribute wie pure
und nothrow
zum Typ einer Funktion hinzu, wenn er beweisen kann, dass sie zutreffen.
Weitere Funktionsmerkmale wie Currying und allgemeine Funktionen höherer Ordnung wie Map , Filter und Reduce sind über die Standardbibliotheksmodule std.functional
und verfügbar std.algorithm
.
import std.stdio, std.algorithm, std.range;
void main()
{
int[] a1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
int[] a2 = [6, 7, 8, 9];
// must be immutable to allow access from inside a pure function
immutable pivot = 5;
int mySum(int a, int b) pure nothrow // pure function
{
if (b <= pivot) // ref to enclosing-scope
return a + b;
else
return a;
}
// passing a delegate (closure)
auto result = reduce!mySum(chain(a1, a2));
writeln("Result: ", result); // Result: 15
// passing a delegate literal
result = reduce!((a, b) => (b <= pivot) ? a + b : a)(chain(a1, a2));
writeln("Result: ", result); // Result: 15
}
Alternativ können die obigen Funktionszusammensetzungen mit der Uniform Function Call Syntax (UFCS) für ein natürlicheres Lesen von links nach rechts ausgedrückt werden :
auto result = a1.chain(a2).reduce!mySum();
writeln("Result: ", result);
result = a1.chain(a2).reduce!((a, b) => (b <= pivot) ? a + b : a)();
writeln("Result: ", result);
Parallelität
Parallele Programmierkonzepte sind in der Bibliothek implementiert und erfordern keine zusätzliche Unterstützung durch den Compiler. Das D-Typ-System und der Compiler stellen jedoch sicher, dass die gemeinsame Nutzung von Daten erkannt und transparent verwaltet werden kann.
import std.stdio : writeln;
import std.range : iota;
import std.parallelism : parallel;
void main()
{
foreach (i; iota(11).parallel) {
// The body of the foreach loop is executed in parallel for each i
writeln("processing ", i);
}
}
iota(11).parallel
entspricht der std.parallelism.parallel(iota(11))
Verwendung von UFCS.
Das gleiche Modul unterstützt auch taskPool
die dynamische Erstellung paralleler Aufgaben sowie Map-Filter-Reduce- und Fold-Style-Operationen für Bereiche (und Arrays), was in Kombination mit funktionalen Operationen nützlich ist. std.algorithm.map
gibt einen verzögert ausgewerteten Bereich anstelle eines Arrays zurück. Auf diese Weise werden die Elemente von jedem Worker-Task parallel automatisch berechnet.
import std.stdio : writeln;
import std.algorithm : map;
import std.range : iota;
import std.parallelism : taskPool;
/* On Intel i7-3930X and gdc 9.3.0:
* 5140ms using std.algorithm.reduce
* 888ms using std.parallelism.taskPool.reduce
*
* On AMD Threadripper 2950X, and gdc 9.3.0:
* 2864ms using std.algorithm.reduce
* 95ms using std.parallelism.taskPool.reduce
*/
void main()
{
auto nums = iota(1.0, 1_000_000_000.0);
auto x = taskPool.reduce!"a + b"(
0.0, map!"1.0 / (a * a)"(nums)
);
writeln("Sum: ", x);
}
Gleichzeitigkeit
Parallelität ist vollständig in der Bibliothek implementiert und erfordert keine Unterstützung durch den Compiler. Alternative Implementierungen und Methodiken zum Schreiben von gleichzeitigem Code sind möglich. Die Verwendung des D-Typisierungssystems trägt zur Gewährleistung der Speichersicherheit bei.
import std.stdio, std.concurrency, std.variant;
void foo()
{
bool cont = true;
while (cont)
{
receive( // Delegates are used to match the message type.
(int msg) => writeln("int received: ", msg),
(Tid sender) { cont = false; sender.send(-1); },
(Variant v) => writeln("huh?") // Variant matches any type
);
}
}
void main()
{
auto tid = spawn(&foo); // spawn a new thread running foo()
foreach (i; 0 .. 10)
tid.send(i); // send some integers
tid.send(1.0f); // send a float
tid.send("hello"); // send a string
tid.send(thisTid); // send a struct (Tid)
receive((int x) => writeln("Main thread received message: ", x));
}
Metaprogrammierung
Die Metaprogrammierung wird durch Vorlagen, Funktionsausführung zur Kompilierzeit, Tupel und String-Mixins unterstützt. Die folgenden Beispiele veranschaulichen einige der Kompilierzeitfunktionen von D.
Vorlagen in D können im Vergleich zum funktionalen C++-Stil für Vorlagen in einem zwingenderen Stil geschrieben werden. Dies ist eine reguläre Funktion, die die Fakultät einer Zahl berechnet :
ulong factorial(ulong n) {
if (n < 2)
return 1;
else
return n * factorial(n-1);
}
Hier wird die Verwendung von static if
, dem bedingten Konstrukt zur Kompilierzeit, demonstriert, um eine Vorlage zu erstellen, die dieselbe Berechnung mit Code durchführt, der dem der obigen Funktion ähnlich ist:
template Factorial(ulong n) {
static if (n < 2)
enum Factorial = 1;
else
enum Factorial = n * Factorial!(n-1);
}
In den folgenden zwei Beispielen werden die oben definierte Vorlage und Funktion verwendet, um Fakultäten zu berechnen. Die Typen der Konstanten müssen nicht explizit angegeben werden, da der Compiler ihre Typen von der rechten Seite der Zuweisungen ableitet :
enum fact_7 = Factorial!(7);
Dies ist ein Beispiel für die Ausführung von Funktionen zur Kompilierzeit (CTFE). Gewöhnliche Funktionen können in konstanten Ausdrücken zur Kompilierzeit verwendet werden, sofern sie bestimmte Kriterien erfüllen:
enum fact_9 = factorial(9);
Die std.string.format
Funktion führt eine printf
-ähnliche Datenformatierung durch (auch zur Kompilierzeit, über CTFE), und das Pragma "msg" zeigt das Ergebnis zur Kompilierzeit an:
import std.string : format;
pragma(msg, format("7! = %s", fact_7));
pragma(msg, format("9! = %s", fact_9));
String-Mixins, kombiniert mit der Ausführung von Funktionen zur Kompilierzeit, ermöglichen die Generierung von D-Code mithilfe von String-Operationen zur Kompilierzeit. Dies kann verwendet werden, um domänenspezifische Sprachen zu analysieren , die als Teil des Programms kompiliert werden:
import FooToD; // hypothetical module which contains a function that parses Foo source code
// and returns equivalent D code
void main() {
mixin(fooToD(import("example.foo")));
}
Speicherverwaltung
Der Speicher wird normalerweise mit Garbage Collection verwaltet , aber bestimmte Objekte können sofort abgeschlossen werden, wenn sie den Gültigkeitsbereich verlassen. Dies wird von den meisten in D geschriebenen Programmen und Bibliotheken verwendet.
Für den Fall , mehr Kontrolle über Speicherlayout und eine bessere Leistung erforderlich ist, explizite Speicherverwaltung ist möglich , die Verwendung von überladenen Operatoren new
und delete
, durch den Aufruf von C ‚s malloc und kostenlos direkt oder benutzerdefinierten allocator Systemen Implementierung (dh auf Stapel mit Rückfall, RAII Stil Zuordnung, Referenzzählung, gemeinsame Referenzzählung). Die Garbage Collection kann gesteuert werden: Programmierer können Speicherbereiche hinzufügen und aus der Überwachung durch den Collector ausschließen, den Collector deaktivieren und aktivieren und entweder einen Generations- oder einen vollständigen Sammelzyklus erzwingen. Das Handbuch enthält viele Beispiele für die Implementierung verschiedener hochoptimierter Speicherverwaltungsschemata für den Fall, dass die Garbage Collection in einem Programm unzureichend ist.
In Funktionen structs
werden standardmäßig auf dem Stack zugewiesen, während sie classes
standardmäßig auf dem Heap zugewiesen werden (wobei nur auf die Klasseninstanz auf dem Stack verwiesen wird). Dies kann jedoch für Klassen geändert werden, z. B. mithilfe der Standardbibliotheksvorlage std.typecons.scoped
, oder indem Sie new
for structs verwenden und einem Zeiger anstelle einer wertbasierten Variablen zuweisen.
In der Funktion werden statische Arrays (von bekannter Größe) auf dem Stack zugewiesen. Für dynamische Arrays kann man core.stdc.stdlib.alloca
function (ähnlich wie C function alloca
, um Speicher auf dem Stack zuzuweisen. Der zurückgegebene Zeiger kann in ein (typisiertes) dynamisches Array verwendet (recast) werden, mittels eines Slice (jedoch muss die Größenänderung des Arrays, einschließlich des Anhängens, sein vermieden werden; und aus offensichtlichen Gründen dürfen sie nicht von der Funktion zurückgegeben werden).
Ein scope
Schlüsselwort kann sowohl zum Annotieren von Codeteilen als auch zum Annotieren von Variablen und Klassen/Strukturen verwendet werden, um anzuzeigen, dass sie sofort beim Verlassen des Gültigkeitsbereichs zerstört (Destruktor aufgerufen) werden sollen. Was auch immer der Speicher freigegeben wird, hängt auch von der Implementierung und den Unterschieden zwischen Klasse und Struktur ab.
std.experimental.allocator
enthält modulare und zusammensetzbare Zuweisungsvorlagen, um benutzerdefinierte Hochleistungszuweisungen für spezielle Anwendungsfälle zu erstellen.
SafeD
SafeD ist der Name der Teilmenge von D, die garantiert speichersicher ist (keine Schreibvorgänge in Speicher, der nicht zugewiesen oder recycelt wurde). Markierte Funktionen @safe
werden zur Kompilierzeit überprüft, um sicherzustellen, dass sie keine Funktionen verwenden, die zu einer Beschädigung des Speichers führen könnten, wie z. B. Zeigerarithmetik und ungeprüfte Umwandlungen, und alle anderen aufgerufenen Funktionen müssen ebenfalls als @safe
oder markiert werden @trusted
. Funktionen können @trusted
für Fälle markiert werden, in denen der Compiler nicht zwischen der sicheren Verwendung einer in SafeD deaktivierten Funktion und einem möglichen Fall einer Speicherbeschädigung unterscheiden kann.
Lebenslange Sicherheit des Umfangs
Anfänglich unter den Bannern von DIP1000 und DIP25 (jetzt Teil der Sprachspezifikation) bietet D Schutz gegen bestimmte schlecht geformte Konstruktionen, die die Lebensdauer von Daten betreffen.
Die gegenwärtigen Mechanismen befassen sich hauptsächlich mit Funktionsparametern und Stapelspeicher, jedoch ist es ein erklärtes Ziel der Führerschaft der Programmiersprache, eine gründlichere Behandlung der Lebensdauern innerhalb der Programmiersprache D bereitzustellen. (Beeinflusst von Ideen aus der Programmiersprache Rust ).
Lebenslange Sicherheit von Aufgaben
Innerhalb von @safe-Code wird die Lebensdauer einer Zuweisung mit einem Referenztyp überprüft, um sicherzustellen, dass die Lebensdauer des Zugeordneten länger ist als die des Zugeordneten.
Zum Beispiel:
@safe void test()
{
int tmp = 0; // #1
int* rad; // #2
rad = &tmp; // If the order of the declarations of #1 and #2 is reversed, this fails.
{
int bad = 45; // Lifetime of "bad" only extends to the scope in which it is defined.
*rad = bad; // This is kosher.
rad = &bad; // Lifetime of rad longer than bad, hence this is not kosher at all.
}
}
Anmerkungen zur Lebensdauer von Funktionsparametern im @safe-Code
Bei Anwendung auf Funktionsparameter, die entweder vom Zeigertyp oder Referenzen sind, beschränken die Schlüsselwörter return und scope die Lebensdauer und Verwendung dieses Parameters.
Der Standard schreibt folgendes Verhalten vor:
Lagerklasse | Verhalten (und Einschränkungen) eines Parameters mit der Speicherklasse |
---|---|
Umfang | Verweise im Parameter können nicht maskiert werden. Ignoriert für Parameter ohne Referenzen |
Rückkehr | Parameter kann zurückgegeben oder in den ersten Parameter kopiert werden, aber ansonsten nicht aus der Funktion entkommen. Solche Kopien sind erforderlich, um die Argumente, aus denen sie abgeleitet wurden, nicht zu überleben. Ignoriert für Parameter ohne Referenzen |
Ein kommentiertes Beispiel ist unten angegeben.
@safe:
int* gp;
void thorin(scope int*);
void gloin(int*);
int* balin(return scope int* p, scope int* q, int* r)
{
gp = p; // error, p escapes to global gp
gp = q; // error, q escapes to global gp
gp = r; // ok
thorin(p); // ok, p does not escape thorin()
thorin(q); // ok
thorin(r); // ok
gloin(p); // error, gloin() escapes p
gloin(q); // error, gloin() escapes q
gloin(r); // ok that gloin() escapes r
return p; // ok
return q; // error, cannot return 'scope' q
return r; // ok
}
Interaktion mit anderen Systemen
C ‚s Application Binary Interface (ABI) unterstützt wird , sowie alle der grundlegenden C und abgeleitete Typen, die direkten Zugang zu C - Code und Bibliotheken existieren. D- Bindungen sind für viele gängige C-Bibliotheken verfügbar. Zusätzlich C-Standardbibliothek ist Teil des Standard D.
Unter Microsoft Windows kann D auf COM-Code ( Component Object Model ) zugreifen .
Bei richtiger Speicherverwaltung können viele andere Sprachen mit D in einer einzigen Binärdatei gemischt werden. Zum Beispiel ermöglicht der GDC-Compiler das Verknüpfen von C, C++ und anderen unterstützten Sprachcodes, um gemischt zu werden. D-Code (Funktionen) kann auch als C-, C++-, Pascal-ABIs-verwendend gekennzeichnet und somit als Callbacks an die in diesen Sprachen geschriebenen Bibliotheken übergeben werden . In ähnlicher Weise können Daten zwischen den in diesen Sprachen geschriebenen Codes auf beide Arten ausgetauscht werden. Dies beschränkt normalerweise die Verwendung auf primitive Typen, Zeiger, einige Formen von Arrays, Unions, Strukturen und nur einige Typen von Funktionszeigern.
Da viele andere Programmiersprachen oft die C-API zum Schreiben von Erweiterungen oder zum Ausführen des Interpreters der Sprachen bereitstellen, kann D auch direkt mit diesen Sprachen kommunizieren, indem Standard-C-Bindungen (mit einer dünnen D-Schnittstellendatei) verwendet werden. Beispielsweise gibt es bidirektionale Bindungen für Sprachen wie Python , Lua und andere Sprachen, die häufig Codegenerierung zur Kompilierzeit und Methoden zur Typreflektion zur Kompilierzeit verwenden.
Interaktion mit C++-Code
D verfolgt einen freizügigen, aber realistischen Ansatz für die Zusammenarbeit mit C++-Code.
Für D-Code, der als extern(C++) gekennzeichnet ist , werden die folgenden Funktionen angegeben:
- Die Konventionen zur Namensverkleinerung müssen denen von C++ auf dem Ziel entsprechen.
- Für Funktionsaufrufe ist die ABI gleichwertig.
- Die vtable soll bis zur einfachen Vererbung abgeglichen werden (die einzige von der D-Sprachspezifikation unterstützte Ebene).
C++-Namespaces werden über die Syntax extern(C++, namespace) verwendet, wobei namespace der Name des C++-Namespace ist.
Ein Beispiel für die C++-Interoperation
Die C++-Seite
#include <iostream>
using namespace std;
class Base
{
public:
virtual void print3i(int a, int b, int c) = 0;
};
class Derived : public Base
{
public:
int field;
Derived(int field) : field(field) {}
void print3i(int a, int b, int c)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
int mul(int factor);
};
int Derived::mul(int factor)
{
return field * factor;
}
Derived *createInstance(int i)
{
return new Derived(i);
}
void deleteInstance(Derived *&d)
{
delete d;
d = 0;
}
Die D-Seite
extern(C++)
{
abstract class Base
{
void print3i(int a, int b, int c);
}
class Derived : Base
{
int field;
@disable this();
override void print3i(int a, int b, int c);
final int mul(int factor);
}
Derived createInstance(int i);
void deleteInstance(ref Derived d);
}
void main()
{
import std.stdio;
auto d1 = createInstance(5);
writeln(d1.field);
writeln(d1.mul(4));
Base b1 = d1;
b1.print3i(1, 2, 3);
deleteInstance(d1);
assert(d1 is null);
auto d2 = createInstance(42);
writeln(d2.field);
deleteInstance(d2);
assert(d2 is null);
}
Besseres C
Die Programmiersprache D hat eine offizielle Untermenge, die als " Better C " bekannt ist. Diese Untermenge verbietet den Zugriff auf D-Funktionen, die die Verwendung anderer Laufzeitbibliotheken als der von C erfordern.
Aktiviert über die Compiler-Flags "-betterC" auf DMD und LDC und "-fno-druntime" auf GDC, kann Better C nur D-Code aufrufen, der unter demselben Flag (und verknüpftem Code außer D) kompiliert wurde, aber ohne die kompilierte Eine bessere C- Option kann Code aufrufen, der damit kompiliert wurde: Dies führt jedoch aufgrund der Unterschiede in der Verarbeitung von Asserts in C und D zu leicht unterschiedlichen Verhaltensweisen.
In Better C . enthaltene Funktionen
- Uneingeschränkte Nutzung von Kompilierzeit-Features (zum Beispiel können die dynamischen Zuordnungsfunktionen von D zur Kompilierzeit verwendet werden, um D-Daten vorab zuzuweisen)
- Vollständige Metaprogrammierungsfunktionen
- Verschachtelte Funktionen, verschachtelte Strukturen, Delegaten und Lambdas
- Memberfunktionen, Konstruktoren, Destruktoren, Betriebsüberladungen usw.
- Das komplette Modulsystem
- Array-Slicing und Array-Grenzen-Überprüfung
- RAII
- Geltungsbereich (Ausgang)
- Speichersicherheitsschutz
- Schnittstelle zu C++
- COM-Klassen und C++-Klassen
- Assert- Fehler werden an die C-Laufzeitbibliothek weitergeleitet
- mit Saiten wechseln
- Endschalter
- Unittest- Blöcke
- printf Formatvalidierung
Von Better C ausgeschlossene Funktionen
- Müllabfuhr
- TypInfo und ModulInfo
- Eingebautes Gewinde (zB
core.thread
) - Dynamische Arrays (obwohl Slices von statischen Arrays funktionieren) und assoziative Arrays
- Ausnahmen
-
synchronisiert und
core.sync
- Statische Modulkonstruktoren oder -destruktoren
Geschichte
Walter Bright begann 1999 mit der Arbeit an einer neuen Sprache. D wurde erstmals im Dezember 2001 veröffentlicht und erreichte im Januar 2007 die Version 1.0. Die erste Version der Sprache (D1) konzentrierte sich auf die zwingenden, objektorientierten und metaprogrammierenden Paradigmen, ähnlich wie C++.
Einige Mitglieder der D-Community, die mit Phobos, der offiziellen Laufzeit- und Standardbibliothek von D, unzufrieden waren , erstellten eine alternative Laufzeit- und Standardbibliothek namens Tango. Die erste öffentliche Tango-Ankündigung erfolgte wenige Tage nach der Veröffentlichung von D 1.0. Tango hat einen anderen Programmierstil angenommen, der OOP und hohe Modularität umfasst. Als von der Community geführtes Projekt war Tango offener für Beiträge, was es ermöglichte, schneller voranzukommen als die offizielle Standardbibliothek. Zu dieser Zeit waren Tango und Phobos aufgrund unterschiedlicher APIs zur Laufzeitunterstützung (Garbage Collector, Threading-Unterstützung usw.) inkompatibel. Dies machte es unmöglich, beide Bibliotheken im selben Projekt zu verwenden. Die Existenz von zwei Bibliotheken, die beide weit verbreitet sind, hat zu erheblichen Streitigkeiten geführt, da einige Pakete Phobos und andere Tango verwenden.
Im Juni 2007 wurde die erste Version von D2 veröffentlicht. Der Beginn der Entwicklung von D2 signalisierte die Stabilisierung von D1. Die erste Version der Sprache wurde in Wartung gestellt und erhielt nur Korrekturen und Implementierungs-Bugfixes. D2 führte grundlegende Änderungen an der Sprache ein, beginnend mit seinem ersten experimentellen const-System . D2 fügte später zahlreiche weitere Sprachfunktionen hinzu, wie zum Beispiel Closures , Reinheit und Unterstützung für die funktionalen und gleichzeitigen Programmierparadigmen. D2 löste auch Probleme mit Standardbibliotheken, indem es die Laufzeit von der Standardbibliothek trennte. Die Fertigstellung eines D2 Tango-Ports wurde im Februar 2012 bekannt gegeben.
Die Veröffentlichung von Andrei Alexandrescus Buch The D Programming Language am 12. Juni 2010 markierte die Stabilisierung von D2, das heute allgemein nur als "D" bezeichnet wird.
Im Januar 2011 wechselte die D-Entwicklung von einer Bugtracker-/Patch-Einreichungsbasis zu GitHub . Dies hat zu einem deutlichen Anstieg der Beiträge zum Compiler, zur Laufzeit und zur Standardbibliothek geführt.
Im Dezember 2011 gab Andrei Alexandrescu bekannt, dass D1, die erste Version der Sprache, zum 31. Dezember 2012 eingestellt wird. Die endgültige D1-Version, D v1.076, wurde am 31. Dezember 2012 veröffentlicht.
Der Code für den offiziellen D-Compiler, den Digital Mars D-Compiler von Walter Bright, wurde ursprünglich unter einer benutzerdefinierten Lizenz veröffentlicht , die als verfügbare Quelle qualifiziert ist, aber nicht der Open-Source-Definition entspricht . Im Jahr 2014, der Compiler -Frontend wurde erneut lizenziert als Open Source unter der Boost - Software - Lizenz . Dieser neu lizenzierte Code schloss das Backend aus, das teilweise bei Symantec entwickelt wurde . Am 7. April 2017 wurde der gesamte Compiler unter der Boost-Lizenz zur Verfügung gestellt, nachdem Symantec die Erlaubnis erteilt hatte, auch das Backend neu zu lizenzieren. Am 21. Juni 2017 wurde die Sprache D zur Aufnahme in GCC akzeptiert.
Implementierungen
Die meisten aktuellen D - Implementierungen kompiliert direkt in Maschinencode für eine effiziente Ausführung.
Produktionsbereite Compiler:
- DMD – Der Digital Mars D-Compiler von Walter Bright ist der offizielle D-Compiler; Open Source unter der Boost-Softwarelizenz . Das DMD-Frontend wird von GDC (jetzt in GCC) und LDC gemeinsam genutzt, um die Kompatibilität zwischen Compilern zu verbessern. Ursprünglich wurde das Frontend in C++ geschrieben, mittlerweile ist das meiste davon aber in D selbst geschrieben (self-hosting). Die Backend- und Maschinencode-Optimierer basieren auf dem Symantec-Compiler. Zunächst unterstützte es nur 32-Bit-x86, mit Unterstützung für 64-Bit-amd64 und PowerPC von Walter Bright. Später wurde das Backend und fast der gesamte Compiler von C++ nach D portiert, um ein vollständiges Self-Hosting zu ermöglichen.
- GCC – The GNU Compiler Collection , fusionierte GDC in GCC 9 am 29. Oktober 2018. Die ersten funktionierenden Versionen von GDC mit GCC, basierend auf GCC 3.3 und GCC 3.4 auf 32-Bit-x86 unter Linux und macOS, wurden am 22. März 2004 veröffentlicht dann erhielt GDC Unterstützung für mehr Plattformen, verbesserte die Leistung und behob Fehler, während der Upstream-DMD-Code für das Frontend und die Sprachspezifikation verfolgt wurde.
- LDC – Ein Compiler basierend auf dem DMD-Front-End, das LLVM als Compiler-Back-End verwendet. Die erste Version in Release-Qualität wurde am 9. Januar 2009 veröffentlicht. Sie unterstützt Version 2.0.
Spielzeug- und Proof-of-Concept-Compiler:
- D-Compiler für .NET – Ein Back-End für den D-Programmiersprache 2.0-Compiler. Es kompiliert den Code in Common Intermediate Language (CIL)-Bytecode und nicht in Maschinencode. Die CIL kann dann über eine virtuelle Maschine der Common Language Infrastructure (CLI) ausgeführt werden . Das Projekt wurde seit Jahren nicht mehr aktualisiert und der Autor hat angegeben, dass das Projekt nicht mehr aktiv ist.
- SDC – Der Stupid D Compiler verwendet ein benutzerdefiniertes Front-End und LLVM als Compiler-Back-End. Es ist in D geschrieben und verwendet einen Scheduler, um die Symbolauflösung zu handhaben, um die Kompilierzeit-Features von D elegant zu handhaben. Dieser Compiler unterstützt derzeit eine begrenzte Teilmenge der Sprache.
Mit den oben genannten Compilern und Toolchains ist es möglich, D-Programme für viele verschiedene Architekturen zu kompilieren, einschließlich x86 , amd64 , AArch64 , PowerPC , MIPS64 , DEC Alpha , Motorola m68k , Sparc , s390 , WebAssembly . Die primär unterstützten Betriebssysteme sind Windows und Linux , aber verschiedene Compiler unterstützen auch Mac OS X , FreeBSD , NetBSD , AIX , Solaris/OpenSolaris und Android , entweder als Host oder Ziel oder beides. Das WebAssembly- Target (unterstützt über LDC und LLVM) kann in jeder WebAssembly-Umgebung betrieben werden, z. B. in modernen Webbrowsern ( Google Chrome , Mozilla Firefox , Microsoft Edge , Apple Safari ) oder dedizierten virtuellen Wasm-Maschinen.
Entwicklungswerkzeuge
Zu den Editoren und integrierten Entwicklungsumgebungen (IDEs), die Syntax-Highlighting und teilweise Codevervollständigung für die Sprache unterstützen, gehören unter anderem SlickEdit , Emacs , vim , SciTE , Smultron , Zeus und Geany .
- Dexed (ehemals Coedit), eine D-fokussierte grafische IDE, geschrieben in Object Pascal
- Mono-D ist eine funktionsreiche, plattformübergreifende D-fokussierte grafische IDE basierend auf MonoDevelop / Xamarin Studio, die hauptsächlich in C Sharp geschrieben wurde.
- Eclipse- Plug-Ins für D enthalten DDT und Descent (totes Projekt).
- Die Visual Studio-Integration wird von VisualD bereitgestellt.
- Visual Studio Code- Integration mit Erweiterungen wie Dlang-Vscode oder Code-D.
- Für TextMate ist ein Bundle verfügbar , und die Code::Blocks IDE enthält teilweise Unterstützung für die Sprache. Standard-IDE-Features wie Code Completion oder Refactoring sind jedoch noch nicht verfügbar, obwohl sie teilweise in Code::Blocks funktionieren (aufgrund der Ähnlichkeit von D mit C).
- Das Xcode 3 Plugin "D for Xcode" ermöglicht D-basierte Projekte und Entwicklung.
- Das Autovervollständigungs-Plugin von KDevelop (sowie seinem Texteditor-Backend Kate) ist verfügbar.
Es gibt Open-Source- D-IDEs für Windows , von denen einige in D geschrieben sind, wie Poseidon, D-IDE und Entice Designer.
D-Anwendungen können mit jedem C/C++-Debugger wie GDB oder WinDbg debuggt werden , obwohl die Unterstützung für verschiedene D-spezifische Sprachfeatures extrem eingeschränkt ist. Unter Windows können D-Programme mit Ddbg oder Microsoft-Debugging-Tools (WinDBG und Visual Studio) debuggt werden , nachdem die Debug-Informationen mit cv2pdb konvertiert wurden . Der ZeroBUGS- Debugger für Linux bietet experimentelle Unterstützung für die D-Sprache. Ddbg kann mit verschiedenen IDEs oder über die Befehlszeile verwendet werden; ZeroBUGS verfügt über eine eigene grafische Benutzeroberfläche (GUI).
DustMite ist ein leistungsstarkes Tool zum Minimieren von D-Quellcode, das beim Auffinden von Compiler- oder Testproblemen nützlich ist.
dub ist ein beliebter Paket- und Build-Manager für D-Anwendungen und -Bibliotheken und wird oft in die IDE-Unterstützung integriert.
Beispiele
Beispiel 1
Dieses Beispielprogramm gibt seine Befehlszeilenargumente aus. Die main
Funktion ist der Einstiegspunkt eines D-Programms und args
ist ein Array von Strings, die die Befehlszeilenargumente darstellen. A string
in D ist ein Array von Zeichen, dargestellt durch immutable(char)[]
.
import std.stdio: writefln;
void main(string[] args) {
foreach (i, arg; args)
writefln("args[%d] = '%s'", i, arg);
}
Die foreach
Anweisung kann über jede Sammlung iterieren. In diesem Fall erzeugt es eine Folge von Indizes ( i
) und Werten ( arg
) aus dem Array args
. Der Typ von Index i
und Wert wird arg
vom Typ des Arrays abgeleitet args
.
Beispiel 2
Im Folgenden werden verschiedene D-Fähigkeiten und D-Design-Kompromisse in einem kurzen Programm gezeigt. Es durchläuft die Zeilen einer Textdatei namens words.txt
, die in jeder Zeile ein anderes Wort enthält, und gibt alle Wörter aus, die Anagramme anderer Wörter sind.
import std.stdio, std.algorithm, std.range, std.string;
void main() {
dstring[] [dstring] signature2words;
foreach (dchar[] w; lines(File("words.txt"))) {
w = w.chomp().toLower();
immutable signature = w.dup.sort().release().idup;
signature2words[signature] ~= w.idup;
}
foreach (words; signature2words) {
if (words.length > 1) {
writeln(words.join(" "));
}
}
}
-
signature2words
ist ein integriertes assoziatives Array, das dstring (32-bit / char)-Schlüssel Arrays von dstrings zuordnet. Es ist ähnlich wiedefaultdict(list)
in Python . -
lines(File())
ergibt Zeilen träge, mit dem Zeilenumbruch. Es muss dann mit kopiert werdenidup
, um einen String zu erhalten, der für die assoziativen Array-Werte verwendet werden soll (dieidup
Eigenschaft von Arrays gibt ein unveränderliches Duplikat des Arrays zurück, das erforderlich ist, da derdstring
Typ tatsächlich istimmutable(dchar)[]
). Integrierte assoziative Arrays erfordern unveränderliche Schlüssel. - Der
~=
Operator hängt einen neuen dstring an die Werte des zugehörigen dynamischen Arrays an. -
toLower
,join
undchomp
sind Zeichenfolgenfunktionen, die D mit einer Methodensyntax verwenden kann. Die Namen solcher Funktionen ähneln oft Python-String-Methoden. DastoLower
wandelt eine Zeichenfolge in Kleinbuchstaben um,join(" ")
fügt ein Array von Zeichenfolgen zu einer einzigen Zeichenfolge unter Verwendung eines einzelnen Leerzeichens als Trennzeichen zusammen undchomp
entfernt einen Zeilenumbruch am Ende der Zeichenfolge, falls vorhanden. Dasw.dup.sort().release().idup
ist besser lesbar, entspricht aberrelease(sort(w.dup)).idup
zB. Diese Funktion wird als UFCS (Uniform Function Call Syntax) bezeichnet und ermöglicht die Erweiterung aller integrierten Pakettypen oder Pakettypen von Drittanbietern mit methodenähnlicher Funktionalität. Der Schreibstil von Code wie dieser wird oft als Pipeline (insbesondere wenn die verwendeten Objekte langsam berechnet werden, zum Beispiel Iteratoren / Bereiche) oder Fluent-Schnittstelle bezeichnet . - Das
sort
ist eine std.algorithm-Funktion, die das Array an Ort und Stelle sortiert und eine eindeutige Signatur für Wörter erstellt, die Anagramme voneinander sind. Dierelease()
Methode für den Rückgabewert vonsort()
ist praktisch, um den Code als einzelnen Ausdruck zu erhalten. - Die zweite
foreach
iteriert über die Werte des assoziativen Arrays und kann den Typ vonwords
. -
signature
einer unveränderlichen Variablen zugewiesen wird, wird ihr Typ abgeleitet. -
UTF-32
dchar[]
wird anstelle von normalem UTF-8 verwendet,char[]
sonstsort()
verweigert es die Sortierung. Es gibt effizientere Möglichkeiten, dieses Programm nur mit UTF-8 zu schreiben.
Verwendet
Bemerkenswerte Organisationen, die die Programmiersprache D für Projekte verwenden, sind Facebook , eBay und Netflix .
D wurden erfolgreich eingesetzt AAA - Spiele , Sprachdolmetscher, virtuelle Maschinen eines Betriebssystem - Kernel , GPU - Programmierung, Web - Entwicklung , numerische Analyse , GUI - Anwendungen , ein Fahrgastinformationssystem , maschinelles Lernen, Textverarbeitung, Web- und Anwendungsserver und Forschung.
Siehe auch
Verweise
Weiterlesen
- Alexandrescu, Andrei (4. Januar 2010). Die Programmiersprache D (1 Aufl.). Addison-Wesley-Profi. ISBN 978-0-321-63536-5.
- Alexandrescu, Andrei (15. Juni 2009). "Der Fall für D" . Dr. Dobbs Tagebuch.
- Hell, Walter (8. April 2014). "Wie ich dazu kam, D zu schreiben" . Dr. Dobbs Tagebuch.
- ehreli, Ali (1. Februar 2012). "Programmieren in D" .(verteilt unter CC-BY-NC-SA-Lizenz). Dieses Buch bringt Anfängern das Programmieren bei, deckt aber auch viele fortgeschrittene D-Themen ab.
- Metz, Cade (7. Juli 2014). „Die nächste große Programmiersprache, von der Sie noch nie gehört haben“ . Verkabelt .
- Ruppe, Adam (Mai 2014). D Kochbuch (1 Aufl.). PACKT-Veröffentlichung. ISBN 978-1-783-28721-5.