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
- ↑ Templates D 1.0 (Class Template Declaration). [dostęp 2010-06-03]. (ang.).
- ↑ Templates D 1.0 (Function Template Declaration). [dostęp 2010-06-03]. (ang.).
- ↑ Variadic Templates D 1.0. [dostęp 2010-06-03]. (ang.).
- ↑ Templates Revisited D 1.0. [dostęp 2010-06-03]. (ang.).
- ↑ Factorial (Rosetta Code). [dostęp 2010-06-03]. (ang.).
Bibliografia
- Templates D 1.0. [dostęp 2010-06-03]. (ang.).
- Templates D 2.0. [dostęp 2010-06-03]. (ang.).