Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

Godny następca C i C++, czyli programowanie w D - cz. 2: Zaczynamy pisać w D

Witam ponownie. W poprzednim wpisie pokrótce opisałem, czym jest D i co zmieniło w porównaniu do C i C++. W drugiej części pokażę, jak to wygląda w praktyce. Opiszę, skąd ściągnąć i jak zainstalować kompilator. Przedstawię też podstawy programowania w D (wpis kieruję do osób, które mają już ogólne pojęcie o programowaniu. Jeżeli chciałbyś nauczyć się D jako pierwszego języka, zapraszam na Wikibooka o D ). Do dzieła :)

Z czym to się je?

Jak się zapewne domyślacie, żeby pisać programy, trzeba mieć jakiś kompilator. Oczywiście D nie jest w tym przypadku wyjątkiem. W pierwszej części wymieniłem najważniejsze kompliatory - DMD, GDC, LDC i D Compiler for .NET. Osobiście polecam ten pierwszy. DMD jest kompilatorem oficjalnym i wyznacza standardy dla języka. Korzystając z najnowszej wersji DMD mamy pewność, że nasze programy nie będą odstawać od nich tak, jak Internet Explorer od internetowych ;)

DMD możemy ściągnąć z oficjalnej strony języka D - Digitalmars. Do wyboru jest kilka opcji - wieloplatrormowa paczka .ZIP, windowsowy instalator .EXE i pakiecik .DEB dla Ubuntu. Możemy też zainstalować wersję D1 (1.030 lub 1.057) lub D2 (2.042). Pomimo oznaczenia Alpha proponuję wybrać D 2.0. Działa stabilnie, więc o błędy nie trzeba się martwić. Poza tym większość bibliotek jest już pisanych pod tą wersję, więc za starszą wersję chyba nie opłaca się zabierać.
Tak więc ściągamy i instalujemy wersję 2.042 w pakiecie EXE lub DEB. Jeżeli korzystamy z dystrybucji Linuksa obsługującej inne pakiety, niż DEB, musimy poradzić sobie z archiwum ZIP. Nie jest to zbyt trudne, a instrukcje znajdują się w paczce.

Zaczynamy

Mając świeżo zainstalowany kompilator, możemy przystąpić do pisania. A pierwszym programem, który napiszemy, będzie (tu niestety nikogo nie zaskoczę) Hello World. Otwieramy dowolny edytor tekstu (Domyślne dla KDE i GNOME oferują kolorowanie składni do D. Dla Windowsa możemy ściągnąć np. Notepad++ ) i piszemy:

// Listing 1
import std.stdio;

void main()
{
	write("Hello world!");
	readln();
}

Jak widać, od C trochę się różni. Czym? Od początku:
- import std.stdio; - ta linia bardziej przypomina Javę, niż C/C++. Zamiast plików nagłówkowych mamy podział na pakiety i moduły. pakiet std to biblioteka standardowa zwana Phobos. Zawarty w nim moduł stdio odpowiada za standardowe wejście i wyjście. Niektóre moduły dołączane wraz z językiem znajdują się w pakiecie core (jak np. core.thread).
- void main() - funkcja main nie musi zwracać wartości. Może przyjmować argument typu string[] zawierający wywołania programu.
- write("Hello, world!"); - wypisuje linię tekstu. W C mieliśmy printf(), w D mamy do wyboru cztery funkcje: write(), writeln(), writef(), writefln(). Używamy ich podobnie, jak printf() z C. Funkcje z literami "ln" na końcu dostawiają znak nowego wiersza, zaś funkcje bez "f" (czyli write i writeln) nie biorą pod uwagę formatów (np. %d).
- readln(); - po prostu wczytuje linię tekstu, żeby konsola nam nie zniknęła za wcześnie.
W D możemy również używać biblioteki standardowej języka C. W zasadzie przydaje się to tylko wtedy, gdy moduły D nie mają odpowiednich funkcji (np. funkcja system()). Każdemu plikowi nagłówkowemu C odpowiada moduł std.c.nazwapliku. Np. dla stdlib mamy moduł std.c.stdlib.

Skoro napisaliśmy już kod, czas na kompilację. Kod źródłowy należy zapisać w pliku z rozszerzeniem .d, np. main.d. Program kompilujemy poleceniem dmd:

dmd main.d

Jeżeli nie popełniliśmy żadnych błędów, kompilator nic nie napisze, a nazwa pliku wykonywalnego będzie taka sama, jak pliku źródłowego.

