Category Archives: D tutorials

classes in D

A class is where D’s object oriented features live.

Some quick facts:

  • All classes inherit for a root class, Object
  • classes may have only one super class but can implement many interfaces (just like Java)
  • methods are virtual by default (again, just like Java)
  • The default protection attribute is public
  • The constructor is defined like: this()
  • objects live on the garbage collected heap

Operator overloading for structs

D’s structs can use operator overloading. This is accomplished by defining specially named member functions.

Operator overloading is the ability to define language operators (e.g +, +=) on custom types.

In this tutorial, we will focus on designing a rational number struct (called Rational).
We will also overload operators of it to make its usage feel more natural.

You can read the official D documentation on operator overloading here.

Here is our first version.

We can create a Rational like this: auto r = Rational(48, 18); Calling writeln(r.n, "/", r.d); correctly displays 8/3.

Okay let’s move on to what this tutorial is about – operator overloading.

1. Binary Operators

First, we’d like to be able to add (+), subtract (-), multiply (*) and divide (/) rationals.
These are binary operators, which means they take two arguments.

To overload binary operators, we must define opBinary!op. This is a template function where op is the operator, as a string.

  • Unlike C++, we don’t need to define multiple overloads for each binary operator. In D, one is enough.
  • By using auto ref, the compiler will pass by value or by reference depending on the situation.

Now we can do this:

2. Unary Operators

We would also like to overload unary (to invert a rational) and + (not necessary, just for completeness).

Overloading of unary operators is done with opUnary!op. It is similar to opBinary!op, but instead it takes no arguments.

So here’s our implementation for unary + and –

3. Op Assignment Operators (+=, etc)

+=, -=, *= and /= would also be useful. Those can be overloaded with opOpAssign!op.

Note that we return ref Rational, a reference to this object, as these operators modify the object rather than returning a new one.

4. Equality Operators (==, !=)

To compare rationals for equality, we need to the define opEquals. Note that there’s no opNotEquals (or anything along those lines) – Expressions like a != b are automatically re-written by the compiler to the form !(a == b).

Here’s the implementation. We simply compare the fields that matter to each specific case. In our case, we must compare both numerator and denominator.

The extra set of empty parentheses is to make the the function a template (as auto ref only works with templates).

5. Comparison Operators (<, >, ...)

Finally, we would like to able to compare rationals using <, > – the comparison operators.

This can be accomplished with opCmp.

You might wonder why the notion of equality is not defined in terms of comparison operators.
From the official D documentation:


The equality and inequality operators are treated separately because while practically all user-defined types can be compared for equality, only a subset of types have a meaningful ordering.

For example, while it makes sense to determine if two RGB color vectors are equal, it is not meaningful to say that one color is greater than another, because colors do not have an ordering. Thus, one would define opEquals for a Color type, but not opCmp.

If opCmp is defined but opEquals isn’t, the compiler will supply a default version of opEquals that performs member-wise comparison.

Result

Here’s what the result looks like:

Note: It would be really useful if we had defined overloads that accept int – so that we could to things like Rational(1, 1) + 5 . However, this can be done with the same logic with what we ‘ve done above. Just provide an implementation of your operator of choice that takes an int as its parameter.

Introduction to structs

In D, like in all modern languages, you can define your own types.

These are the four custom types of… types you can create:

  • struct
  • class
  • union
  • enum

This tutorial is an introduction to structs.

structs are types that are defined by programmers and represent records. For example, you could have a Point struct that has two data members: x and y.

Let’s see a simple example.
Suppose we are making an application with jokes.
We could define a type, Joke, to hold the required data of a joke:

Alright, seems easy. But a jokes application without jokes sucks… So let’s add some!

Note that we are able to construct the jokes with the syntax: Joke(id, text, category).

Better. The users might actually laugh now.
But there’s still a small problem. The data members are public, which means a reckless programmer might accidentally mutate them. We don’t want this to ever happen, so let’s define some getters.

What we have done is to make id, text and category const private member variables, and provided property getters to access them.

We could have made the variables const without providing property getters, but as good citizens of encapsulation, we should not expose the public fields.

Here is the full source code:

And the output:

Category: Chuck Norris
“Some magicians can walk on water. Chuck Norris can swim through land.”

Category: Food
“Why don’t the French eat snails? They don’t like fast food.”

