C/C++ to D

Some notes.

I’m assuming you want to use D largely, but not entirely, for competitive programming. That’s me right now.

Basics

Syntax is very similar. Function definitions, semicolon-terminated statements, variable declarations, and so on. You can declare int main() {...} or void main() {...} or something with arguments.

Basic types like bool and int and double are all there. Wonderfully, long is 64 bits. Instead of unsigned whatever, just prefix a u, e.g. uint.

Arithmetic operators and bit operators are all there too, including unsigned right shift >>>. Although ^ is still xor, D has exponentiation as ^^. Sadly, % is still same-sign remainder; there’s no true mod.

import std.stdio;

Casts look like cast(int) x;

Control Flow

if, while, for, do, and even switch all work as you’d expect, along with break and continue.

foreach is the nice addition though. Not only can you iterate over arrays and stuff, but range loops go like:

foreach (i; 0 .. 10) writeln(i);

If you want both the index and the element (a la zipWithIndex):

foreach (i, e; array) writeln(i, e);

And to modify the element in place:

foreach (ref e; array) e += 4;

This ref is also the syntax to pass something by reference to a function, by the way. Functions are otherwise pretty much the same.

void func(ref int x) { x += 4; }

I/O

You have access to scanf and printf, for some weird reason.

But D’s alternatives are readf and writef. They also have variants writefln to add a newline. You often won’t even need writef anyway, since write and writeln take any number of arguments and turn them into strings before printing.

int n = 100;
writeln("Case #1: ", n, " is the answer.");

Two gotchas with readf:

  1. readf specifiers don’t skip white space, so where you’d write:

    scanf("%d%d", &a, &b);

    you should write:

    readf(" %d %d", &a, &b);
  2. readf on a string reads to end of input.

So call readln instead, to read a line: either with no arguments to return a string or with a buffer to read it into there. This also reads the newline, but after import std.string; you can call s = chomp(s); to get rid of control characters.

Call functions like stderr.writef(...) for debug output.

I just want my int-scanning macro so I don’t forget my ampersands, please

I can do better: readf infers the type of whatever you give it. This variadic magic fits on an 80-character line. After seeing this, just thinking about cstdio code makes me angry.

void scan(T...)(ref T args) { foreach (ref arg; args) readf(" %s", &arg); }

Arrays

int[100008] a; gives you a static (fixed-length) array.

int[] b; gives you a dynamic (variable-length) array. It’s sort of like C++’s STL vector. Actually, it’s properly called a slice, and you can read more about it in the D Slices article, which gets linked quite often in the forum.

Unfortunately after a tragic experience on Codeforces, I’ve discovered its reallocation semantics seems somewhat slow when compared to similar uses of C++ vector for one big problem: after you pop an element off the back or otherwise decrease the length, the slice must be reallocated to insert more elements. So far, I’ve found std.container.Array better fits such needs. But if you only push things in, iterate across them, or delete everything at once, I think slices are okay.

Declare Arrays like:

Array!int a;

You use a bang for templates in D.

Anyway, in general, binary ~ is the append or concatenate operator; you can append elements by writing b ~= 3; or b ~= [5, 7]; For slices, experimenting shows that D does roughly double the slice after each reallocation so that this is amortized O(1), as you’d hope, although I can’t find any documentation saying so. You can get the length with b.length.

Arrays, on the other hand, multiply their capacities by 3/2 on reallocation.

Also, if you import std.array;, you get to do b.front and b.back on slices (you don’t need parentheses!), which are refs through which you can modify the element. You also get b.popFront() and b.popBack() (which are void like their C++ STL counterparts, to my disappointment) and b.empty. There doesn’t appear to be a .clear() function but you can just set b.length = 0;. Also, calling .dup makes a dynamic copy.

Strings (string) are just an alias for immutable char[]s. (So if you want to store a string in a char[], call .dup. But you can still ~= a string to a variable of type string; it just replaces the variable with a new copy.)

Arrays also have .front, .back, .length, .empty, indexing and so on. Their popping methods are called .removeFront() and removeBack(), however. Arrays do have a .clear() method, but unlike STL vector, it throws away any capacity. It appears setting .length = 0 is slightly faster if you expect to make the Array large again soon.

Deques, etc.

This is so far the one place D has disappointed me relative to C++, and only mildly. Your best bet is DList (doubly-linked list) from std.container. The type looks like DList!int. As above, you can .front, .popFront(), and also .insertFront(x), and same for back and cognates, of course.

You can also .empty, but sadly, you can’t get the length of a DList. But now that I think about it, .empty is often enough anyway if you’re BFSing or whatever, and you can always maintain the length yourself somewhere.

You can declare associative arrays by putting stuff between the same square brackets you used for arrays. These are hash tables, not ordered. The type is just e.g.

string[int] map; // int to string map

Test for key inclusion Pythonically with in, as:

if (key in map) map[key] += 1;

It also supports .length; .keys and .values (dynamic array copies); .byKey() and .byValue() and .byKeyValue() (iterable-ish things, not copies); .get (pass a second default value to return if )

There is a RedBlackTree and a BinaryHeap in std.container.

Structs

As you’d expect, but no semicolon.

struct Pt {
int x;
int y;
}

You get == and a constructor Pt(42, 1337) for free.

Debug

Sweet: just surround a block with debug.

debug {
// code here
}

Compile with -debug to enable.

You also get assert(...);, which is normally enabled; you compile with -release to disable.

Pairs/Tuples

