Szablon (D)

Szablony (ang. template) są jednym z podejść do programowania uogólnionego. Szablony w D są bardzo podobne do szablonów i przestrzeni nazw w C++. Można im nadawać osobne funkcjonujące niezależnie nazwy przez co zachowują się wtedy jak sparametryzowana przestrzeń nazw. Szablony w D tworzy się następująco:

template Nazwa(T) {
  //deklaracje funkcji, klas, zmiennych
  int func(T arg) {
    return sizeof(arg);
  }
}

Gdzie Nazwa jest szablonową przestrzenią nazw o parametrze T. Używanie szablonów w programie następuje poprzez poprzedzenie listy parametrów w nawiasach okrągłych wykrzyknikiem.

Nazwa!(long).func(10);
Nazwa!(int[]).func(1,2,3,4,5,6,7,8,9,10);

Klasy szablonowe i funkcje

Klasy i funkcje szablonowe można tworzyć na 2 sposoby.[1][2] Długi poprzez umieszczenie klasy/funkcji o nazwie takiej samej jak szablon lub w krótszy sposób poprzez umieszczenie listy parametrów zaraz za deklaracją nazwy klasy/funkcji.


class MojaKlasa(T) {
  // klasa szablonowa z parametrem T
}

int func(T)(T arg) {
  // funkcja szablonowa
}

Użycie jest podobne do zwykłych szablonów, ale można pominąć wtedy przestrzeń nazw:

MojaKlasa!(int) a = new MojaKlasa!(int);
func(52);

Zmienna liczba parametrów szablonu

W D istnieje możliwość tworzenia szablonów z nieokreśloną liczbą parametrów [3], co może być w wielu przypadkach bardzo użyteczne.

real sum(T...)(T args) {
  real ret = 0;
  foreach(a; args) ret += a;
  return ret; // zwróci sumę wszystkich argumentów jako liczbę rzeczywistą
}

Użycie takiej funkcji jest o tyle proste, że nie trzeba wymieniać wszystkich argumentów:

sum(12,34434.42432,32453235432,21421,124,2424.2342,214124.5);

Metaprogramowanie z wykorzystaniem szablonów

Metaprogramowanie w D jest znacznie ułatwione dzięki zastosowaniu instrukcji warunkowej kompilacji "static if". Przykładowo szablon liczący silnię z podanej liczby [4] (por. silnia w czasie kompilacji (c++)):

template Silnia(N) {
  static if(N==0) // statyczny if
    enum Silnia = 1;
  else
    enum Silnia = Silnia!(N-1)*N;
}

Gdzie N to parametr określający liczbę z jakiej liczymy silnię.

Kompilatory D pozwalają również na wykonywanie funkcji "czystych" (ang. pure) w czasie kompilacji, tak więc powyższy szablon można zastąpić zwykłą funkcją:

int silnia(int n) {
  return (n == 1) ? 1 : n * silnia(n-1);
}

static int x = silnia(5);

Wartość zmiennej x będzie obliczona w czasie kompilacji.

Można to sprawdzić, korzystając z możliwości D operowania na ciągach znaków w czasie kompilacji oraz z instrukcji kompilatora pragma(msg,) [5]:

template itoa(long i)
{
    static assert (i > 0);
    static if (i < 10) const char[] itoa = "" ~ cast(char)(i + '0');
    else const char[] itoa = itoa!(i / 10) ~ itoa!(i % 10);
}

pragma(msg, itoa!(silnia(6));

Ponieważ szablon itoa parametryzowany jest wartością typu long, użyta jest instrukcja warunkowej kompilacji static assert, może ona być pominięta, jeśli typ argumentu i zostanie zmieniony na ulong.

Instrukcja ta spowoduje przerwanie kompilacji, jeśli podany argument jest liczbą ujemną.

Przypisy

  1. Templates D 1.0 (Class Template Declaration). [dostęp 2010-06-03]. (ang.).
  2. Templates D 1.0 (Function Template Declaration). [dostęp 2010-06-03]. (ang.).
  3. Variadic Templates D 1.0. [dostęp 2010-06-03]. (ang.).
  4. Templates Revisited D 1.0. [dostęp 2010-06-03]. (ang.).
  5. Factorial (Rosetta Code). [dostęp 2010-06-03]. (ang.).

Bibliografia