D Programming - Functions



This chapter describes the functions used in D programming.

Function Definition in D

A basic function definition consists of a function header and a function body.

Syntax

return_type function_name( parameter list ) { 
   body of the function 
}

Here are all the parts of a function −

  • Return Type − A function may return a value. The return_type is the data type of the value the function returns. Some functions perform the desired operations without returning a value. In this case, the return_type is the keyword void.

  • Function Name − This is the actual name of the function. The function name and the parameter list together constitute the function signature.

  • Parameters − A parameter is like a placeholder. When a function is invoked, you pass a value to the parameter. This value is referred to as actual parameter or argument. The parameter list refers to the type, order, and number of the parameters of a function. Parameters are optional; that is, a function may contain no parameters.

  • Function Body − The function body contains a collection of statements that define what the function does.

Calling a Function

You can a call a function as follows −

function_name(parameter_values)

Function Types in D

D programming supports a wide range of functions and they are listed below.

  • Pure Functions
  • Nothrow Functions
  • Ref Functions
  • Auto Functions
  • Variadic Functions
  • Inout Functions
  • Property Functions

The various functions are explained below.

Pure Functions

Pure functions are functions which cannot access global or static, mutable state save through their arguments. This can enable optimizations based on the fact that a pure function is guaranteed to mutate nothing which is not passed to it, and in cases where the compiler can guarantee that a pure function cannot alter its arguments, it can enable full, functional purity, that is, the guarantee that the function will always return the same result for the same arguments).

import std.stdio; 

int x = 10; 
immutable int y = 30; 
const int* p;  

pure int purefunc(int i,const char* q,immutable int* s) { 
   //writeln("Simple print"); //cannot call impure function 'writeln'
   
   debug writeln("in foo()"); // ok, impure code allowed in debug statement 
   // x = i;  // error, modifying global state 
   // i = x;  // error, reading mutable global state 
   // i = *p; // error, reading const global state
   i = y;     // ok, reading immutable global state 
   auto myvar = new int;     // Can use the new expression: 
   return i; 
}

void main() { 
   writeln("Value returned from pure function : ",purefunc(x,null,null)); 
}

When the above code is compiled and executed, it produces the following result −

Value returned from pure function : 30 

Nothrow Functions

Nothrow functions do not throw any exceptions derived from class Exception. Nothrow functions are covariant with throwing ones.

Nothrow guarantees that a function does not emit any exception.

import std.stdio; 

int add(int a, int b) nothrow { 
   //writeln("adding"); This will fail because writeln may throw 
   int result; 
   
   try { 
      writeln("adding"); // compiles 
      result = a + b; 
   } catch (Exception error) { // catches all exceptions 
   }

   return result; 
} 
 
void main() { 
   writeln("Added value is ", add(10,20)); 
}

When the above code is compiled and executed, it produces the following result −

adding 
Added value is 30 

Ref Functions

Ref functions allow functions to return by reference. This is analogous to ref function parameters.

import std.stdio;

ref int greater(ref int first, ref int second) { 
   return (first > second) ? first : second; 
} 
 
void main() {
   int a = 1; 
   int b = 2;  
   
   greater(a, b) += 10;   
   writefln("a: %s, b: %s", a, b);   
}

When the above code is compiled and executed, it produces the following result −

a: 1, b: 12

Auto Functions

Auto functions can return value of any type. There is no restriction on what type to be returned. A simple example for auto type function is given below.

import std.stdio;

auto add(int first, double second) { 
   double result = first + second; 
   return result; 
} 

void main() { 
   int a = 1; 
   double b = 2.5; 
   
   writeln("add(a,b) = ", add(a, b)); 
}

When the above code is compiled and executed, it produces the following result −

add(a,b) = 3.5

Variadic Functions

Variadiac functions are those functions in which the number of parameters for a function is determined in runtime. In C, there is a limitation of having atleast one parameter. But in D programming, there is no such limitation. A simple example is shown below.

import std.stdio;
import core.vararg;

void printargs(int x, ...) {  
   for (int i = 0; i < _arguments.length; i++) {  
      write(_arguments[i]);  
   
      if (_arguments[i] == typeid(int)) { 
         int j = va_arg!(int)(_argptr); 
         writefln("\t%d", j); 
      } else if (_arguments[i] == typeid(long)) { 
         long j = va_arg!(long)(_argptr); 
         writefln("\t%d", j); 
      } else if (_arguments[i] == typeid(double)) { 
         double d = va_arg!(double)(_argptr); 
         writefln("\t%g", d); 
      } 
   } 
}
  
void main() { 
   printargs(1, 2, 3L, 4.5); 
}

When the above code is compiled and executed, it produces the following result −

int 2 
long 3 
double 4.5

Inout Functions

The inout can be used both for parameter and return types of functions. It is like a template for mutable, const, and immutable. The mutability attribute is deduced from the parameter. Means, inout transfers the deduced mutability attribute to the return type. A simple example showing how mutability gets changed is shown below.

import std.stdio;

inout(char)[] qoutedWord(inout(char)[] phrase) { 
   return '"' ~ phrase ~ '"';
}

void main() { 
   char[] a = "test a".dup; 

   a = qoutedWord(a); 
   writeln(typeof(qoutedWord(a)).stringof," ", a);  

   const(char)[] b = "test b"; 
   b = qoutedWord(b); 
   writeln(typeof(qoutedWord(b)).stringof," ", b); 

   immutable(char)[] c = "test c"; 
   c = qoutedWord(c); 
   writeln(typeof(qoutedWord(c)).stringof," ", c); 
} 

When the above code is compiled and executed, it produces the following result −

char[] "test a" 
const(char)[] "test b" 
string "test c"

Property Functions

Properties allow using member functions like member variables. It uses the @property keyword. The properties are linked with related function that return values based on requirement. A simple example for property is shown below.

import std.stdio;

struct Rectangle { 
   double width; 
   double height;  

   double area() const @property {  
      return width*height;  
   } 

   void area(double newArea) @property {  
      auto multiplier = newArea / area; 
      width *= multiplier; 
      writeln("Value set!");  
   } 
}

void main() { 
   auto rectangle = Rectangle(20,10); 
   writeln("The area is ", rectangle.area);  
   
   rectangle.area(300); 
   writeln("Modified width is ", rectangle.width); 
}

When the above code is compiled and executed, it produces the following result −

The area is 200 
Value set! 
Modified width is 30
Advertisements