Category: Animals
“Why did the dinosaur cross the road? Chickens hadn’t evolved yet”

Want more? Check out Humorista: https://play.google.com/store/apps/details?id=greenbits.humorista

Install DCD auto-completion engine

The D Completion Daemon is an auto-complete program for the D programming language.

It is used by several IDEs and text editors to provide syntax auto completion (IntelliJ, Atom, …).

How to install

Ubuntu (and other Debian-based distributions)

Open a terminal and execute:

OS-X with Homebrew

Open a terminal and execute:

Windows / Manual Installation

If you are on Windows or want to compile DCD yourself, the easiest way to install it is by using dub. If you haven’t installed dub yet (highly recommended!), read the installation instructions here.

Open a terminal and execute:

Using D with IntelliJ

Before continuing, make sure you have installed:

  1. A D compiler (DMD, GDC, LDC)
  2. dub (D’s package manager) [optional, but highly recommended]
  3. Java
  4. IntelliJ

1. Open IntelliJ

New IntelliJ user

If you haven’t opened IntelliJ before, you will be presented with the Welcome screen.
To directly install the plugin from here, click on the gear icon, like the in the image below.

intellij_15_welcome_screen
IntelliJ 15 welcome screen

Existing IntelliJ user

If you already are an IntelliJ user, your last opened project will be shown. To go into plugins, select File → Settings, and then choose Plugins from the menu on the left.

2. Install the D plugin

From the plugins window (below), select Browse repositories...

intellij_plugins
Plugins window

Install

In the window that opens, type something like D lang.
Select the one whose vendor is MasterThought and click Install.

intellij_dlang_plugin

When it’s finished, you will be asked to restart the IDE. Press Restart.
intellij_restart_prompt

3. Create a D project

To create a project, select Create New Project from the Welcome screen.
intellij_create_project_0

Otherwise, if IntelliJ takes you to your last opened project, select File → New → Project...

3.1 Choose Project Type

Choose the project type from the left-side panel. The recommended type is D Application with DUB (this presumes that you have installed dub).

intellij_create_project_1

3.2 Choose Compiler

In this step you can select which D compiler to use. I only have DMD at this time, so I select that one.

intellij_create_project_2

3.3 dub Project Configuration (for dub projects)

In this screen you can configure parameters for dub, e.g. dub’s configuration file format (json (recommended) or SDL).

intellij_create_project_3

Press next to proceed.

3.4 Project Name

Finally, set a name for your project.

intellij_create_project_4

4. Using the IDE

Now that the project has been created, the main editor window is shown.

intellij_project_view

From the project view (on the left), expand the project by clicking on the icon. In the source folder, dub has created app.d, which contains the application’s main() function.
Double-click on app.d to open it in the editor.

intellij_main_editor
app.d, generated by dub

5. Running the project

To run the project, a Run Configuration must be created.
Select Run → Edit Run... → Run DUB

Even though you have dub installed, the plugin will complain that it doesn’t know where to find it. A pop-up will appear at the top right corner of the editor. Press Configure to set the required configuration.

intellij_configure_run_config

The following window will appear. At the top, you must specify dub’s location. On my machine, Auto Find succeeds in finding it. If on yours it does not, press the ... icon to manually locate it. Finally, press OK.

intellij_dub_config

If all went well, the project will run and you will see the following output in the output panel.

6. Setting up auto-completion

6.1 Install DCD auto-completion engine

The plugin uses DCD for auto-completion, so make sure you have installed it. If you haven’t, read the instructions here.

6.2 Configure the plugin

  1. Select File → Settings...
  2. From the menu on the left, select Other Settings → D Tools
  3. At Dcd Server section, locate the dcd-server executable (or press Auto Find to automatically find it). You also need to set up the import paths of the D runtime and standard library, because dcd doesn’t seem to pick them up by default. In the Flags text field, if you are using DMD and Linux, enter -I/usr/include/dmd/druntime/import -I/usr/include/dmd/phobos/ . If you are on windows, you need to find the corresponding directories in the DMD installation path.
  4. At Dcd Client section, locate the dcd-client executable, just the above.
  5. Press OK

Associative arrays

D has built-in support for associate arrays (AAs) (also known as hash tables).

This is analogous to Java’s Map and C++’s std::unordered_map.

Declaring an associative array

To declare an associative array, use the following syntax:

Inserting into an associative array

