Book Image

D Cookbook

By : Adam Ruppe
Book Image

D Cookbook

By: Adam Ruppe

Overview of this book

Table of Contents (21 chapters)
D Cookbook
Credits
Foreword
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Using a custom exception type


D uses exceptions to handle errors, just like many other programming languages. In D, exceptions are always implemented as classes that derive from the class Throwable, and they are differentiated by their type. So, it is best to generate a new exception subclass for different types of errors your code can generate. This way, users of your code will get the most information and control out of your exceptions.

How to do it…

Let's use a custom exception type by using the following steps:

  1. Declare a class that inherits from Exception.

  2. Make a constructor that takes, minimally, two parameters: string file and size_t line, with default values of __FILE__ and __LINE__, respectively.

  3. Have the constructor forward the arguments to the constructor of Exception.

  4. Use your exception.

The following is the code:

class MyException : Exception {
    this(string message, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
          super(message, file, line, next);
    }
}

void main() {
    import std.stdio;
    try
      throw new MyException("message here");
    catch(MyException e)
      writeln("caught ", e);
}

How it works…

D uses exceptions to handle errors. All throwable objects inherit from either Exception, for recoverable events, or Error for unrecoverable errors, which generally ought not be caught. The common base class is Throwable.

Typically, a custom exception inherits from Exception, then declares, minimally, a constructor that forwards the functionality to super(). You may also store additional information specific to your use case.

The constructor of Exception (here, called with super()) takes four arguments: a string message, a filename, a line number, and optionally, a reference to another exception. The message, filename, and line number are used to construct a message for the user, which is printed to the console if the exception is not caught.

You don't have to specify the file and line number at the throw site; any default argument of __FILE__ or __LINE__ is automatically expanded at the function's call site. This is useful to make the error message more useful by showing exactly where the exception came from.

The fourth parameter, Throwable next, is used if an exception handler throws an exception. It references the exception that was being handled when this one was generated.

Tip

To get full stack trace symbols in the printed exception, you may need to compile with the debug information enabled by using dmd –g.

There's more…

You should check error codes when using the C functions and turn them into exceptions. If it sets errno for error details, the std.exception module has a subclass called ErrnoException that is perfect for the following code:

import core.sys.posix.unistd; // for the low-level Posix functions
import core.sys.posix.fnctl // for more low-level Posix functions
import std.exception; // for ErrnoException
auto fd = open("myfile.txt", O_RDONLY);
// open() returns -1 if it was unable to open the file,
// and sets errno with error details. Check for that failure.
if(fd == -1)
    throw new ErrnoException("Couldn't open myfile.txt");
// close the file automatically at the end of the scope
scope(exit) close(fd);
/* read the file here */

See also

Scope guards, discussed in Chapter 5, Resource Management, are convenient for use with exceptions. They let you put clean-up or recovery code near the creation point in an exception-safe way. In the preceding example, you used a scope guard to ensure the file is properly closed when the function returns, even if an exception is thrown.