Not as beautiful as I’d hoped, but certainly not worse than C++. The import is

import std.typecons;

The type looks like Tuple!(int, string). Construct as tuple(1337, "hi"). Access elements with [0], [1] and so on.

The nice thing is that these are real tuples that can hold more than two things, I guess.

Also, interestingly, you can foreach-iterate across a tuple, which gets expanded at compile-time.

Alias

D’s alternatives to typedefs and some other things.

alias Weight = double;

Sorting and Other Algorithms

import std.algorithm;

No s.begin(), s.end() nonsense. Just sort(array). Optionally, you can specify a comparator with either a string or a function:

sort!"a > b"(array);
sort!((x, y) => x > y)(array);
bool myComp(int x, int y) @safe pure nothrow { return x > y; }
sort!myComp(array);

The string made me kind of uncomfortable at first, but I guess that’s my reflex to JavaScript string callbacks and the same concerns don’t apply here. There’s no evaling, it’s all compile time, and local variables don’t leak; any expression involving a and b works.

What else is there? min and max, which support more than 2 arguments; swap; fill(array, val); sum.

Extra Things

Inside indexes, $ is a shortcut for array length, so a[$-1] is the last element. You can do stuff like a[$/2] too.

D has const, which has the same meaning as in C++ (you can’t modify the variable through this pointer) but it also has two more powerful modifiers: immutable (this variable is guaranteed not-modifiable through any pointer) and enum (compile-time constants, as if #defined). If you write enum x = ... you can force D to calculate the expression at compile-time, even if it’s very complicated involving function calls and whatnot

Other function parameter modifiers than ref include in, out, lazy.

The cases of switch has some extra goodies. You can write

case 2: .. case 5:

or

case 2, 4:

Also, remember how you strangely import stuff from std.array and then you can call things on arrays? This is actually Uniform Function Call Syntax (UFCS) sugar; you can write array.front to call front(array). Of course, you can directly call front(array) too. This is how the std.array functions work.

You can even do it yourself, although I probably wouldn’t advise it:

int foo(int a, int b) {
return a + b + 1;
}
writeln(3.foo(4));

There are functional goodies filter, map, reduce, which are also called like sort, with the bang ! template-ish syntax. UFCS lets you chain functional goodies like array.map!"a + 1".filter!"a > 5" if you want to. You can call reduce(seed, array) (= foldl) or reduce(array) (= foldl1).

These goodies work on “ranges”, not just arrays, which are things that allow sequential access in general, like how C++ uses pairs of generators. Note that filter and map return lazy ranges; you can call array on the results (after importing std.array) to convert it to an array.

std.range has lots of other more basic things you’d expect in a functional language:

  • take, drop, repeat, cycle (all with the same meaning as in Haskell)
  • retro is a reversed version of a range like Python’s reversed
  • chain(...) chains two or more ranges together like Python’s itertools.chain
  • chunks(n) cuts the source into chunks of length n
  • only yields a range of its varargs.

The function to from std.conv does conversions between types, and is quite good at it, e.g. to!int and to!string.

Operator overloading is a bit weird (but see D’s rationale):

import std.stdio;
import std.algorithm;
import std.array;
struct Pt {
int x, y;
Pt opBinary(string op)(in Pt o)
if (op == "+") // this is checked at compile time
{
return Pt(x + o.x, y + o.x);
}
int opCmp()(const ref Pt o) const {
if (x != o.x) return x < o.x ? -1 : 1;
if (y != o.y) return y < o.y ? -1 : 1;
return 0;
}
}
void main() {
Pt[] ps = [ Pt(1, 2), Pt(3, 4), Pt(1, 5), Pt(3, 2) ];
sort(ps);
writeln(ps);
ps = ps.map!(a => a + Pt(100, 100)).array;
Pt res = ps.reduce!"a + b";
writeln(ps);
writeln(res);
}

opCmp is three-valued, and the comparison operators <, <=, >, >= are derived from it. Unfortunately, there doesn’t seem to be a more convenient way to write this that is still as fast; you can do cmp(only(x, y), only(o.x, o.y)), using cmp from std.algorithm for lexicographical comparison and only from std.range to create a lazy range, but it still takes 3 to 4 times as much time. (This version is then 3 to 4 times faster than if you cmp([x, y], [o.x, o.y]).)

Pleasingly, structs have a built-in definition of ==.

Note, by the way, we can’t use map!"a + Pt(100,100)" because “Pt” isn’t available in the thing that compiles the string into the function.

Template

Okay, I added a few more imports, but this is still much shorter than my C++ template.

I don’t need any of the range-loop macros or scanning macros or debug macros or (s).begin(), (s).end() macros or vector-dumping function (vectors have nice to!string already) or typedefs.

I might need to add a minify and maxify, but the definitions of those will be shorter than C++ too. w00t. For now (this’ll get updated later):

import std.stdio, std.array, std.range, std.string, std.typecons;
import std.algorithm, std.container, std.math, std.numeric, std.random;
void scan(T...)(ref T args) { foreach (ref arg; args) readf(" %s", &arg); }
void minify(T)(ref T a, in T b) { if (a > b) a = b; }
void maxify(T)(ref T a, in T b) { if (a < b) a = b; }
void ewriteln(T...)(T args) { stderr.writeln("\033[35m", args, "\033[0m"); }
int ilen(T)(const ref T a) { return cast(int)(T.length); }

(note: the commenting setup here is experimental and I may not check my comments often; if you want to tell me something instead of the world, email me!)