To insert into an AA, use the [] operator.
Here is an example that creates an AA with the squares of numbers from [0, 10] and then displays them to standard output.

Running the above displays:
[0:0, 6:36, 7:49, 2:4, 3:9, 10:100, 1:1, 8:64, 5:25, 4:16, 9:81]
Note that the numbers are not sorted — This is expected, as associative arrays are not internally sorted.

Notes

  • Re-assigning an already existing key will replace the previous value.
  • Attempting to access a key that does not exist, will result in a core.exception.RangeError error.

Removing from an associative array

Use the remove() function to remove a key.
aa.remove("hello");

Testing if a key exists

To test if a key exists, use the in operator, which returns a pointer to the value. If the key doesn’t exist, the pointer is null.

Clearing an associative array

There are two ways to clear an associative array:

  1. Iterate over the keys and remove them
  2. Throw away the old AA and create a new one

Method 1 – Remove the keys

Method 2 – Create a new associative array

To throw away the current AA, assign null to it.

Properties

We have already seen some properties of associative arrays, like remove(), and keys. Here are the rest of them:

Property Description
.sizeof Returns the size of the reference to the associative array; it is 4 in 32-bit builds and 8 on 64-bit builds.
.length Returns number of values in the associative array. Unlike for dynamic arrays, it is read-only.
.dup Create a new associative array of the same size and copy the contents of the associative array into it.
.keys Returns dynamic array, the elements of which are the keys in the associative array.
.values Returns dynamic array, the elements of which are the values in the associative array.
.rehash Reorganizes the associative array in place so that lookups are more efficient. rehash is effective when, for example, the program is done loading up a symbol table and now needs fast lookups in it. Returns a reference to the reorganized array.
.byKey() Returns a forward range suitable for use as a ForeachAggregate to a ForeachStatement which will iterate over the keys of the associative array.
.byValue() Returns a forward range suitable for use as a ForeachAggregate to a ForeachStatement which will iterate over the values of the associative array.
.byKeyValue() Returns a forward range suitable for use as a ForeachAggregate to a ForeachStatement which will iterate over key-value pairs of the associative array. The returned pairs are represented by an opaque type with .key and .value properties for accessing the key and value of the pair, respectively.
.get(Key key, lazy Value defVal) Looks up key; if it exists returns corresponding value else evaluates and returns defVal.

Further reading

For more information (e.g. how to use struct or class within associative arrays), read more here [dlang.org].

loops

D supports the common for / while / do-while statements.

for

while

do-while

For convenience, D has foreach and foreach_reverse.

foreach

The code above prints the square of the numbers in the range [0, 10). Notice that the range is inclusive/exclusive, so 10 is not included.
Also note that we didn’t write i‘s type, as it is automatically infered, but you can write it explicitly if you’d like.

You can also iterate over arrays:

But what if’d you like to print the indices of the even numbers?
Fortunately, D’s foreach can provide you with the index as well!

foreach_reverse

foreach_reverse works like foreach but in reverse order. Using the previous example:

As expected, the same results are printed, in reverse order.

Things to remember

  • for/while/do-while work just like in C, C++
  • foreach / foreach_reverse are more convenient forms of for
  • foreach / foreach_reverse loops can also provide you with the index in the form foreach (index, element)

if statement

Your familiar if-statement

D’s if-statement syntax is the same as other languages with C-style cyntax (C, C++, Java, etc).

Where expression is any expression that can be converted to a boolean value.
As it turns out, a lot of expressions can be converted to a boolean expression.

  • 0 is falsy
    • Applies for (u)int, (u)short, (ulong)
    • All other numbers are truthy
  • 0f and 0.0 are falsy
    • Any other floating point number is truthy, even float.nan and float.infinity!
  • The character literal '\0' is falsy

Array thuthiness

But what about static/dynamic arrays?
To test, let’s use the following template function:

Static arrays

Static arrays cannot be converted to booleans.

Dynamic arrays
  • string literals are truthy, whereas the null string and a string which is an empty array ([]) are falsy.
  • A non-empty array like [1, 2, 3] is truthy. An empty array is falsy.

In the last example, however, even though t‘s length was set to 0, it was truthy! But why?

To understand why, remember that a dynamic array can be thought as:

What the if-statement does it to check whether ptr is null. If it is not null, the expression is truthy, otherwise it’s falsy. It does not care about the length property!
So what happened is that the array’s length was set to 0, but its ptr property was not null. That is the reason the expression was truthy.