Przy pierwszym programie wspomnę jeszcze o komentarzach. Podobnie jak w C++, mamy do dyspozycji komentarze liniowe (//coś tam) i blokowe (/*coś tam*/). Oprócz tego jest jeszcze jeden nowy typ komentarzy blokowych. Umożliwia on umieszczanie jednego komentarza w drugim: (/+jeden komentarz /+ i drugi wewnątrz +/ tego pierwszego +/).

"Więc chodź, pomaluj mój świat..."

Po pokazaniu, jak wygląda prosty program w D, czas zobaczyć, co to cudeńko potrafi. Spróbujmy przykładowo napisać coś w konsoli na zielono. Kolorowanie tekstu w konsoli jest dostępne zarówno na Windowsie, jak i na systemach Linuksowych. Kłopot w tym, że każdy z systemów robi to inaczej. Linux wykorzystuje sekwencje ucieczki, zaś Windows - odpowiednie funkcje niedostępne pod innymi platformami.
Kompilacja warunkowa to mechanizm, który - w zależności od instrukcji zawartych w kodzie lub flag kompilatora - pozwala na kompilowanie programu w różny sposób. Możemy ją na przykład wykorzystać, by inne instrukcje były wbudowane w build dla Windowsa, a inne w build dla Linuksa. Nasz kod będzie wyglądał tak:

// Listing 2
import std.stdio;
version(Windows)
{
	import std.c.windows.windows;
}

void main()
{
	version(Windows)
	{
		HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
		SetConsoleTextAttribute(hOut, FOREGROUND_GREEN);
		write("Zielono mi! :)");
	}
	else version(linux)
	{
		write("\033[32mZielono mi! :)\033[0m");  //]] - inaczej jest błąd w bloku kodu na stronie
	}
	else
	{
		write("Nie działa na twoim systemie.");
	}
	readln();
}

Od razu rzucają się w oczy bloki version(Windows) i version(linux). To właśnie one odpowiadają za kompilację pod pewnymi warunkami. W zaprezentowanym kodzie warunkiem jest system operacyjny. Bloki, które nie nie znajdą się w programie (jak np. blok version(linux) na Windowsie) w ogóle nie są brane pod uwagę przez kompilator. Nawet, jeśli zawierają błędy, kompilator ignoruje je. Tak więc ten kod dla kompilatora będzie wyglądał tak:

Na Windowsie:

// Listing 3
import std.stdio;
import std.c.windows.windows;

void main()
{
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(hOut, FOREGROUND_GREEN);
	write("Zielono mi! :)");
	readln();
} 

Na Linuksie:

// Listing 4
import std.stdio;

void main()
{
	write("\033[32mZielono mi! :)\033[0m");  //]] - inaczej jest błąd w bloku kodu na stronie
	readln();
} 

I na innych systemach (else działa tak samo, jakby zamiast version było if)

// Listing 5
import std.stdio;

void main()
{
	write("Nie działa na twoim systemie.");
	readln();
} 

Jeżeli w listingu 1 pominęlibyśmy else w linii "else version(linux)", na Windowsie zobaczymy wyjście:

Zielono mi :)Nie działa na twoim systemie.

gdzie fragment "Zielono mi :)" będzie w kolorze zielonym.

Szczegółów dotyczących kolorowania wyjścia konsoli na poszczególnych systemach nie będę opisywał, ponieważ artykuł i tak jest już długi. Informacje te można bardzo łatwo znaleźć za pośrednictwem wyszukiwarki.

Kompilacji warunkowej ciąg dalszy

Możliwości kompilacji warunkowej nie kończą się na kompilacji w zależności od systemu. Jako parametr bloku version() możemy podać dowolny identyfikator wersji. Standardowo mamy już zdefiniowane identyfikatory dla systemów operacyjnych, architektur, kolejności bajtów, wersji języka D 2.0... Ponadto możemy definiować własne warunki na dwa sposoby:

// Listing 6
...
version(4) // lub inna liczba naturalna
{
	// kod
}

Ten wariant określa poziom wersji. Do polecenia kompilacji dopisujemy wtedy flagę -version 4, co poskutkuje wkompilowaniem do programu kodu oznaczonego poziomie 4 lub wyższym.

// Listing 7
...
version(TwojaWersja)
{
	// kod
}

W takim wypadku w celu zawarcia tego kodu w programie używamy flagi kompilatora -version TwojaWersja.

Możemy też przyporządkować wersje w kodzie. Załóżmy, że chcemy skompilować program w trzech edycjach - Free, Standard i Pro. Wersje te będą różniły się ilością funkcji.

 // Listing 8
version(Free)
{
	version = FajnaFunkcja;
}
version(Standard)
{
	version = FajnaFunkcja;
	version = BardzoFajnaFunkcja;
	version = DrugaBardzoFajnaFunkcja;
	
}
version(Pro)
{
	version = FajnaFunkcja;
	version = BardzoFajnaFunkcja;
	version = DrugaBardzoFajnaFunkcja;
	version = SuperHiperWypasionaFunkcja;
}

// Dalsza część kodu

version(FajnaFunkcja)
{
	// implementacja
}
version(BardzoFajnaFunkcja)
{
	// implementacja
}
version(DrugaBardzoFajnaFunkcja)
{
	// implementacja
}
version(SuperHiperWypasionaFunkcja)
{
	// implementacja
}

Teraz każda z wersji Free, Standard i Pro ustawia dodatkowe parametry określające, które z funkcji mają być zawarte w każdej z wersji. Ustawiając flagę kompilatora -version=Pro dostaniemy wszystkie funkcje, zaś -version=Free da nam tylko tą fajną.

Kompilacja warunkowa jest również wykorzystywana w programowaniu kontraktowym. Szerszy opis kontraktów w D znajdziecie w jednej z kolejnych części.

To jeszcze nie koniec

Oj, rozpisałem się. Ale to jeszcze nie koniec. Myślę, że kolejne części (a podejrzewam, że będą jeszcze co najmniej dwie) pojawią się na moim blogu w najbliższych dniach. Opiszę w nich między innymi tablice dynamiczne i asocjacyjne (napiszemy słownik) oraz programowanie kontraktowe. Zapraszam do czytania.

P.S.
04.04.2010 - Poprawiłem jeden błąd merytoryczny. Kompilacja warunkowa w C++ jest możliwa, przepraszam za niedomówienie. W D jednak mechanizm ten wydaje się być znacznie prostszy w użyciu i bardziej funkcjonalny. 

Komentarze