Because of this, you must not do

to test whether an array is empty or not, but check its length property.

Things to remember

  • D’s if-statement works more like C/C++’s instead of Java/C#’s
  • 0, 0.0f, 0.0 are falsy
  • nan and infinities are truthy
  • string literals are always truthy
  • Dynamic arrays whose ptr property is null are falsy. Otherwise they are truthy
  • Use the .length property of dynamic arrays to test whether they are empty or not

Dynamic arrays, strings

Dynamic arrays consist of a length and a pointer to the array data. Multiple dynamic arrays can share all or parts of the array data.

A dynamic array can be thought as:

where T is the type of data the array holds.

  • Dynamic arrays are allocated on the garbage collected heap (unlike static arrays which live on the stack)
  • Like static arrays, they have a .length property, which is the number of elements in the array
  • Like static arrays, dynamic arrays can be returned from functions
  • Unlike static arrays, they can be resized at runtime
  • Dynamic arrays can be sliced

How to define a dynamic array

Dynamic arrays can be concatenated

Dynamic arrays can be concatenated using the concatenation operator, ~.

Dynamic arrays can be resized

By changing the length property, you can resize a dynamic array. If a smaller value than the current one is set, the array is shrunk.

In the example above, we read a number from the user and allocate a dynamic array of that size. Then we read another number and resize the array to the new size.

Dynamic arrays can be sliced

Array slicing is an awesome feature of D arrays. By slicing an array, you get a different view of the original array, without any memory allocations involved.

In the program above, we have function isPalindrome which returns true if a string is palindrome, otherwise false. The function is recursive, that is, it calls itself with a different argument each time, until it reaches a state in which it terminates.

The first thing we do in isPalindrome is to check whether the string is the empty string, or has length 1. By definition both of these are palindrome.

Then we compare the first character of the string with the last one. If they are not equal, the string is not palindrome, so we return false. In D, you can use the $ character inside array indixes, which denotes the length. This is a shorthand for str[str.length – 1].

Finally, the function calls itself with a slice of the string, using the slice operator, ... The slice contains all characters except the first and the last.

To better illustrate what happens, consider a call to isPalindrome with the argument “abccba”.

Recursion depth string isPalindrome?
0 “abccba” true
1 “bccb” true
2 “cc” true
3 “” true

So after 4 tests, the algorithm decides that “abccba” is indeed a palindrome string.

You might have noticed the @nogc annotation on the function. This ensures that no GC allocations can occur.

Yes, strings are dynamic arrays!

In the previous example, I have used a string, while we are talking for dynamic arrays.
In fact, a string is a dynamic array of immutable characters!

A string an alias for immutable(char)[].
That’s all you need to know about strings for now. We will talk about immutable data in a later tutorial.

Things to remember

  • Dynamic arrays are allocated on the garbage collector’s heap
  • DAs can be concatenated using the ~ operator
  • DAs can be resized by changing their length property
  • DAs can be sliced using the .. operator
  • array[$ – 1] is a shorthand for array[array.length – 1]
  • The string type in D is nothing more than an alias to immutable(char)[]. Thus, strings are dynamic arrays as well

Static arrays

  • Static arrays are analogous to C arrays. They are distinguished by having a length fixed at compile time.
  • The total size of a static array cannot exceed 16Mb. A dynamic array should be used instead for such large arrays.
  • A static array with a dimension of 0 is allowed, but no space is allocated for it. It’s useful as the last member of a variable length struct, or as the degenerate case of a template expansion.
  • Static arrays are value types. Unlike in C, static arrays are passed to functions by value. Static arrays can also be returned by functions.

How to define a static array

Non matching sizes result in a compile error.

Static arrays are value types

Because static arrays are value types, when copying one into another, the elements are copied as well. In the example above, we see that the mutation in t0 did not affect t1.
As we will see in the next tutorial, this is not the case with dynamic arrays.

Static arrays know their length

Static arrays (and dynamic) have a .length property. Its type is size_t and returns the number of elements in the array. Note that for static arrays, this is the size the array was initialized with and cannot change.

Calling functions that accept a pointer

Sometimes you might have a function that takes an array as a pointer as its argument, e.g when calling a function from a C library.
Static arrays have a .ptr property that is a pointer type.

Further reading

See the reference on dlang.org.