Faculteit Toegepaste Wetenschappen Vakgroep Informatietechnologie
Vergelijkende studie van C++, C#, Java en D door Benny Botte & Jan Hanselaer
Promotor: Prof. Dr. Ir. H. TROMP Scriptiebegeleider: Dr. K. DE SCHUTTER Scriptiebegeleider: Ir. B. ADAMS
Scriptie ingediend tot het behalen van de academische graad van licentiaat in de toegepaste informatica
Academiejaar 2006–2007
Voorwoord We begonnen aan dit eindwerk vanuit onze interesse voor programmeertalen en de honger naar nieuwe kennis daaromtrent. Op korte tijd hebben we enorm veel bijgeleerd over programmeren en alles wat daar rond hangt. We presenteren hier onze bevindingen. Graag willen wij onze promotor Herman Tromp en onze begeleiders Bram Adams en Kris De Schutter bedanken voor de begeleiding, de suggesties en de vele steun de afgelopen maanden.
Benny Botte & Jan Hanselaer, mei 2006
Toelating tot bruikleen “De auteur geeft de toelating deze scriptie voor consultatie beschikbaar te stellen en delen van de scriptie te kopi¨eren voor persoonlijk gebruik. Elk ander gebruik valt onder de beperkingen van het auteursrecht, in het bijzonder met betrekking tot de verplichting de bron uitdrukkelijk te vermelden bij het aanhalen van resultaten uit deze scriptie.”
Benny Botte & Jan Hanselaer, mei 2007
Vergelijkende studie van C++, C#, Java en D. door Benny BOTTE & Jan HANSELAER Scriptie ingediend tot het behalen van de academische graad van licentiaat in de toegepaste informatica Academiejaar 2006–2007 Promotor: Prof. Dr. Ir. H. TROMP Scriptiebegeleider: Dr. K. DE SCHUTTER Scriptiebegeleider: Ir. B. ADAMS Faculteit Toegepaste Wetenschappen Universiteit Gent Vakgroep Informatietechnologie
Samenvatting De software-ontwikkelaar beschikt over een waar arsenaal aan mogelijke programmeertalen die dienst kunnen doen bij de implementatie van applicaties. Deze talen kunnen sterk uiteenlopen, bijvoorbeeld Prolog versus C++. Evengoed zijn ze echter nauw verwant en zijn er slechts kleinere verschillen op vlak van visie en/of filosofie. De tweede situatie gaat op voor de volgende familie van talen : C++, C#, Java en D. Doel van dit afstudeerwerk is om een vergelijking te maken van deze object-geori¨enteerde talen, waarbij gekozen werd voor een eerder conceptuele aanpak. Er werd gesteund op de beschikbare informatie onderbouwt met enkel praktische experimenten.
Trefwoorden Programmeertalen, C++, C#, Java, D.
A comparative study between C++, C#, Java and D Benny Botte & Jan Hanselaer Supervisor(s): Bram Adams, Kris De Schutter, Herman Tromp Abstract—There exist many programming languages, each of them with his own features and support. Most of the time people want to create applications as fast and reliable as possible. How can you determine what language is best suited for a specific application? Are some languages better in a specific domain? We try to find out what features and possibilities really matter by doing a theoretical and practical research for four closely related programming languages. That way we can find out what the features, that the languages claim to support, are worth in practice. Keywords—Programming Languages, C++, C#, Java, D
I. I NTRODUCTION
T
ODAY a programmer has a choice between a lot of programming languages for writing a software application. Some languages have very similar features. The question is if one language is better than another wether or not in a specific domain. We study four closely related general purpose languages, namely C++, C#, Java and D. First we compare them from a theoretical approach and look what features they are offering. Then we write some domain specific applications to find out how their features hold in practice and to determine the possible advantages of one language versus another. II. T HEORETICAL A diffilty we encounterd was that some terms - like features or specific language constructs - occasionally have different names in the different languages. Also there are some features languages claim to have, that don’t offer the same functionality for different languages. We give a short explanation of the basic features of the languages. All of them support at least the object oriented paradigm. They’re all static strongly typed languages. A. C++ C++ was created in 1983 by Bjarne Stroustrup at AT&T. The language is influenced by C++ and Simula67. The main goal was to insert object oriented features in C. C++ is a multiparadigm language, it supports among others the procedural, object oriented and generic programming paradigm. The latter is supported by an advanced template system. C++ works with pointers and there’s no garbage collection. Low-level access to the memory is possible and therefore C++ is very powerful but can also be very buggy. B. C# C# was created at Microsoft with Anders Hejlsberg as lead architect. It was first released in 2000. C# is an object oriented language. It’s not meant to work with pointers but there’s a way to do it. C# has a garbage collector. C# came together with the .Net framework. They were mainly meant to simplify programming concepts from the past like component based programming. Together with .Net, C# is
suited for enterprise application development. C# compiles to intermediate language (IL) en must be executed by a component that implements the Common Language Infrastructure (CLI). As a consequence C# applications are portable. In Windows is er de bijhorende Common Language Runtime (CLR) en in Linux bestaat een open source implementatie van de CLI, namely Mono. C. Java Java was created at Sun MicroSystems with James Gosling as lead architect. The first public release was in 1995. Java is an object oriented language. Originally the language was meant for settop-boxes, but it was until the internet began to grow that Java became really popular thanks to the possibility of embedding a Java program as an applet in a webpage. Java introduced some concepts that weren’t widely in use till then. Java works with a virtual machine and compiled code, called bytecode, is therefore portable to other platforms. Java doesn’t use pointers and is fully garbage collected. Java supports enterprise application development with his J2EE framework. D. D D is a less known language than the others, mainly because it lacks commercial support. It was created in 1999 by Walter Bright as a successor to C en C++ but is also influenced by more modern languages like Java en C#. It’s a member of the C syntax family, and its appearance is very similar to that of C++. D isn’t meant to work with pointers but it’s still possible, mainly to ease the path for programmers coming from C++. D is garbage collected but it’s also possible to manage the memory by writing and calling the necessary constructors and destructors. The language is still under development. Its focus is on combining the power and high performance of C and C++ with the programmer productivity of modern languages like Ruby and Python. The D language compiles directly to machine code. D is a multi-paradigm language, it supports among others the procedural, object oriented, generic programming paradigm. As in C++, generic programming is supported by an advanced template system. D’s template system is an attempt to improve the C++ template system. Special attention is given to the needs of quality assurance, documentation, management, portability and reliability. In other words, D tries to support robustness as much as possible. Therefore it has some concepts built in the language like unittests, contract programming and others. The majority of these concepts can be simulated with the other languages but they lack a thorough support.
III. P RACTICAL We wrote a couple of applications to determine the way the languages differ from each other concerning programming concepts and features. We chose to implement a GUI, a batch and a network application. There are still other kinds of applications that are widely used (for example applications that access a database), but we had to draw a line somewhere. It’s impossible to imagine the computer world today without graphical applications. They can clarify data presentations. Besides that, people are spoiled and like things - and thus programs - more when they look nice. For the GUI application we made a little game. It’s a variant of the game Sokoban. Batch applications are meant to process large amounts of data. Applications like this must be very reliable and capable of working without interaction once started. We wrote an application that scans a couple of locations on the file system and parses some kinds of types fixed by the user. The output consists of statistics concerning the number of files and directories and of the number of words and characters in the parsed files. Network applications are also very common nowadays. A lot of computers are working more over a network - local or on the world wide web - than on the local machine. So synchronization and the ability to work with threads are important for a programming language. The netwerk application we implemented is a chatroom. Now we give a survey of the most important topics that arose while writing the applications. A. Documentation generation A programming language needs a lot of external support like documentation. For C++, Java en C# there was more than enough information in books and on the internet but the availability of sound documentation was rather disappointing for D. The language specification is only available via the website and so far there has never been written a book in english. B. IDE Another important aspect concerning support is the availability of an advanced IDE. Such an IDE can enhance productivity thoroughly. For C++, C# and Java, we respectively used Bloodshed-DevC++, Visual Studio 2005 and Netbeans 5.5. Those are all advanced and support beside basic features like code completion and an object browser also automatic documentation generation, debugging and others. Again D lacks thorough support here but there are a couple of IDE’s under construction. C. GUI and platform independence For GUI applications the availability of graphical support built in the standard library is a must. C# has the .NET Windows Forms and Java has the javax.swing package. C++ and D have nu built in support and need to work with extern libraries.
Both languages have for a example bindings to wxWidgets. An extern libriry can have the disadvantage of being more difficult in use. With an external library you can also have the problem thet you’re application isn’t portable. D. Threading For among other things, support for threading is very important for network and GUI applications. C#, Java en D have the necessary support in their standard libraries, C++ doesn’t and is in need of an extern library to work with threads. E. Delegates and Events In a GUI application one needs the possibility to react on events. Delegates are a very nice feature here. You can assign one or more delegates to an event and when that event occurs all the assigned delegates will be executed. C# and D work with delegates. Java has no delegates but uses a similar system with listeners that can register themselves at an event. IV. C ONCLUSION All the languages we talked about are very similar concerning the supported features with a few exceptions. So with this point of view it’s not always possible to say which language is preferable. The main conclusion we can draw is thus that the programming language one uses to accomplish a certain goal is not important on its own. The support for the language, among which we can place documentation, development environments and libraries, has to be taken into account too. The existing knowledge of a programming language is of course also important because learning a new language is a very time consuming and thus expensive work. Nevertheless can it be an investment worth the effort. We can state that it’s always useful to have knowledge of more than one programming language because a language suitable for all kinds of applications will probabely never exist. With learning new languages you learn a lot of programming styles and logical approaches for a programming problem.
INHOUDSOPGAVE
INHOUDSOPGAVE
Inhoudsopgave I
Theorie
1
1 Inleiding: De context 1.1 Doelstelling . . . . . . . . . . . . . . . . . . . . . . . 1.2 Wat is een taal? . . . . . . . . . . . . . . . . . . . . 1.3 Wat is een programmeertaal? . . . . . . . . . . . . . 1.4 Ontstaan van programmeertalen . . . . . . . . . . . 1.5 Programmeertalen catalogiseren . . . . . . . . . . . . 1.6 Programmeerparadigma’s . . . . . . . . . . . . . . . 1.6.1 Imperatief . . . . . . . . . . . . . . . . . . . . 1.6.1.1 Puur Imperatief . . . . . . . . . . . 1.6.1.2 Procedureel . . . . . . . . . . . . . . 1.6.1.3 Object-geori¨enteerd . . . . . . . . . 1.6.2 Declaratief . . . . . . . . . . . . . . . . . . . 1.6.2.1 Functioneel . . . . . . . . . . . . . . 1.6.2.2 Logisch . . . . . . . . . . . . . . . . 1.6.3 Besluit . . . . . . . . . . . . . . . . . . . . . . 1.7 Typering . . . . . . . . . . . . . . . . . . . . . . . . . 1.7.1 Typesystemen . . . . . . . . . . . . . . . . . 1.7.2 Typecontrole . . . . . . . . . . . . . . . . . . 1.7.3 Statisch versus dynamisch getypeerd . . . . . 1.7.4 Sterk versus zwak getypeerd . . . . . . . . . . 1.7.5 Safe versus unsafe getypeerd . . . . . . . . . 1.7.6 Nominatief versus structureel getypeerd . . . 1.8 Compilatie versus interpretatie . . . . . . . . . . . . 1.9 Vergelijken van programmeertalen . . . . . . . . . . 1.9.1 Waarom vergelijken van programmeertalen? . 1.9.2 Hoe vergelijken van programmeertalen? . . . 1.9.3 Problematiek en gevaren bij het vergelijken? . 1.9.4 Gevolgde werkwijze . . . . . . . . . . . . . .
pagina i
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
2 2 2 3 4 4 4 5 5 6 6 7 7 8 8 8 8 8 8 9 10 11 11 13 13 13 13 14
INHOUDSOPGAVE
INHOUDSOPGAVE
2 Overzicht van de gebruikte programmeertalen 2.1 C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1 C++ in het kort . . . . . . . . . . . . . . . . . . . . . 2.1.2 Geschiedenis . . . . . . . . . . . . . . . . . . . . . . . 2.1.3 Filosofie . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.4 De verschillende versies . . . . . . . . . . . . . . . . . 2.1.4.1 Ontwikkeling van de standaard . . . . . . . . 2.1.4.2 Relatie en compatibiliteit met C . . . . . . . 2.1.5 Compilers, compilatie en uitvoering . . . . . . . . . . 2.1.6 Namespaces . . . . . . . . . . . . . . . . . . . . . . . . 2.1.7 Geheugen . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.8 Bibliotheken . . . . . . . . . . . . . . . . . . . . . . . 2.1.9 Ondersteuning . . . . . . . . . . . . . . . . . . . . . . 2.1.10 Toekomst . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.1 C# in het kort . . . . . . . . . . . . . . . . . . . . . . 2.2.2 Geschiedenis . . . . . . . . . . . . . . . . . . . . . . . 2.2.3 Filosofie . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.4 .NET Platform . . . . . . . . . . . . . . . . . . . . . . 2.2.5 .NET Framework . . . . . . . . . . . . . . . . . . . . . 2.2.5.1 Ontwerpdoelstellingen . . . . . . . . . . . . . 2.2.5.2 .NET Architectuur . . . . . . . . . . . . . . . 2.2.5.3 .NET Framework Versies . . . . . . . . . . . 2.2.5.4 Andere features . . . . . . . . . . . . . . . . 2.2.5.5 Kritieken . . . . . . . . . . . . . . . . . . . . 2.2.6 Versies en standaarden . . . . . . . . . . . . . . . . . . 2.2.6.1 C# 1.0 . . . . . . . . . . . . . . . . . . . . . 2.2.6.2 C# 2.0 . . . . . . . . . . . . . . . . . . . . . 2.2.6.3 C# 3.0 . . . . . . . . . . . . . . . . . . . . . 2.2.7 Ondersteuning . . . . . . . . . . . . . . . . . . . . . . 2.2.8 De toekomst . . . . . . . . . . . . . . . . . . . . . . . 2.3 Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1 Java in het kort . . . . . . . . . . . . . . . . . . . . . . 2.3.2 Geschiedenis . . . . . . . . . . . . . . . . . . . . . . . 2.3.3 Filosofie . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.4 Object-ori¨entatie . . . . . . . . . . . . . . . . . . . . . 2.3.5 Platform-onafhankelijkheid : de Java Virtual Machine 2.3.6 Versies . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.7 Namespaces : packages . . . . . . . . . . . . . . . . . . 2.3.8 Ondersteuning . . . . . . . . . . . . . . . . . . . . . .
pagina ii
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15 15 15 15 16 17 17 18 19 20 20 21 22 22 23 23 23 24 25 25 25 27 31 35 35 36 36 37 37 40 40 40 40 41 41 42 42 42 43 44
INHOUDSOPGAVE
2.4
INHOUDSOPGAVE
2.3.9 De toekomst . . . . . . . . . . . . . D. . . . . . . . . . . . . . . . . . . . . . . . 2.4.1 D in het kort . . . . . . . . . . . . . 2.4.2 Geschiedenis . . . . . . . . . . . . . 2.4.3 Filosofie . . . . . . . . . . . . . . . . 2.4.4 Versies . . . . . . . . . . . . . . . . . 2.4.5 Compilers, compilatie en uitvoering 2.4.6 Features . . . . . . . . . . . . . . . . 2.4.6.1 Object Ori¨entatie . . . . . 2.4.6.2 Metaprogramming . . . . . 2.4.6.3 Productiviteit . . . . . . . 2.4.6.4 Performantie . . . . . . . . 2.4.7 Ondersteuning . . . . . . . . . . . . 2.4.8 De toekomst . . . . . . . . . . . . .
3 Theoretische vergelijking 3.1 Basisfunctionaliteit . . . . . . . . 3.1.1 Terminologie . . . . . . . 3.1.2 Conventies . . . . . . . . 3.1.2.1 Naam conventies 3.1.3 Constanten . . . . . . . . 3.1.3.1 C++ . . . . . . 3.1.3.2 C# . . . . . . . 3.1.3.3 Java . . . . . . . 3.1.3.4 D . . . . . . . . 3.1.4 Arrays . . . . . . . . . . . 3.1.4.1 Algemeen . . . . 3.1.4.2 C++ . . . . . . 3.1.4.3 C# . . . . . . . 3.1.4.4 Java . . . . . . . 3.1.4.5 D . . . . . . . . 3.1.5 Functies . . . . . . . . . . 3.1.5.1 Algemeen . . . . 3.1.5.2 C++ . . . . . . 3.1.5.3 C# . . . . . . . 3.1.5.4 Java . . . . . . . 3.1.5.5 D . . . . . . . . 3.1.6 Events . . . . . . . . . . . 3.1.6.1 C++ . . . . . . 3.1.6.2 C# . . . . . . . 3.1.6.3 Java . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
pagina iii
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
44 44 44 44 45 46 46 46 46 47 47 47 48 48
. . . . . . . . . . . . . . . . . . . . . . . . .
50 50 50 51 51 52 52 53 53 53 53 53 54 55 56 56 57 58 59 60 62 62 64 65 65 65
INHOUDSOPGAVE
3.2
3.3 3.4
INHOUDSOPGAVE
3.1.6.4 D . . . . . . . . . . . . . . . . . . . . . . . . 3.1.7 Andere . . . . . . . . . . . . . . . . . . . . . . . . . . 3.1.7.1 String switches . . . . . . . . . . . . . . . . . 3.1.7.2 Aliases versus Typedef . . . . . . . . . . . . 3.1.7.3 Preprocessor directieven . . . . . . . . . . . . 3.1.7.4 Enums . . . . . . . . . . . . . . . . . . . . . 3.1.7.5 Operatoren en casts . . . . . . . . . . . . . . Object-geori¨enteerd programmeren . . . . . . . . . . . . . . . 3.2.1 Klassen en Interfaces . . . . . . . . . . . . . . . . . . . 3.2.1.1 Zichtbaarheid . . . . . . . . . . . . . . . . . . 3.2.1.2 Parti¨ele klassen . . . . . . . . . . . . . . . . 3.2.1.3 Function overloading . . . . . . . . . . . . . 3.2.2 Overerving en polymorfie . . . . . . . . . . . . . . . . 3.2.2.1 Implementatie versus interface overerving . . 3.2.2.2 Meervoudige versus enkelvoudige overerving 3.2.2.3 Public, protected of private overerving . . . . 3.2.2.4 Syntax . . . . . . . . . . . . . . . . . . . . . 3.2.2.5 Hiding . . . . . . . . . . . . . . . . . . . . . 3.2.2.6 Covariantie en Contravariantie . . . . . . . . 3.2.2.7 Operator overloading . . . . . . . . . . . . . 3.2.3 Structs . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.4 Data-abstractie . . . . . . . . . . . . . . . . . . . . . . 3.2.4.1 Properties . . . . . . . . . . . . . . . . . . . Generiek programmeren . . . . . . . . . . . . . . . . . . . . . Robuustheid . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4.1 C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4.1.1 Unit Testing . . . . . . . . . . . . . . . . . . 3.4.1.2 Contract programming . . . . . . . . . . . . 3.4.1.3 Code coverage . . . . . . . . . . . . . . . . . 3.4.1.4 RAII . . . . . . . . . . . . . . . . . . . . . . 3.4.1.5 Exception handling . . . . . . . . . . . . . . 3.4.2 C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4.2.1 Unit Testing . . . . . . . . . . . . . . . . . . 3.4.2.2 Contract Programming . . . . . . . . . . . . 3.4.2.3 Code Coverage . . . . . . . . . . . . . . . . . 3.4.2.4 RAII . . . . . . . . . . . . . . . . . . . . . . 3.4.2.5 Exception handling . . . . . . . . . . . . . . 3.4.3 Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4.3.1 Unit Testing . . . . . . . . . . . . . . . . . . 3.4.3.2 Contract Programming . . . . . . . . . . . .
pagina iv
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
65 65 65 66 67 69 70 72 72 72 73 73 73 74 74 75 75 77 77 78 79 80 81 83 84 85 85 85 86 86 86 86 86 87 87 87 87 88 88 88
INHOUDSOPGAVE
3.5
3.6
INHOUDSOPGAVE
3.4.3.3 Code Coverage . . . . . . . 3.4.3.4 RAII . . . . . . . . . . . . 3.4.3.5 Exception handling . . . . 3.4.4 D . . . . . . . . . . . . . . . . . . . . 3.4.4.1 Unit Testing . . . . . . . . 3.4.4.2 Contract programming . . 3.4.4.3 Code coverage . . . . . . . 3.4.4.4 RAII . . . . . . . . . . . . 3.4.4.5 Exception handling . . . . Memory management en garbage collection 3.5.1 Pointers . . . . . . . . . . . . . . . . 3.5.2 C++ . . . . . . . . . . . . . . . . . . 3.5.3 C# . . . . . . . . . . . . . . . . . . 3.5.4 Java . . . . . . . . . . . . . . . . . . 3.5.5 D . . . . . . . . . . . . . . . . . . . . Andere . . . . . . . . . . . . . . . . . . . . . 3.6.1 Documentatie genereren . . . . . . . 3.6.2 Attributen en Annotaties . . . . . . 3.6.2.1 C++ . . . . . . . . . . . . 3.6.2.2 C# . . . . . . . . . . . . . 3.6.2.3 Java . . . . . . . . . . . . . 3.6.2.4 D . . . . . . . . . . . . . . 3.6.3 Reflectie . . . . . . . . . . . . . . . . 3.6.3.1 C++ . . . . . . . . . . . . 3.6.3.2 C# . . . . . . . . . . . . . 3.6.3.3 Java . . . . . . . . . . . . . 3.6.3.4 D . . . . . . . . . . . . . . 3.6.4 Threading . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
4 Besluiten 4.1 Algemene besluiten . . . . . . . . . . . . . . . 4.1.1 Terminologie . . . . . . . . . . . . . . 4.2 De talen . . . . . . . . . . . . . . . . . . . . . 4.2.1 Programmeerstijlen . . . . . . . . . . 4.2.2 Typesysteem en typering . . . . . . . 4.2.3 Geheugenbeheer en garbage collection 4.2.4 Basisfunctionaliteit . . . . . . . . . . . 4.2.5 Objectgeori¨enteerd programmeren . . 4.2.6 Generiek programmeren . . . . . . . . 4.2.7 Andere . . . . . . . . . . . . . . . . . 4.2.7.1 Robuustheid . . . . . . . . .
pagina v
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
88 88 88 88 89 89 90 90 91 91 91 91 92 92 92 93 93 93 93 93 94 94 94 95 95 95 95 95
. . . . . . . . . . .
97 97 97 97 97 98 99 99 99 100 100 100
INHOUDSOPGAVE
4.3 4.4
II
INHOUDSOPGAVE
4.2.7.2 Attributen en annotaties 4.2.7.3 Reflectie . . . . . . . . . 4.2.7.4 Threading . . . . . . . . Compilatie, installatie en uitvoering . . . Ondersteuning . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
Praktische proeven
100 100 101 101 101
102
5 Inleiding 5.1 Applicaties . . . . . . . . . . . . . . 5.1.1 GUI applicatie : Sokoban . . 5.1.2 Batch applicatie : FileDigger 5.1.3 Server applicatie : Chatroom 5.1.4 Andere applicaties . . . . . . 5.2 Hardware . . . . . . . . . . . . . . . 5.3 Software . . . . . . . . . . . . . . . . 5.3.1 C++ . . . . . . . . . . . . . . 5.3.2 C# . . . . . . . . . . . . . . 5.3.3 Java . . . . . . . . . . . . . . 5.3.4 D . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
6 GUI Applicatie : Sokoban 6.1 Inleiding . . . . . . . . . . . . . . . . . . . . 6.2 Functionaliteit . . . . . . . . . . . . . . . . 6.3 Specificatie . . . . . . . . . . . . . . . . . . 6.4 C++ . . . . . . . . . . . . . . . . . . . . . . 6.4.1 Ontwerp . . . . . . . . . . . . . . . . 6.4.2 De taal . . . . . . . . . . . . . . . . 6.4.3 IDE : DevC++ . . . . . . . . . . . . 6.4.4 Bibliotheken . . . . . . . . . . . . . 6.4.4.1 wxWidgets . . . . . . . . . 6.5 C# . . . . . . . . . . . . . . . . . . . . . . . 6.5.1 Ontwerp . . . . . . . . . . . . . . . . 6.5.2 De taal . . . . . . . . . . . . . . . . 6.5.2.1 Syntax . . . . . . . . . . . 6.5.2.2 Broncode en documentatie 6.5.2.3 Parti¨ele klassen . . . . . . 6.5.2.4 Preprocessor Directieven . 6.5.2.5 Memory management . . . 6.5.2.6 RAII . . . . . . . . . . . . 6.5.2.7 Drag ’n Drop . . . . . . . .
pagina vi
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
103 103 103 103 104 104 104 104 104 105 106 106
. . . . . . . . . . . . . . . . . . .
108 108 108 108 109 109 110 111 112 112 115 115 116 116 116 116 116 117 117 117
INHOUDSOPGAVE
6.5.3
6.5.4
6.6
6.7
6.5.5 Java 6.6.1 6.6.2 6.6.3 6.6.4
6.6.5 D. . 6.7.1 6.7.2
6.7.3
6.7.4
INHOUDSOPGAVE
6.5.2.8 Allerlei . . . . . . . . . . . . .NET bibliotheek . . . . . . . . . . . . 6.5.3.1 OpenFileDialog . . . . . . . 6.5.3.2 Datatoegang . . . . . . . . . 6.5.3.3 BindingSource . . . . . . . . 6.5.3.4 Collections . . . . . . . . . . IDE : Microsoft Visual Studio . . . . . 6.5.4.1 Integratie . . . . . . . . . . . 6.5.4.2 WinForms . . . . . . . . . . 6.5.4.3 User Controls . . . . . . . . Microsoft Developer Network (MSDN) . . . . . . . . . . . . . . . . . . . . . . . IDE : Netbeans - Matisse . . . . . . . Ontwerp . . . . . . . . . . . . . . . . . MVC - Events - Listeners . . . . . . . De taal . . . . . . . . . . . . . . . . . 6.6.4.1 Enum . . . . . . . . . . . . . 6.6.4.2 ExtensionFilter . . . . . . . . Bibliotheek : javax.swing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ontwerp . . . . . . . . . . . . . . . . . De taal . . . . . . . . . . . . . . . . . 6.7.2.1 string switch . . . . . . . . . 6.7.2.2 cast . . . . . . . . . . . . . . 6.7.2.3 Gedeeltelijke imports . . . . Bibliotheek : DFL . . . . . . . . . . . 6.7.3.1 DFL PictureBox . . . . . . . 6.7.3.2 Reageren op Events . . . . . IDE : Entice Designer . . . . . . . . .
7 Batch Applicatie : Filedigger 7.1 Inleiding . . . . . . . . . . . . . 7.1.1 Functionaliteit . . . . . 7.1.2 Specificatie . . . . . . . 7.2 C++ . . . . . . . . . . . . . . . 7.3 C# . . . . . . . . . . . . . . . . 7.3.1 Ontwerp . . . . . . . . . 7.3.2 De taal . . . . . . . . . 7.3.2.1 Properties . . 7.3.2.2 Extensions . . 7.3.2.3 Strings splitten
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
pagina vii
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
118 118 118 118 119 119 119 120 120 120 120 121 121 121 121 122 122 122 122 122 122 123 123 123 123 124 124 124 125
. . . . . . . . . .
126 126 126 127 127 127 127 128 128 128 129
INHOUDSOPGAVE
INHOUDSOPGAVE
. . . . . . . . . . . . . . . . . .
129 130 131 131 131 131 132 132 132 133 133 133 133 133 134 134 134 134
. . . . . . . . . . . . . . . .
136 136 136 136 136 137 137 137 137 137 137 139 139 139 140 140 140
9 Besluiten 9.1 Algemene beschouwing van de talen . . . . . . . . . . . . . . . . . . . . . . . . . 9.1.1 C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.1.2 C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
141 141 141 141
7.4
7.5
7.3.3 IDE: Microsoft Visual Studio . . . . 7.3.4 .NET bibliotheek . . . . . . . . . . . Java . . . . . . . . . . . . . . . . . . . . . . 7.4.1 Ontwerp . . . . . . . . . . . . . . . . 7.4.2 De taal . . . . . . . . . . . . . . . . 7.4.2.1 Generics . . . . . . . . . . 7.4.2.2 JUnitTests . . . . . . . . . 7.4.2.3 Java Heap Space . . . . . . 7.4.3 Bibliotheek . . . . . . . . . . . . . . 7.4.3.1 Partities van het systeem . D. . . . . . . . . . . . . . . . . . . . . . . . 7.5.1 Ontwerp . . . . . . . . . . . . . . . . 7.5.2 De taal . . . . . . . . . . . . . . . . 7.5.2.1 Unittests . . . . . . . . . . 7.5.2.2 Output . . . . . . . . . . . 7.5.2.3 Frequentietabellen . . . . . 7.5.3 Bibliotheek . . . . . . . . . . . . . . 7.5.3.1 Paden hi¨erarchisch scannen
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
8 Netwerk Applicatie : Chatroom 8.1 Inleiding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.1.1 Functionaliteit . . . . . . . . . . . . . . . . . . . . . 8.1.2 Specificatie . . . . . . . . . . . . . . . . . . . . . . . 8.2 C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.3 C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.3.1 Ontwerp . . . . . . . . . . . . . . . . . . . . . . . . . 8.3.2 De taal . . . . . . . . . . . . . . . . . . . . . . . . . 8.3.2.1 Struct . . . . . . . . . . . . . . . . . . . . . 8.3.3 .NET bibliotheek . . . . . . . . . . . . . . . . . . . . 8.3.3.1 AsyncCallback . . . . . . . . . . . . . . . . 8.3.3.2 .NET Remoting . . . . . . . . . . . . . . . 8.4 Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.4.1 Ontwerp . . . . . . . . . . . . . . . . . . . . . . . . . 8.4.2 Bibliotheek . . . . . . . . . . . . . . . . . . . . . . . 8.4.2.1 ObjectOutputStream - ObjectInputStream 8.5 D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
pagina viii
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
INHOUDSOPGAVE
9.2
9.3
III
INHOUDSOPGAVE
9.1.3 Java . . . . . . . . . . . . . 9.1.4 D . . . . . . . . . . . . . . . De talen . . . . . . . . . . . . . . . 9.2.1 Geheugenbeheer en garbage 9.2.2 Basisfunctionaliteit . . . . . 9.2.3 OO . . . . . . . . . . . . . 9.2.3.1 Parti¨ele klassen . 9.2.4 Generiek programmeren . . Ondersteuning . . . . . . . . . . . 9.3.1 Bibliotheken . . . . . . . . 9.3.2 Documentatie . . . . . . . . 9.3.3 IDE . . . . . . . . . . . . . 9.3.4 Onderhoudbaarheid . . . . 9.3.5 Andere . . . . . . . . . . . 9.3.5.1 Versioning . . . . 9.3.5.2 RAII . . . . . . . 9.3.5.3 Extensions . . . .
. . . . . . . . . . . . . . . . . . collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Conclusie en future work
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
142 142 142 142 143 143 143 143 143 143 143 144 144 144 144 144 144
145
10 Conclusie
146
11 Future Work
147
pagina ix
Deel I
Theorie
pagina 1
Hoofdstuk 1. Inleiding: De context
Hoofdstuk 1
Inleiding: De context ”Every programmer knows there is one true programming language. A new one every week.” Brian Hayes in [52].
1.1
Doelstelling
Het onderwerp van dit eindwerk betreft het maken van een vergelijkende studie van de programmeertalen C++, C#, Java en D. Meer over het waarom en hoe we de talen zullen vergelijken kan u vinden in 1.9.1. Voor we beginnen te vergelijken, zullen we in deze inleiding dan ook even een breder kader schetsen waarin we zullen werken. Vragen zoals: • Wat zijn programmeertalen? • Welke soorten programmeertalen zijn er? • Waar kunnen we de gebruikte programmeertalen situeren? • Welke criteria kunnen we gebruiken om programmeertalen te vergelijken? zijn hier aan de orde. Na het bekijken van deze situerende vraagjes, kunnen we verder spitten in de talen C++, C#, Java en D en het eigenlijke onderzoek starten. Uitgaande van theoretische en praktische kennis zullen we hierbij een aantal praktische applicaties implementeren waaraan naderhand deze programmeertalen hopelijk hun verschillen en/of gelijkenissen zullen prijsgeven.
1.2
Wat is een taal?
Volgens het Van Dale woordenboek: ”Het op basis van een universeel, aangeboren vermogen gevormde geheel van tekens en regels om deze tot betekenisdragende elementen te combineren waarvan de mens gebruik maakt, om zijn gedachten te articuleren, zijn wereld te ordenen en te communiceren”
pagina 2
1.3 Wat is een programmeertaal?
Hoofdstuk 1. Inleiding: De context
Een taal maakt dus vaak gebruik van een alfabet. Of dit letters of figuren kunnen zijn laten we hier in het midden. Dit alfabet bevat de symbolen waarmee men woorden (in geval van letters) of andere betekenisdragende elementen kan bouwen. De symbolen op zich hebben geen echte betekenis. De woorden duiden op bepaalde begrippen en moeten voldoen aan spellingsregels. Deze woorden kunnen gegroepeerd worden tot zinnen die eveneens moeten voldoen aan grammaticaregels. Naast deze regels om een correcte zin op te bouwen, is er ook nog het begrip semantiek of de betekenis van de zin. Dit bepaalt de de mate waarin een zin effectief ’zinvol’ is. Talen zijn er in overvloed. Zo schat men het aantal talen in de wereld meestal tussen de 4000 en 6000. Het niet correct kunnen bepalen en/of benaderen van dit aantal heeft grotendeels te maken met het bestaan van dialecten en varianten. [52]
1.3
Wat is een programmeertaal?
Het bovenstaande verhaal kan ook toegepast worden op de programmeertalen. Zo maakt men ook gebruik van bepaalde woordenschat om de code voor een programma op te bouwen. Deze code moet voldoen aan een bepaalde syntax en moet met andere woorden syntactisch juist zijn naar analogie met de grammaticaregels. De code heeft echter ook een bepaalde semantiek of betekenis. Dit is hetgeen men verlangt dat het stuk code doet. Een stuk code kan namelijk syntactisch correct zijn, terwijl de semantiek niet de gevraagde is. Zo zal onderstaand Java fragment zeker compileren maar in plaats van de som berekend het het verschil van twee getallen. 1 2 3
public int sum ( int x , int y ) { return x − y ; } Het doel van een programmeertaal is aan een computer opdrachten doorgeven die hij slechts op ´e´en manier kan interpreteren en uitvoeren. Een natuurlijke taal is door zijn hogere niveau en ambiguiteit dan ook niet geschikt als programmeertaal. Onder programmeertalen worden deze verstaan die Turing-volledig zijn. Turing-volledigheid houdt verband met de berekenbaarheid van de programmeertaal. In een niet-Turing volledige programmeertaal kan je niet alles berekenen. Bv. SQL kan niet de kortste route bepalen tussen 2 punten. Programmeertalen die Turing volledig zijn, zijn normaal ook equivalent in die zin dat je elk programma in de ene programmeertaal ook in de andere kan schrijven. In de loop der jaren zijn net als bij de natuurlijke talen onnoemelijk veel soorten en generaties programmeertalen en hun bijhorende dialecten ontstaan. Sommigen beweren dat er wel 2500 verschillende soorten bestaan, anderen gaan nog veel verder en zetten het aantal op 8500. Hierbij dient men toch even op te merken dat deze evolutie zich in slechts 50 jaar ontwikkelde [52]. Verder in deze tekst zullen we, tenzij expliciet aangegeven, de begrippen programmeertalen en talen door elkaar gebruiken en bedoelen we steeds programmeertalen en dus niet de natuurlijke talen.
pagina 3
1.4 Ontstaan van programmeertalen
1.4
Hoofdstuk 1. Inleiding: De context
Ontstaan van programmeertalen
Mensen of programmeurs zijn steeds op zoek naar die ene taal die alles zal kunnen oplossen. Steeds verbeteren we een voorgaande taal of proberen we dit toch met als resultaat een nieuwe taal. Zou het niet veel eenvoudiger zijn om ´e´en algemene taal te ontwikkelen die alles kan? E´en taal met ´e´en syntax zodat iedereen hetzelfde schrijft? Sommigen beweren dat deze overvloed aan programmeertalen en hun incompatibiliteit wel eens een bedreiging zou kunnen vormen voor de verdere vooruitgang in de computerwereld. Het antwoord is meestal n´og een andere programmeertaal voor te stellen. [52] Het ontwerp van een programmeertaal is meestal een complexe bezigheid die verschillende afwegingen en compromissen vraagt. Het is vaak onderworpen aan de noden van het moment, de reeds bestaande omgeving en standaarden en natuurlijk niet in het minst belangrijk, de geschiedenis. Er bestaan zoals gezegd reeds vele programmeertalen. Deze kunnen onderverdeeld worden in verschillende generaties.
1.5
Programmeertalen catalogiseren
Zoals reeds duidelijk is de lijst met programmeertalen lang en onoverzichtelijk. We proberen dan ook orde te scheppen in de chaos. Talen worden onderverdeeld in verschillende categorie¨en en hanteren hiervoor welbepaalde criteria. Deze criteria zijn zelf ook heel uiteenlopend en leiden dus tot verschillende soorten indelingen. Voorbeelden van criteria: • programmeerparadigma’s • typering • compilatie versus interpretatie • features • low-level versus high-level talen C++, C#, Java en D zijn in de eerste plaats high-level talen en is een criterium dat hier niet van toepassing is. Welke features een taal ondersteunt is hier wel belangrijk en daar wordt in hoofdstuk 3 uitgebreid op ingegaan. Voor de andere criteria geven we hier een korte inleiding waarop we onze vergelijking baseren in de verdere tekst.
1.6
Programmeerparadigma’s
Een veelgebruikte manier om een onderscheid te maken tussen verschillende programmeertalen is op basis van de programmeerparadigma’s die ze ondersteunen. Maar wat wordt nu precies bedoeld met een ”programmeerparadigma”? Volgens het Van Dale woordenboek:
pagina 4
1.6 Programmeerparadigma’s
Hoofdstuk 1. Inleiding: De context
”Het geheel van wetenschappelijke prestaties van voorgangers dat door onderzoekers op een bepaald gebied op een bepaald moment in de ontwikkeling van een wetenschap als maatgevend wordt beschouwd. In de filosofie is een paradigma een samenhangend stelsel van modellen en theorie¨en die een denkkader vormen waarmee de werkelijkheid geanalyseerd wordt.” We kunnen een programmeerparadigma dus zien als een model met achterliggende theorie¨en dat gebruikt wordt om te programmeren. Meer concreet bedoelen we dus een manier van denken over problemen en hoe ze op te lossen eerder dan gewoon een verzameling van features. We geven nu een kort overzicht van de belangrijkste bestaande en voor ons van toepassing zijnde programmeerparadigma’s. Na veel opzoekwerk kwamen we trouwens tot de conclusie dat niet iedereen op dezelfde manier denkt over programmeerparadigma’s, maar we proberen toch een zo correct mogelijk beeld te schetsen. Op het hoogste niveau kunnen we de paradigma’s opdelen in twee basiscategorie¨en, nl. de imperatieve en declaratieve. Kort gezegd beschrijven imperatieve talen hoe iets moet gebeuren en declaratieve talen wat er moet gebeuren. Over het algemeen worden declaratieve talen als hoger-niveau talen beschouwd omdat ze dichter bij de menselijke taal liggen [58, 80, 73]. Deze twee categorie¨en zijn in het kader van dit onderzoek de belangrijkste, maar daarnaast bestaan er nog een aantal paradigma die daarbuiten vallen. Het generiek paradigma ondersteunt een vorm van programmeren waarbij algoritmes worden geschreven in een bepaalde syntaxis waardoor de algoritmes adaptief zijn en kunnen worden ge¨ınstantieerd door de compiler [124]. De algoritmes beschrijven hoe ze moeten werken maar niet op welke types. Er kunnen eventueel wel beperkingen aan opgelegd worden. Het aspect-geori¨ enteerd paradigma wordt gebruikt als een uitbreiding op andere bestaande paradigma’s, maar het concept past qua structuur het best bij het object-geor¨ıenteerde. Het is bedoeld om het probleem van crosscutting concerns op te lossen. Croscutting concerns zijn handelingen die door het hele programma heen uitgevoerd moeten worden. Een voorbeeld hiervan is logging. [136] We geven nu wat meer uitleg over de familie van imperatieve en declaratieve paradigma’s. Voor een uitgebreider overzicht van de verschillende paradigma’s verwijzen we naar [122].
1.6.1
Imperatief
Een overzicht van de imperatieve paradigma’s is te zien in figuur 1.1. 1.6.1.1
Puur Imperatief
Een pure imperatieve taal maakt gebruik van opeenvolgende statements die de toestand van het programma veranderen. Die statements zijn commando’s die de hardware direct ´e´en na ´e´en kan uitvoeren. We spreken hier dus van talen zonder lussen, methoden, ... In praktijk gebruiken imperatieve talen variabelen die de toestand van het programma bevatten en operaties die deze toestand incrementeel veranderen. Het puur imperatieve paradigma is hier
pagina 5
1.6 Programmeerparadigma’s
Hoofdstuk 1. Inleiding: De context
Figuur 1.1: Imperatieve versus declaratieve paradigma’s eerder conceptueel bedoeld, want voor zover we weten bestaat er geen hoog niveau taal die zo werkt. 1.6.1.2
Procedureel
Procedurele programmeertalen zijn imperatieve talen die gebruik maken van procedures. De kern van het procedurele paradigma is de procedure. Deze abstractie associeert een naam aan een combinatie van acties die met ´e´en commando opgeroepen kunnen worden. Een eenvoudig maar belangrijk voordeel tegenover imperatieve talen is dus dat stukken code en hun interne variabelen afgeschermd kunnen worden door ze in aparte procedures te steken. Dit maakt de code veel overzichtiger en boilerplate code wordt vermeden. Enkele voorbeelden: • FORTRAN • Algol • Pascal • C 1.6.1.3
Object-geori¨ enteerd
Object-geori¨enteerde programmeertalen zijn procedurele talen die gebruik maken van objecten. De idee bij object-ori¨entatie is elementen uit de re¨ele wereld voor te stellen door objecten en hun eigenschappen en gedrag vast te leggen respectievelijk door attributen (variabelen) en methoden (functies). De kern van het object-geori¨enteerde paradigma is dus het object. Deze abstractie verzamelt
pagina 6
1.6 Programmeerparadigma’s
Hoofdstuk 1. Inleiding: De context
stukken imperatieve code en de data waarop ze werken in objecten. Men spreekt van data encapsulatie Een object is een instantie van een bepaalde klasse. Een klasse is de abstracte voorstelling van een object. Er kunnen meerdere objecten van een bepaalde klasse gedefinieerd zijn. Elk object dat een instantie is van een bepaalde klasse heeft dezelfde soort attributen. De waarde van deze attributen - die wel verschillend kunnen zijn van object tot object - stellen de status of toestand van het object voor. Enkele voorbeelden: • Smalltalk • Java • C# Smalltalk is puur object-geori¨enteerd maar Java en C# beschikken naast objecten ook nog over primitieve types. C++ en D ondersteunen ook het object-geori¨enteerde paradigma, maar dit zijn multi-paradigma1 talen. In sectie 3.2 wordt object-ori¨entatie meer in detail uitgelegd.
1.6.2
Declaratief
Een overzicht van de declaratieve paradigma’s is te zien in figuur 1.1. 1.6.2.1
Functioneel
Functionele programmeertalen zijn gebaseerd op wiskundige functies. De kern van het functionele paradigma is de functie. Deze abstractie wordt in functionele talen beschouwd als een eerste-klasse object. Een functie kan bijvoorbeeld als parameter van een andere functie optreden. De functie werkt als een soort blackbox die parameters aanvaardt als input en waarden teruggeeft als output. Belangrijk punt hierbij is dat de berekening enkel afhangt van de inputparameters en enkel de outputwaarde als gevolg heeft. Er zijn in theorie geen andere randeffecten en er hoeft dus geen toestand van de gehele machine worden bijgehouden. Redeneren is hierdoor eenvoudiger. Nog typisch in het functionele paradigma is dat veel gebruik wordt gemaakt van recursie eerder dan van iteratie en dat variabelen eens ge¨ınstantieerd niet meer van waarde veranderen. Enkele voorbeelden • ML • Haskell 1
Met multi-paradigma programmeren wordt bedoeld dat er gebruik gemaakt wordt van verscheidene programmeerparadigma. Een multi-paradigma taal veronderstellen we hier als een taal die meerdere programmeerparadigma ondersteunt. Met ondersteunen bedoelen we hier het eenvoudig maken om te programmeren op de manier zoals het paradigma het voorstelt. Dit is niet hetzelfde als het mogelijk maken van een paradigma, wat enkel wijst op het mogelijk zijn. Het is dan ook vaak onnodig moeilijk om hetzelfde effect te verkrijgen [92].
pagina 7
1.7 Typering
1.6.2.2
Hoofdstuk 1. Inleiding: De context
Logisch
Logische programmeertalen zijn gebaseerd op de predikaatcalculus. De kern van het logische paradigma bestaat uit feiten en regels. Samen vormen zij een regelsysteem. Het regelsysteem zal door deductie trachten beweringen aan te tonen of te weerleggen. Tijdens zo een deductieproces worden telkens nieuwe feiten uit gekende feiten geresolveerd. Dit gaat zo door tot de gevraagde bewering al dan niet bewezen is. Enkele voorbeelden • Prolog • G¨ odel
1.6.3
Besluit
De declaratieve paradigma’s maken gebruik van een hoog-niveau abstractie als model. Imperatieve talen zijn in hun vorm sterk verbonden aan het berekeningsmodel van Turing en de onderliggende hardware. Hierin onderscheiden zij zich van functionele programmeertalen, die het Churchmodel van functietoepassing gebruiken als model van berekening (zie ook lambdacalculus). Functionele programmeertalen zijn niet-procedureel. Functionele talen met het Churchmodel onderscheiden zich van de logische programmeertalen, waarin het bewijsobject en de predicatencalculus als berekeningsmodel centraal staan [108].
1.7 1.7.1
Typering Typesystemen
Programma’s maken gebruik van variabelen. Variabelen kunnen een bepaald type hebben, maar dit hoeft niet steeds. Types zijn een manier om soorten data in te delen in groepen. Het typesysteem is het systeem van alle mogelijke types die een programmeertaal gebruikt. Dit systeem kan ingebakken in de taal zitten of kan zich zoals in .NET buiten de taal bevinden. .NET zorgt voor een typeringssysteem dat alle talen die op .NET kunnen draaien gebruiken.
1.7.2
Typecontrole
Het is belangrijk dat bij het uitvoeren van computerprogramma’s de variabelen van het juiste type zijn en correct gebruikt worden. Hiermee kan men bepaalde fouten vermijden. De typecontrole staat in voor het controleren van de variabelen en hun type. Ook de operaties tussen verschillende variabelen worden gecontroleerd. Afhankelijk van de gebruikte programmeertaal wordt bij detectie van een ongedefinieerde operatie een bepaalde actie ondernomen.
1.7.3
Statisch versus dynamisch getypeerd
Het moment waarop de typecontrole plaatsvindt, is hier verschillend. Er dient hier een onderscheid te worden gemaakt tussen de compilatie (compile time) en de uitvoering (runtime). Het
pagina 8
1.7 Typering
Hoofdstuk 1. Inleiding: De context
al dan niet bekend zijn van het type van variabelen bij compilatie en aldus de mogelijkheid om typecontrole uit te voeren, is de kern van de zaak. Bij de statisch getypeerde talen kan men een onderscheid maken tussen manifeste en type-inferentie getypeerde talen. Bij manifest getypeerde talen krijgen de variabelen hun type van de programmeur. Deze geeft bij de declaratie van de variabele het type aan. Het type is dan expliciet aangegeven en dus bekend ten tijde van compilatie (at compile time). Dit heeft als gevolg en voordeel dat de compiler tijdens het compileren reeds kan controleren of de variabele wel van het juiste type is en zodoende fouten kan opvangen. Het type bindt zich hierbij aan een variabele. We willen bijvoorbeeld een object Appel aan een variabele van het type Peer toewijzen. Het is echter onmogelijk om dit te doen. De compiler zal bij compilatie het type controleren en een foutmelding geven. Een bijkomend voordeel zijn optimalisaties en de mogelijkheid tot het apart controleren van modules. De optimalisaties zijn mogelijk aangezien men de types bij compilatie weet. Het nadeel voor de statisch getypeerde talen is meer werk voor de programmeur die overal expliciet het type moet aangeven. Verder zijn er ook talen die gebruik maken van type-inferentie, waardoor het type van een variabele uit de context wordt afgeleid. De context bestaat hier uit de tekstuele expressies in de broncode. De voordelen blijven behouden alsook het bijkomende voordeel dat het type niet expliciet moet worden aangegeven. Dynamische talen volgen voorgaand stramien niet. Een variabele hoeft tijdens compilatie nog geen type te hebben. Dit wordt dan wel tijdens de uitvoering bepaald. Typecontrole kan dan echter niet bij compilatie gebeuren en eventuele fouten zullen pas tijdens de uitvoering tevoorschijn komen. Variabelen kunnen tijdens de uitvoering ook van type veranderen. De types binden zich hier aan data in plaats van aan een specifieke variabele. Deze manier geeft ons wel meer flexibiliteit en compactheid. Dynamische talen worden dan ook vaak gebruikt voor prototyping 2 . Opdat we de types tijdens runtime zouden kennen, is het nodig dat dynamische talen een soort boekhouding bijhouden van de variabelen en de op dat moment bijhorende types. We zeggen dat we de variabele en zijn type samen boxen. Daar de types bij statische talen reeds gekend en vast zijn bij compilatie lijkt het niet nodig om ook hier te gaan boxen. In praktijk gebeurt dit vaak wel omwille van bijvoorbeeld polymorfisme (zie sectie 3.2.2), wat mogelijk is door dynamische binding in de compiler. [108]
1.7.4
Sterk versus zwak getypeerd
Er is vaak veel onenigheid in de literatuur over wat nu sterk en zwak getypeerde talen zijn. De gebruikte definities verschillen van bron tot bron of staan orthogonaal ten opzichte van elkaar. Veelal is er ook verwarring met de begrippen uit de voorgaande sectie. Programmeertalen worden aan de hand van deze verschillende definities dikwijls ook anders geklasseerd. We pogen zelf 2
Prototyping is het proces waarbij snel een werkend model in elkaar gestoken wordt met de bedoeling van allerlei aspecten te testen, idee¨en te illustreren of feedback te verzamelen.
pagina 9
1.7 Typering
Hoofdstuk 1. Inleiding: De context
een lijn te vinden in de wirwar aan interpretaties. Het staat vast dat dit een andere manier van indelen is. We veronderstellen dat de types van de variabelen geweten zijn. Het al dan niet gekend zijn bij compilatie staat hier los van. De kern van de zaak is de manier waarop typeringsregels tijdens de uitvoering van het programma strikt nageleefd worden of niet. We beschouwen talen als sterk getypeerd indien ze eisen dat de regels strikt nageleefd worden. Een variabele is van een bepaald type. Er kan niet zomaar van type gewisseld worden. Verder mogen er geen operaties gebeuren op incompatibele types. We zouden bijvoorbeeld twee variabelen kunnen declareren, ´e´en van het type string en ´e´en van het type int. Het is echter onmogelijk om zonder omwegen deze 2 variabelen op te tellen en aan een nieuwe variabele van het type int toe te wijzen. De compiler zou een fout genereren omwille van incompatibele types. Een achterpoortje dat hierbij vaak gebruikt wordt, is de expliciete cast. Tegenover de sterk getypeerde talen staan de zwak getypeerde talen waarbij bepaalde ongedefinieerde operaties toch toegelaten worden en een variabele tijdens de uitvoering verschillende types kan hebben. Men zou hier dus een string ”64”bij een int met waarde 3 kunnen optellen en aan een variabele toewijzen. Het programma kan niet zeggen naar welk type de variabele momenteel wijst. Vaak wordt dan ook gebruik gemaakt van impliciete typeconversies en adhoc polymorfisme (overloading) of beide mechanismen tegelijk. Het voordeel hiervan is dat de programmeur niet steeds zelf een expliciete cast of typeconversie moet voorzien. Dit gebeurt automatisch tijdens de uitvoering. Een voordeel hiervan is dat de programmeur sneller kan ontwikkelen. De taal zorgt dan zelf voor een behoorlijk resultaat in plaats van een foutmelding. Afhankelijk van de gebruikte taal kunnen er verschillende oplossingen voorkomen [108].
1.7.5
Safe versus unsafe getypeerd
Talen worden als safe beschouwd indien ze geen operaties toelaten die fouten kunnen veroorzaken en dus niet typeveilig zijn. Dit wil zeggen dat code niet wordt toegelaten om bijvoorbeeld lowlevel operaties zoals het declareren en gebruiken van pointers te bevatten. Ook conversies tussen pointers en integrale types, en het gebruik van adressen van variabelen is niet toegelaten. C++, C# en D laten het gebruik van pointers toe en kunnen dus als unsafe worden beschouwd. Voor C# dient wel te worden opgemerkt dat het gebruik van pointers expliciet aangegeven moet worden als unsafe, zowel in de code als bij het compileren. Dit legt de verantwoordelijkheid voor eventuele fouten dan ook bij de programmeur, aangezien hij verondersteld wordt te weten wat hij doet. De typeveiligheid van een taal waar het hier dus om draait kan sterk of zwak en statisch of dynamisch zijn. Het is een vaak voorkomende misvatting om te veronderstellen dat zwakke en dynamische typering hand in hand gaan met een gebrek aan veiligheid. C++ dat sterk getypeerd is kan met een minimaal aantal lijnen code gecrasht worden.
pagina 10
1.8 Compilatie versus interpretatie
1.7.6
Hoofdstuk 1. Inleiding: De context
Nominatief versus structureel getypeerd
In een typeersysteem zijn er steeds 2 belangrijke predikaten die een degelijk antwoord vragen. Dit is het equivalentiepredikaat, waarbij we ons afvragen of, gegeven twee expressies, deze expressies equivalent zijn. We vragen ons in de context van typering dus af of ze hetzelfde type hebben. Verder beschouwen we het subtype predikaat dat, gegeven twee expressies, vraagt of de ene expressie een subtype van de andere is [66]. Bij structureel getypeerde systemen wordt gekeken naar de structuur van de types. Indien twee types dezelfde structuur hebben worden ze als equivalent beschouwd. Bij nominaal getypeerde systemen is dit niet voldoende. De twee types moeten hier dan ook dezelfde naam hebben. Voor het subtype predikaat, zal bij structurele systemen opnieuw naar de structuur worden gekeken. Stel dat mijn tweede type een attribuut toevoegt ten opzichte van mijn eerste type. Voor de rest behoudt het wel dezelfde structuur. Het structurele typesysteem stelt dan dat dit tweede type een subtype van het eerste is. Bij gebruik van een nominatief typesysteem is dit niet voldoende. De programmeur dient daar expliciet aan te geven dat het tweede type een subtype is van het eerste (bv. via overerving). Statisch getypeerde talen zijn meestal ook nominatief getypeerd. Dynamische getypeerde talen zijn dan weer structureel getypeerd. Het runtime typecontrole systeem maakt veelal gebruik van duck typing, wat eigenlijk op structureel typeren neerkomt. Als het kwaakt als een eend en het loopt als een eend, dan zal het een eend zijn. We kijken hier dus naar de structuur van het type. Als de structuur overeenkomt, dan zal het ook wel dit type zijn [66]. Natuurlijk zijn er ook hybride talen. Zo is C++ nominatief getypeerd voor de basis en structureel getypeerd voor het templategedeelte. Vaak wordt ook gebruik gemaakt van type aliasing, waarbij men aan een type verschillende namen kan geven. Dit wordt onder andere gebruik in C++ via typedef en is ook aanwezig in C#. Voor Java werd dit aan de wish-list van de volgende versie toegevoegd. Welk van de twee is nu het beste systeem? Elk heeft zijn voor- en nadelen. Zo is het gestructureerde systeem natuurlijk flexibeler dan het nominatieve. Zo zal bij de creatie van een nieuw supertype dit bij het nominatieve systeem een aanpassing vragen van de subtypes daar je dit expliciet moest aangeven als zijnde een subtype. Gestructureerde systemen zijn wel minder type-safe. Toevallige type-equivalentie kan hierbij voorkomen.
1.8
Compilatie versus interpretatie
De overgang van broncode naar uitvoerbare code kan gebeuren aan de hand van verschillende technieken. Voorbeelden van zulke technieken zijn compilatie en interpretatie. Er is echter noch een heel spectrum aan mogelijkheden tussen deze twee. Dit is afhankelijk van de omvang van broncodeanalyse voorafgaand aan de uitvoering van het programma.
pagina 11
1.8 Compilatie versus interpretatie
Hoofdstuk 1. Inleiding: De context
Compilatie C++ en D zijn gecompileerde talen3 . Een programmeertaal wordt gecompileerd door een compiler. Een compiler is een computerprogramma (of een set van programma’s) die de broncode geschreven in een bepaalde programmeertaal zal vertalen in een andere computertaal, de doeltaal. Deze output wordt ook de object code genoemd. Deze code is klaar om nog door andere programma’s zoals een linker te worden verwerkt. De linker zal er eenvoudig gezegd voor zorgen dat de verschillende modules (indien deze er zijn natuurlijk) aan elkaar gelinkt worden. Ook de nodige bibliotheken worden dan toegevoegd om uiteindelijk een uitvoerbaar bestand als output te verkrijgen. Wat de compiler dus hoofdzakelijk doet, is broncode van een hoge niveau programmeertaal omzetten naar een laag niveau taal zoals een assemblytaal of machinetaal. Een programma dat het omgekeerde doet, noemt men een decompiler. JIT compilatie Java en C# maken gebruik van Just-In-Time compilatie. JIT compilatie is een techniek om de runtime performantie van computerprogramma’s te verhogen. Het vertaalt, at runtime, code van het ene formaat naar het andere. Een Just-in-time compiler wordt bijvoorbeeld vaak gebruikt om in een bytecode gecompileerd systeem van bytecode over te gaan naar uitvoerbaar bestand in native code. Bytecode is een intermediaire voorstelling van de code en dit tussen de broncode en de uitvoerbare code. Het zorgt voor de platformonafhankelijkheid van de computerprogramma’s. Virtuele machines zoals de Java Virtuele Machine voor Java en de Common Language Runtime voor C#, kunnen via een JIT-compiler deze bytecode net voor de uitvoering omzetten naar native uitvoerbare code. Vandaar de naam Just-in-time compilatie. Dit kan per bestand, per functie of per codefragment gebeuren. Een nadeel van JIT-compilatie is de startup delay. Dit kan worden opgelost door gebruik te maken van preJITting waarbij men de hele bytecode al op voorhand zal gaan omzetten. Dit kan op de schijf worden opgeslagen om later eventueel opnieuw te worden gebruikt. [132] Interpretatie Een interpreter is een computerprogramma dat computerprogramma’s compileert en uitvoert. Dit doet het ”on-the-fly” at runtime. We zeggen dat het programma wordt ge¨ınterpreteerd. Dit heeft een aantal voordelen ten opzichte van compilatie. Het laat ons onder andere toe om de programma-implementatie onafhankelijk van de onderliggende machine te houden. Een nadeel is dat ge¨ınterpreteerde talen vaak trager zijn. [125, 126] 3
Men dient op te merken dat men vaak stelt dat een taal ofwel gecompileerd ofwel ge¨ınterpreteerd wordt. In strikte zin is dit niet altijd zo. Compilers en interpreters zijn slechts implementaties van een taal en niet de taal zelf. De taal zelf legt meestal niet vast dat compilatie of interpretatie vereist is en kan dan ook vaak op de twee manieren benaderd worden. Er zijn natuurlijk altijd uitzonderingen.
pagina 12
1.9 Vergelijken van programmeertalen
1.9 1.9.1
Hoofdstuk 1. Inleiding: De context
Vergelijken van programmeertalen Waarom vergelijken van programmeertalen?
We wensen te weten welke taal nu het meest geschikt is voor welke toepassing. Elke toepassing heeft verschillende menselijke noden die zich vertalen in technische specificaties. Afhankelijk hiervan en afhankelijk van de mogelijkheden die de taal biedt, dient een keuze te worden gemaakt. Wij beschouwen hier zoals gezegd enkel C++, C#, Java en D. Deze lijken sterk op elkaar of zijn sterk door elkaar be¨ınvloed. Aan de andere kant zijn er ook enkel fundamentele verschillen tussen deze talen.
1.9.2
Hoe vergelijken van programmeertalen?
Vergelijken van programmeertalen kan net als het catalogiseren op veel verschillende manieren. We hebben dus nood aan goedgekozen criteria. Waar de ene taal aan de hand van een bepaald criterium goed uit de test komt, zal het bij gebruik van een totaal ander criterium misschien de minder goede taal blijken. Welke criteria moeten we dan kiezen? De doelstelling bepaalt veelal automatisch de keuze voor een criterium. We geven een voorbeeld. Indien we een snelle server bouwen, zal de aandacht naar het gebruik van threads in of de snelheid van de programmeertalen gaan. Indien we een programmeercursus voor beginnende programmeurs opstarten, zal de voorkeur niet naar moeilijke lowlevel talen gaan. Een haalbare leercurve en eventueel eenvoudig syntax spelen hier een belangrijkere rol. Deze voorbeelden geven ons twee totaal verschillende doelstellingen waarvoor programmeertalen vergeleken kunnen worden. De besluiten zijn dan ook totaal anders. Voorbeelden van vaak gebruikte criteria zijn het vergelijken van syntax, features, snelheid, ondersteuning, portabiliteit, stabiliteit, gebruiksgemak, expressiviteit, populariteit, compatibiliteit, onderhoudbaarheid, modules, ... Indien we dus programmeertalen grondig en volledig wensen te vergelijken, biedt het een enge kijk om uit te gaan van slechts ´e´en criterium. We kunnen bijvoorbeeld enkel de features die een taal aanbiedt in aanmerking nemen. Zonder de doelstelling en de context waarin de taal dagelijks gebruikt wordt, is de kans op een vertekend beeld re¨eel.
1.9.3
Problematiek en gevaren bij het vergelijken?
We hebben reeds twee stappen van het proces ontrafeld. Een eerste stap is het duidelijk formuleren van de doelstelling. Een tweede blijkt het kiezen van goede criteria. In beide gevallen kan het mislopen. Indien je niet weet wat de doelstelling is, kan dit foute conclusies tot gevolg hebben. De Sapir-Whorfhypothese of lingustische relativiteit geeft ons een derde punt. Deze hypothese stelt volgens [135] dat de waarneming en voorstelling van de werkelijkheid sterk afhangt van de
pagina 13
1.9 Vergelijken van programmeertalen
Hoofdstuk 1. Inleiding: De context
taal die iemand tot zijn beschikking heeft. Toegepast op onze context wil dit zeggen dat je je idee¨en maar zo goed kan uitdrukken als de taal waarin je denkt het toelaat. Dit is een beperking die onafhankelijk is van features en syntax. De taal be¨ınvloedt als het ware je gedachtenstroom (zwak determinisme). Personen die meerdere talen beheersen hebben dan ook vaak een ander inzicht in bepaalde problemen uit de re¨eele wereld. Verder zal in ons geval de ervaring met een bepaalde taal in rekening worden gebracht. Zo zijn twee van de vier talen nieuwe talen waar we nog nauwelijks of geen ervaring mee hebben, waaronder C# en D. Java is onze moedertaal en C++ hebben we reeds enige ervaring mee. Door deze niet evenredige kennis worden de praktische proeven natuurlijk ook be¨ınvloed. We proberen hier dan ook zo goed mogelijk rekening mee te houden.
1.9.4
Gevolgde werkwijze
De thesis is opgesplitst in twee delen, een theoretisch en praktisch gedeelte. In het theoretische gedeelte worden de verschillende talen onder de loep genomen en de context waarin ze zich bevonden en bevinden uitgelegd. Verder worden ze dan tegenover elkaar gesteld en vergeleken. Hier worden dus theoretische waarden en bestaande vooroordelen en/of eventuele misvattingen samengebracht. In Deel II van deze thesis zullen we dan onze theoretische bevindingen toetsen aan de praktijk. We doen dit aan de hand van implementaties van drie verschillende applicaties. Door het beschouwen van verschillende applicaties testen we verschillende vergelijkingscriteria en krijgen we een bredere kijk op de talen.
pagina 14
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Hoofdstuk 2
Overzicht van de gebruikte programmeertalen 2.1 2.1.1
C++ C++ in het kort
C++ (uitgesproken als ”see plus plus”) is een algemene voor allerhande toepassingen bedoelde, hoog-niveau programmeertaal met laag-niveau mogelijkheden [114]. Het belangrijkste toepassingsgebied volgens Stroustrup in [92] is de systeemprogrammering. Met systeemprogrammering worden de zaken die betrekking hebben op besturingssystemen en fundamentele tools bedoeld. Hiertoe behoren de kernel van het besturingssysteem, drivers van apparatuur, systeemgereedschappen, netwerk, compilers, sommig grafische zaken en GUI1 ’s, databanksystemen, game engines, CAD/CAM, telecommunicatiesystemen, ... 2 . C++ is een statisch getypeerde taal en ondersteunt meerdere paradigma’s waaronder procedureel, object-geori¨enteerd en generiek programmeren (zie sectie 3.3) [114, 92].
2.1.2
Geschiedenis
In het begin van de jaren 1970 zou men in een project van de ”Bell Labs” een nieuw besturingssysteem ontwikkelen. Dennis Ritchie ontdekte dat hierbij nood was aan een programmeertaal die beknopt, snel en van een hoger niveau dan assembler was. Dit leidde tot de ontwikkeling van C [94]. C++ werd later, in 1983, in de AT&T ”Bell Labs” ontwikkeld. Oorspronkelijk werden de voorgaande versies van de taal ”C with classes” (1980) genoemd. Het was een samensmelting van Simula67 met haar objectgeori¨enteerde features en C met haar krachtig en effici¨ent ontwerp. 1
Graphical User Interface. Dit is de grafische omgeving van een toepassing. De gebruiker kan hiermee op een eenvoudigere en duidelijkere wijze interageren met de applicatie, dit in tegenstelling met de commandolijnapplicaties met tientallen commando’s die onthouden dienden te worden [108]. 2 Voorbeelden van toepassingen gemaakt met C++ kan u vinden op http://www.research.att.com/~bs/ applications.html
pagina 15
2.1 C++
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
C++ diende een verbetering ten opzichte van deze talen te zijn. C was niet ontworpen voor objectori¨entatie. Je kan er wel objectgeori¨enteerde ontwerpen in programmeren, maar de implementatie is moeilijk. Simula67 was gewoonweg te traag voor de ”large scale systems” waar men mee bezig was. De verbetering uitte zich in het toevoegen van klassen, virtuele functies, operator overlading, meervoudige overerving, templates en een exceptieafhandelingsmechanisme. Andere invloeden kwamen uit ADA en Clu (CLUster) voor onder andere de ontwikkeling van templates [76]. Cfront was een traditionele compiler die voor het eerst een complete syntax -en semantische analyse kon doen van de C++ code. Hij is geschreven in C++. Daarvoor diende Stroustrup eerst een ”C with classes”-naar-C preprocessor te schrijven. Die preprocessor kon ”C with classes” code vertalen in C [76]. Sinds de jaren 1990 is C++ ´e´en van de meest commercieel succesvolle programmeertalen [114]. Bjarne Stroustrup Bjarne Stroustrup3 is de ontwerper en oorspronkelijke ontwikkelaar van de C++ programmeertaal. Momenteel is hij werkzaam aan de Texas A&M Universiteit. Daarvoor was hij te werk gesteld als hoofd van de Large-scale Programming Research afdeling van AT&T [96]. De naam C++ Deze werd voor het eerst gebruikt in december 1983 en werd bedacht door Rick Mascitti. Voorheen, in het ontwikkelingsstadium van de taal, werd er naar gerefereerd als ”new C” en daarna als ”C with classes”. De uiteindelijke naam C++ komt van de ”++” operator die de waarde van een variabele met 1 verhoogt en een naamgevingsconventie die het ”+” teken gebruikt om een verbeterde versie van een computerprogramma aan te duiden [92].
2.1.3
Filosofie
In [76], beschrijft Bjarne Stroustrup enkele regels die ons helpen begrijpen waarom C++ is zoals het is. Hieronder kan u een korte samenvatting vinden van deze regels. Deze regels kan u in meer detail en aantal bekijken in [76, 35]. Ontwerpdoelstellingen • C++ is ontworpen om een statisch getypeerde, voor allerlei toepassingen bruikbare taal te zijn. Ze diende ook even effici¨ent en portabel als C te zijn. • C++ is ontworpen om meerdere programmeerstijlen te ondersteunen, zowel data-abstractie, objectori¨entatie als generiek programmeren. • C++ is ontworpen voor de programmeur. Deze heeft dan ook een grote vrijheid en dus ook een vrijheid om verkeerde keuzes te maken. • C++ is ontworpen om zo compatibel mogelijk te zijn met C en zodoende een vlotte overgang van C naar C++ te voorzien. 3
Meer informatie kan je vinden op http://www.research.att.com/~bs/bio.html
pagina 16
2.1 C++
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Algemene regels • De evolutie van C++ is gedreven door echte problemen. • C++ moet NU bruikbaar zijn. • Perfectie bestaat niet en de zoektocht naar perfectie dient dan ook vermeden te worden. Ontwerpondersteunende regels • Het is belangrijker om een zinvolle feature toe te voegen dan om elk misbruik te voorkomen. • Alle features mogen niet meer kosten dan ze opbrengen. • C++ vermijdt features die platformafhankelijk of niet voor allerlei toepassingen bruikbaar zijn. • C++ is ontworpen om te werken zonder een uitgebreide programmeeromgeving Taal-technische regels • Geef een even goede ondersteuning voor gebruikersgedefinieerde types als de built-in types. • Lokaliteit is goed. • Preprocessorgebruik moet worden vermeden Regels in verband met laag-niveau programmeerondersteuning • Laat geen ruimte voor een taal met nog lager niveau dan C++ (behalve C en assembler) • C++ heeft geen overhead ten gevolge van features die niet worden gebruikt.
2.1.4 2.1.4.1
De verschillende versies Ontwikkeling van de standaard
Van C++ en haar compilers bestaan er heel wat versies. Voorbeelden zijn Visual C++, Borland C++, Turbo C++, Code Warrior (Mac), Digital Mars C/C++ compiler en ga zo maar door. Deze laten je allemaal toe om C++ programma’s te ontwikkelen, zij het met hier en daar wat onderlinge verschillen. Vaak voorziet elke producent in zijn eigen extensies, die weliswaar verleidelijk om te gebruiken zijn, maar er meestal voor zorgen dat de broncode niet portabel is. Om dan ook te zorgen dat de portabiliteit behouden bleef, ging de American National Standards Institute (ANSI) over tot de ontwikkeling van een standaard. De ISO standaard uit 1998 bestaat uit twee delen. Een eerste deel handelt over de taal C++ zelf. Een tweede deel slaat op de C++ standaardbibliotheek.
pagina 17
2.1 C++
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Versie
Release Datum
C with classes C++ Eerste C++ compiler (Cfront) GNU C++ ANSI C++ C++ 2.0 draft C++ 2.1 Borland C++ Microsoft C++ C++ ISO/IEC 14882:1998 C++ ISO/IEC 14882:2003 C++0x
1979-1980 juli 1983 oktober 1985 1987 1989 1989 1990 1990 1992 1998 2003 In ontwikkeling
Tabel 2.1: C++ Versies 2.1.4.2
Relatie en compatibiliteit met C
Vergeleken met de C taal introduceert C++ een aantal extra features. Hieronder vallen de declaraties als statements, casts die er als functies uitzien, new en delete sleutelwoorden, bool type, referentietypes, inline functies, default argumenten, functieoverlading, namespaces, klassen en alle bijhorende begrippen, operator overlading, templates, de :: operator, exceptiebeheer en RTTI. De term ”object” is vaak een bron van verwarring. In C++ en C duidt men met de term object een aaneengesloten stuk geheugen aan. Dit mag niet verward worden met een klasseinstantie. Enkele features van C++ werden later ook door C overgenomen. Voorbeelden hiervan zijn declaraties in forlussen, C++ commentaarstijl en de inline feature. Ook nieuwe features zoals variadic macros, arrays met variabele lengte, complexgetaltype werden in C99, de ISO standaard van C uit 1999, opgenomen. Deze bestaan niet in C++. C wordt vaak als een subset van C++ gezien. Dit is, op een paar uitzonderingen na, correct. Zo is in C een implicitie conversie van void* naar een ander pointertype mogelijk. In C++ kan dit niet. In C kan je dus schrijven [114]: 1 2
/∗ I m p l i c i e t e c o n v e r s i e van v o i d ∗ naar i n t ∗ ∗/ int ∗ i = m a l l o c ( s i z e o f ( int ) ∗ 5 ) ; maar om het zowel in C als C++ te laten werken dient men van een expliciete cast gebruik te maken:
1
int ∗ i = ( int ∗ ) m a l l o c ( s i z e o f ( int ) ∗ 5 ) ; De meeste verschillen tussen de 2 talen komen voort uit het feit dat in een aantal gevallen C++ meer aan typecontrole doet dan C. Verdere verschillen: • C++ gebruikt ook meer sleutelwoorden dan C doet. In C kunnen deze wel als gewone
pagina 18
2.1 C++
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
variabelennamen gebruikt worden. • Om C en C++ code samen te kunnen gebruiken, moet elke C code die uit C++ opgeroepen dient te worden met het extern sleutelwoord worden aangeduid. • In C++ code wordt voor casts de C++ notatie geprefereerd boven de C notatie. bv. int *i = static cast< int*>(malloc(sizeof(int) * 5));
2.1.5
Compilers, compilatie en uitvoering
C++ is een gecompileerde taal. Broncode wordt hierbij door middel van een compiler vertaald naar machinetaal. Het binaire bestand dat hier het resultaat van is, kan door de computer uitgevoerd worden. Tijdens de compilatie zal een type controle plaats vinden. C++ heeft dus statische typecontrole. Het ontwikkelen van een compiler die voldoet aan de standaard is bewezen geen gemakkelijke opgave te zijn. Reeds vele jaren waren er allerlei compilerimplementaties die elk op hun manier en tot op zeker hoogte aan de standaard voldeden. Recente versies4 voldoen nu bijna allemaal aan de C++ 1998 standaard. [114] C++ programma’s kunnen in een gewone teksteditor gemaakt worden. De headerbestanden dienen te eindigen op ”.h”. De implementatiebestanden krijgen de extensie ”.cpp”. De inhoud moet natuurlijk voldoen aan de C++ syntax. Indien niet, zal de compiler een fout genereren. Bekende C++ compilers zijn CC (Sun) en g++ (GNU). Om een programma te compileren gebruik je dus gewoon het commando g++ met daarna het te compileren bestand. Het uitvoerbare bestand wordt default ”a.out” genoemd. Indien je bij compilatie gebruik maakt van de ”-o”-parameter kan je zelf een naam voor het uitvoerbestand opgeven. Om het bestand dan uit te voeren, geef je gewoon de naam van het gegenereerde bestand in [35].
Figuur 2.1: Compilatie proces Een uitgebreid C++ programma kan ook gesplitst worden in meerdere bronbestanden. De compilatie levert voor elk van deze bestanden een objectbestand op. Dit zijn modules die gecombineerd kunnen worden tot een uitvoerbaar programma. Een objectbestand bevat de namen van de functies en globale variabelen die in het bijhorende bronbestand zijn gedefinieerd. Het 4
Een lijst van compilers, zowel gratis als betalend, kan u vinden op http://www.research.att.com/~bs/ compilers.html
pagina 19
2.1 C++
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
uitvoerbare bestand wordt gemaakt door de modules aan de linker door te geven. Deze probeert dan alle naamverwijzigen in de objectbestanden op te zoeken en associeert deze met de juiste functies en variabelen. Ook C-code kan hier meegelinkt worden. Vaak worden voor dit build en deployproces makefiles gebruikt. Dit zijn files waarin men het hele proces van compileren en linken zal specifi¨eren. Indien men dan het ”make” commando uitvoert met de makefile zal het hele proces automatisch uitgevoerd worden. In C++ vindt er net als in C ook preprocessing plaats. Hierbij zal de code overlopen worden op zoek naar preprocessor directieven. Deze directieven geven enkele instructies door aan de compiler die hij kan gebruiken tijdens de compilatie. Dit wordt vooral gebruikt voor conditionele compilatie en macro expansie. C++ ondersteunt dus net als C conditionele compilatie, waarbij bepaalde stukken code afhankelijk van een bepaalde conditie wel of niet gecompileerd zullen worden. Men kan bv. een DEBUG constante declareren. Indien deze constante bestaat zullen bv. output statements die afhangen van het al dan niet bestaan van deze constante (aangeduid met het #ifndef preprocessor directief) mee gecompileerd en bij het uitvoeren getoond worden. Indien deze niet bestaat zullen deze regels code genegeerd worden. Zo kan men snel bepaalde functionaliteiten uitschakelen. Dit wordt bv. soms gebruikt indien men een trialversie van het programma wenst te maken.
2.1.6
Namespaces
In C++ maakt men gebruik van namespaces of naamruimten. Deze zijn bedoeld om de logische structuur uit te drukken. Ze worden vaak gebruikt om naamsconflicten te vermijden. De namespace hebben meestal een naam, maar ook zonder kunnen ze bestaan. Meerdere namespaces zonder naam worden toch als afzonderlijke delen beschouwd. Indien men vanuit een ander bestand de members van deze namespace wenst te gebruiken, dient men gebruik te maken van het using directief. Namespaces kunnen ook een alias toegewezen krijgen, bv. indien de originele naam te lang zou zijn.
2.1.7
Geheugen
In C en C++ zijn er 3 fundamentele manieren om het geheugen te gebruiken [35]: statisch geheugen Een object gealloceerd in het statisch geheugen is slechts ´e´en keer geconstrueerd en blijft dan tot het einde van het programma in het geheugen. Het adres van het object verandert niet terwijl het programma in uitvoering is. Statische objecten kunnen wel problemen geven indien men gebruik maakt van threads aangezien deze objecten gedeeld zijn. automatisch geheugen Hier zijn de functieargumenten en lokale variabelen gealloceerd. Dit soort geheugen wordt automatisch gecre¨eerd en vrijgegeven. Automatisch geheugen bevindt zich op de stack.
pagina 20
2.1 C++
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
vrij geheugen Objecten die via het programma aangemaakt worden en expliciet om geheugen vragen (via new sleutelwoord) komen in het vrije geheugen terecht. Dit geheugen wordt ook wel eens het dynamisch geheugen of de heap genoemd. Door gebruik van het delete sleutelwoord kan men weer geheugen vrijmaken.
2.1.8
Bibliotheken
Zoals gezegd bestaat de standaard uit 1998 uit twee delen. Het tweede deel beslaat de standaard C++ bibliotheek. Deze bibliotheek omvat het grootste deel van de Standard Template Library (STL)5 en een licht gewijzigde (zodat deze beter met C++ overweg kan) versie van de C standaard bibliotheek. Er bestaan buiten voorgaande nog vele andere bibliotheken voor C++. Deze maken geen deel uit van de standaard. [114] Standard Template Library (STL) Templates bevatten generieke code die overal inzetbaar moet kunnen zijn. De STL is zoals de naam het zegt gebaseerd op templates. De bibliotheek is aangepast als een deel van de ANSI/ISO C++ standaard bibliotheek in 1994. Hij was oorspronkelijk een bibliotheek gemaakt door derden, met name HP en later SGI. De standaard verwijst er ook niet naar als zijnde de STL maar de naam wordt nog door veel mensen gebruikt om naar deze bewerkte standaardbibliotheek te verwijzen. Ook wordt de naam gebruikt om dit aangepaste deel te scheiden van de rest van de bibliotheek (input/output streams, internationalisatie, de C bibliotheek, ...). De bibliotheek bestaat uit vijf grote onderdelen: Containers bv. vector, list, map (associatieve arrays), (multi)set, ... . Iteratoren bv. const iteratoren. Dit zijn gegeneraliseerde pointers. Ze laten toegang tot containers op een zelfde wijze gebeuren als toegang tot een array. Algoritmen Dit zijn algoritmen om bv. te kunnen sorteren of zoeken. Functionele objecten Functionele objecten of functors zijn gegeneraliseerde pointers naar functies. Ze worden gebruikt door de algoritmen als argumenten. Hij is handgecodeerd en heel effici¨ent. Het gebruik ervan kan ons helpen veiliger en meer schaalbare code te schrijven. Adaptoren De meeste C++ compilers voorzien een implementatie van de standaard C++ bibliotheek en de STL. Compileronafhankelijke implementaties van de STL zoals STLPort bestaan ook. 5
Standard Template Library is een software bibliotheek die deel uit maakt van de C++ Standaard Bibliotheek. Het bevat containers, iterators en algoritmes [108].
pagina 21
2.1 C++
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Boost De Boost.org groep probeert het beste te halen uit de huidige versie van C++. Ze zijn actief bezig met het uitbreiden van de functionele -en metaprogrammeringsmogelijkheden van C++. Verder adviseren ze ook het ISO C++ commit´e bij het opstellen van de nieuwe standaard. De Boost bibliotheken zijn een verzameling van opensource bibliotheken die de functionaliteiten van C++ verder uitbreiden. De bibliotheken staan onder licentie van de ”Boost Software License”. Deze licentie zorgt ervoor dat Boost zowel voor open als gesloten projecten gebruikt kan worden. Vele van de Boost oprichters zijn ook lid van het C++ Standaard comit´e en hebben enkele Boost bibliotheken laten accepteren in het ”Technical Report 1” van C++0x. De bibliotheken zijn gericht op een grote gebruikersgroep en tal van applicatiedomeinen. De bibliotkehen gaan van algemene bibliotheken zoals SmartPtr tot besturingssysteemabstracties zoals FileSystem en tot bibliotheken gericht op andere bibliotheekontwikkelaars en geavanceerdeerde C++ gebruikers. In een poging om efficientie en flexibiliteit te garanderen maakt Boost intensief gebruik van templates. Met Boost heeft men heel wat onderzoek naar generiek programmeren en metaprogrammeren in C++ verricht.
2.1.9
Ondersteuning
C++ heeft een uitgebreide ondersteuning in die zin dat er in de loop der jaren heel wat informatie in boekvorm en online verschenen is. De hoeveelheid ervan en de inconsistentie door gebruik van specifieke compilers en omgevingen kunnen soms verwarrend zijn. Ook zijn er veel IDE6 ’s beschikbaar om te programmeren in C++, zowel opensource als betalend.
2.1.10
Toekomst
Terwijl men met Boost nu nog het beste maakt van C++ in zijn huidige vorm is er ook een nieuwe C++ standaard onderweg. De vorige versie van de standaard dateert ondertussen van 1998 met enkele updates in 2003. De nieuwe standaard draagt voorlopig de naam C++ 0X en wordt verwacht in 2009. De X staat voor het nog onbekende jaartal. Voor de invulling van de ’x’ heeft men dus het cijfer 9 op het oog. Hopelijk dient de ’x’ niet hexadecimaal ge¨ınterpreteerd te worden in de vorm van C++ 0A en is de standaard op tijd klaar, aldus Bjarne Stroustrup in [93]. Door gebrek aan bronnen zoals tijd, geld, ervaring, mankracht, ... laat de vooruitgang en het verwerken van de voorstellen soms nogal te wensen over. Het ISO C++ comit´e startte enkele jaren terug met het verzamelen van feedback van C++ programmeurs. Nieuwe idee¨en, plannen en voorstellen werden bekeken en onderzocht. De nieuwe versie zal wel achterwaarts compatibel zijn met C++ 98 en komt bijvoorbeeld inclusief een garbage collector. Ook uit bibliotheken als Boost zijn zaken overgenomen, bv. tupel bibliotheek en threads. [93] geeft een kort overzicht van belangrijke nieuwe features en aanpassingen in de taal en de bibliotheek. Het belang van generiek programmeren komt hierbij duidelijk naar voor. De templates dienen flexibel en performant te zijn. Momenteel is er maar al te vaak ingewikkelde 6
Integrated development environment. Dit is applicatie die de programmeur helpt bij het ontwikkelen van software [108].
pagina 22
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
templatecode nodig. Deze code moet eenvoudiger te lezen, schrijven en te gebruiken worden. Het voorstel voor de nieuwe bibliotheek is beter bekend als TR1 (technical report 1). Het verklaart de nieuwe functionaliteit van deze bibliotheek. De meeste veranderingen zullen zich voordoen in de C++ standaard bibliotheek en niet in de taal zelf. De doelstellingen van de taal zullen dezelfde blijven. Het is geenszins de bedoeling om bijvoorbeeld sommige paradigma’s uit de taal te halen of andere toe te voegen. Stabiliteit en compatibiliteit blijven van groot belang en het weghalen van features zou dit teniet doen. Dit laat enkel nog de keuze voor generalisatie van de bestaande regels en toevoeging van eenvoudig te gebruiken features. Met de nieuwe standaard zal dus geprobeerd worden om newbies7 naar de C++ wereld te leiden. De bedoeling is maw om de huidige C++ flexibeler, krachtiger en eenvoudiger in gebruik, alsook eenvoudiger te leren, te maken. Dit stuit natuurlijk op enig verzet, maar Stroustrup maakt in [93] duidelijk dat deze maatregelen met weinig kosten in termen van performantie of flexibiliteit uitgevoerd kunnen worden. [95, 93, 86]
2.2 2.2.1
C# C# in het kort
C# is een statisch, sterk getypeerde, objectgeori¨enteerde taal die heel wat ambitieuze doelstelling men zich mee bracht. De taal en het .NET framework waar het sterk mee verbonden is, zijn vooral gericht op de vereenvoudiging van concepten uit het verleden. Zo zal componentgebaseerd ontwerp8 een pak eenvoudiger moeten worden. Tevens richt de taal zich op het ontwerp van allerlei toepassingen, met een nadruk op internetgebaseerde toepassingen. [56, 119]
2.2.2
Geschiedenis
C# ontstond met de ontwikkeling van het .NET framework. De taal werd speciaal ontwikkeld om eenvoudig samen te werken met .NET. In [56] vinden we de belangrijkste ontwikkelaars van deze taal, met name Anders Hejlsberg, Scott Wiltamuth en Peter Golde. Volgens [112] is Anders Hejlsberg een Deens software engineer en sinds het jaar 2000 ”lead architect” voor de C# programmeertaal. Voorheen ontwikkelde hij reeds bij Borland de TurboPascal compiler. Sinds 1996 werkt hij bij Microsoft, waar hij zich in eerste instantie bezig hield met J++9 . 7
Hiermee bedoelt men nieuwkomers. In deze context bedoelen we dus de nieuwe en bijgevolg meestal onervaren programmeurs [108]. 8 Component gebaseerd design richt zich op de de compositie van systemen in functionele en logische componenten. Deze hebben een welgedefinieerde interface die gebruik wordt voor de communicatie tussen de componenten. Componenten worden beschouwd als van een hoger abstractieniveau dan objecten in die zin dat ze geen toestand delen en communiceren door het uitwisselen van boodschappen die data bevatten [116]. 9 J++ is de Microsoftversie van Java. Java werd hier geoptimaliseerd voor het Windowsplatform. Gebruik makende van het onderliggende J/Direct framework kan men de Win32 API effici¨enter gebruiken en presteren programma’s dus beter dan wanneer het originele Java framework en de bijhorende api’s worden gebruikt. J++ werd echter niet verder ontwikkeld door een rechtzaak met Sun. Later werd de technologie wel deels gerecycleerd
pagina 23
2.2 C#
2.2.3
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Filosofie
Veel versies van Windows zijn reeds de revue gepasseerd. Bij elke nieuwe versie werden er nieuwe functies aan de bestaande API10 ’s toegevoegd. De complexiteit van de technologie nam steeds toe. Dit uitbreidingsproces ten gevolge van achterwaartse compatibiliteitseisen kon niet eeuwig doorgaan. Met de ontwikkeling van .NET en C# wenste men een nieuwe start te nemen. Het grootste deel van de .NET klassen en gereedschappen zijn dan ook geschreven in C#. De taal werd helemaal van onderen opgebouwd en hield hierbij rekening met bestaande idee¨en, talen en tradities. Microsoft omschrijft C# als een eenvoudige, moderne, objectgeori¨enteerde en typesafe programmeertaal afgeleid van C en C++. Door derden en onder andere in [34] worden Java en ook Delphi nog aan het lijstje toegevoegd. Veel werd dus overgenomen uit deze talen en veel werd geleerd uit fouten en moeilijkheden met het eigen Component Object Model (COM)11 . COM12 is een software architectuur ter ondersteuning van de ontwikkeling van component-gebaseerde toepassingen. Men wil ontwikkelaars dan ook stimuleren om herbruikbare software componenten te laten maken. Tegenhangers van COM zijn de JavaBeans en CORBA. [115] In het pre-C# tijdperk kregen we reeds te maken met de procedurele en objectgeori¨enteerde talen. Met C# probeert men dus nog wat verder te gaan door de softwareontwikkeling weer een stapje dichter te brengen bij het componentgebaseerd design. [34] Uit [56] kunnen we de onderstaande ontwerpdoelstellingen voor C# halen: • C# dient een eenvoudige, moderne, object-geori¨enteerde, voor allerlei soorten toepassingen vatbare programmeertaal. De taal zou dus zowel in een gehoste als embedded omgeving kunnen worden ingezet. Dit kan gaan van heel grote schaal voor besturingssystemen tot heel kleine toepassingen met specifieke functies. • C# dient robuust, duurzaam en productief te zijn. Om aan deze eisen te voldoen moeten toch zeker sterke typering, array bounds controle, controle op niet-ge¨ınitialiseerde variabelen, broncodeportabiliteit en automatische garbage collection door de taal ondersteund worden. • C# heeft de intentie om gebruikt te worden bij de ontwikkeling van softwarecomponenten die gedistribueerde omgevingen kunnen uitbuiten. via de J#-taal. Deze taal werd volledig in India ontwikkeld, maar haalde nooit hetzelfde niveau als C# [143]. 10 Application Programming Interface. Beschrijft de interface tussen een programma en bibliotheken of een computersysteem. De schrijver van het programma weet enkel hoe bepaalde zaken kunnen aangesproken worden zonder de implementatie te kennen [113]. 11 Component Object Model is een platform om software components mee te maken. Het werd geintroduceerd door Microsoft in 1993. Het wordt gebruikt voor interproces communicatie en dynamische objectcreatie [108]. 12 Uit [115]: Een COM-object bevat een interface. Het gebruik van het COM-object door andere objecten verloopt dan ook via de mogelijke functies van de interface. De interne werking van het COM-object blijft verborgen voor gebruikers. De werking van een COM-object is dus nogal vergelijkbaar met die van een object in een object-geori¨enteerde programmeertaal.
pagina 24
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
• De eenvoudige overgang van programmeurs van de ene naar andere taal is heel belangrijk, zeker voor deze reeds bekend met C en C++. • Ondersteuning voor internationalisatie is belangrijk. • Hoewel C# bedoeld is om economisch met geheugen -en processorgebruik om te gaan, is het niet de bedoeling om te gaan concurreren met de performantie en omvang van bijvoorbeeld C of assemblercode. Samengevat kan men wel stellen dat C# zich vooral op de vereenvoudiging en productiviteit richt.
2.2.4
.NET Platform
Wie C# zegt, zegt .NET. C# werd speciaal ontwikkeld voor het .NET platform. Beide werken dan ook nauw samen. Grote delen van het platform zelf zijn in C# geschreven. Het .NET platform is een ontwikkelraamwerk dat een aantal technologie¨en bundelt, waaronder COM+ component services, ASP webontwikkeling, objectgeori¨enteerd ontwerp, webserviceprotocollen, ... Het voornaamste doel van het platform is het ter beschikking stellen van software via web services. Het richt zich dan ook in sterke mate op de ontwikkeling van applicaties voor gebruik via het internet. Natuurlijk is dit niet het enige doel en werd bij de ontwikkeling ook aandacht besteed aan gedistribueerd ontwerp, componentontwikkeling, enterprise services en tal van andere trends in de softwareontwikkeling. Met de ontwikkeling van dit platform wou men lessen trekken uit de noden en fouten van het verleden. Voorbeelden van eisen die aan software worden gesteld zijn onder meer de platformonafhankelijkheid, schaalbaarheid, onderhoudbaarheid en beschikbaarheid. Hier zou het platform een oplossing voor voorzien. Het .NET platform bestaat uit een aantal onderdelen die belangrijk zijn voor de goede werking van het systeem. Hieronder vallen het besturingssysteem, de .NET Enterprise Server-producten, Web Services, Visual Studio.NET en het .NET framework [101].
2.2.5
.NET Framework
Dit framework staat centraal in het .NET platform. Het kwam samen met een groot aantal ambitieuze ontwerpdoelstellingen. [134, 67, 113, 121] 2.2.5.1
Ontwerpdoelstellingen
Met het .NET framework wou men in de eerste plaats meer gemeenschappelijkheid cre¨eeren in het ontwikkelen van applicaties. Met gemeenschappelijkheid wordt hier het raamwerk met gemeenschappelijke klassen die alle ontwikkelaars kunnen gebruiken en compilers die allen een gemeenschappelijke intermediaire code genereren bedoelt.
pagina 25
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
De componenteninfrastructuur Integratie van binaire bibliotheken bleek in het pre-COMtijdperk vaak een kwestie van veranderen in de broncode. COM maakt een soort plug-and-play operatie mogelijk om softwarecomponenten met elkaar te verbinden. In de praktijk bleek het gebruik hiervan echter nog steeds moeilijk door alle regels die nageleefd dienden te worden. Het .NET framework zorgt ervoor dat alle klassen rechtstreeks op het binaire niveau kunnen herbruikt worden en dus plug-and-play automatisch ondersteund is. Componenten worden in .NET aangeduid met de naam assemblies (zie sectie 2.2.5.2). Taalintegratie COM ondersteunt taalonafhankelijkheid. .NET gaat hierin verder en laat bovendien ook taalintegratie toe. Dit is een belangrijk verschil. Taalonafhankelijkheid zorgt er voor dat je in eender welke taal kan ontwikkelen om bv. tot eenzelfde applicatie te komen. Taalintegratie gaat hier dus een stapje verder en laat kruisbestuiving tussen de gebruikte talen toe. Zo is er onder andere polymorfisme over de talen heen en zal een J# klasse kunnen overerven van een C# klasse. Dit alles wordt mogelijk gemaakt door gebruik van het Common Type System (CTS), een overkoepelend typeersysteem, waarover meer in sectie 2.2.5.2. Interoperatie op internet Het .NET framework wenst een oplossing te bieden aan problemen met zijn voorganger Distributed COM (DCOM). Het gebruikt hiervoor lichtgewicht protocollen zoals SOAP dat gebaseerd is op de XML en HTML standaards. Eenvoudige ontwikkeling Bij de ontwikkeling van software voor Windows zijn er reeds zeer veel Application Programming Interfaces (API) de revue gepasseerd. Zo hebben we de Windows API’s, de Microsoft Foundation Classes (MFC)13 , de Active Template Library (ATL)14 , COM interfaces, ... .NET zal hier duidelijkheid proberen scheppen door de verschillende API’s te bundelen. Alle talen kunnen dus eenzelfde API gebruiken. De ontwikkelaars dienen dus geen hele reeks verschillende API’s te bestuderen. Eenvoudige ingebruikname De DLL-hel zoals sommigen het beschrijven is een ander probleem. DLL staat voor dynamic link library en is de implementatie van een gedeelde bibliotheek in Microsoft Windows[121]. Vaak zijn verschillende versies van programma’s nodig, die dan ook verschillende versies dll’s gebruiken. Bij installatie kan het wel eens zijn dat nieuwe dll’s overschreven worden door oude en/of omgekeerd waardoor sommige functies van het programma of zelfs het gehele programma er de brui aan geeft. In .NET dient elke gedeelde DLL of elke openbare component (assembly) zich te registreren bij de Global Assembly Cache (GAC). Deze zorgt er in het geval van de dll’s voor dat elk programma met de juiste dll’s gestart zal worden. Twee versies dll’s kunnen dan ook gerust naast elkaar 13
Microsoft Foundation Classes is een Microsoft bibliotheek dat delen van de Windows API in C++ klassen inpakt [108]. 14 Active Template Library. Dit is een set van C++ klassen die op templates zijn gebaseerd. Ze zijn door Microsoft ontwikkeld en vereenvoudigen het programmeren van Component Object Model (COM) objecten [108].
pagina 26
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
staan zonder problemen te veroorzaken. Zelfs gelijktijdige uitvoering binnen een zelfde proces is toegelaten. Men verwijst hier naar met side-by-side executie. Naast openbare componenten zijn er ook nog private componenten. Deze worden gevonden met behulp van logische paden of configuratiebestanden en dienen zich bijgevolg niet te registreren. Dit alles zorgt voor een eenvoudige installatie en ingebruikname. Doordat assemblies, waarover in sectie 2.2.5.2 meer, niet meer geregistreerd moeten worden in de registry kan men de benodigde bestanden gewoon kopi¨eren naar de gewenste locatie en het programma zal werken. Men verwijst hier soms ook naar als een xcopy-installatie naar aanleiding van het DOS-commando xcopy dat bestanden en mappen kopieert. Betrouwbaarheid .NET maakt gebruik van sterke typering zoals beschreven in sectie 1.7.4. De Common Language Runtime (CLR) dient typen te controleren voordat ze gebruikt mogen worden. Verder wordt er net als in Java en C++ ook gebruik gemaakt van het exceptiemechanisme dat fouten zal opvangen en de programma’s in staat stelt verder uit te voeren. Ook automatische garbage collection wordt ondersteund zodat de programmeur niet zelf moet instaan voor het verwijderen van objecten zoals dit in C++ het geval is. Hierdoor kunnen opnieuw fouten worden vermeden. Beveiliging Tegenwoordig zijn onze computers vaak verbonden met ´e´en of ander netwerk. Een netwerk waarover veel informatie wordt gestuurd. .NET bevat een aantal beveiligingsmiddelen om te voorkomen dat bepaalde personen in data en applicaties binnendringen. Zo wordt er gebruik gemaakt van toegangscontrolelijsten en worden ook specifieke delen van de code beschermd door bijvoorbeeld het gebruik van beveiligingsattributen. 2.2.5.2
.NET Architectuur
Het .NET Framework is in feite een systeemapplicatie in een laag bovenop het besturingssysteem. De belangrijkste component is de Common Language Runtime (CLR)15 . Deze CLR is vergelijkbaar met de java virtuele machine (JVM). [67, 101] Common Language Runtime (CLR) De runtime werd ”from scratch” ontwikkeld. Voorgaande technologie¨en bouwden steeds verder op de vorige architectuur en werden hierdoor steeds groter, complexer en onoverzichtelijk. De CLR zal objecten activeren, positioneren in het geheugen, uitvoeren, verwijderen en beveiligingscontroles uitvoeren. De CLR past hierbij lazy loading toe, waarbij een deel van het programma in een bepaalde assembly pas opgehaald wordt wanneer het effectief wordt gebruikt. [67] Intermediate Language (IL) De .NET compiler compileert de applicatiecode in een door .NET ondersteunde taal eerst naar een intermediaire taal, de Common Intermediate Language (CIL) of ook nog de Microsoft Intermediate Language (MSIL) genoemd. Hierdoor bereikt men 15
Meer informatie op http://msdn2.microsoft.com/en-us/netframework/aa497266.aspx
pagina 27
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
zowel platformportabiliteit als taalonafhankelijkheid. Het framework biedt met de IL een nieuwe laag waardoor de gecompileerde code op elk platform kan draaien. We kunnen onze applicatie dus gewoon op een ander platform draaien mits er voor dit platform een .NET implementatie van de CLR bestaat (bv. Mono). Natuurlijk treden er problemen op indien deze implementatie niet alle mogelijkheden ondersteund daar de CLI een subset is van de CLR. Verschillende talen worden naar eenzelfde IL-code gecompileerd. Een IL wordt echter nooit geinterpreteerd in tegenstelling tot de bytecode van Java. [67] Common Language Infrastructure (CLI) Deze infrastructuur moet er voor zorgen dat derden een CLR voor andere dan Windows-platforms kunnen ontwikkelen. Het is een open specificatie (geen implementatie) en een subset van de CLR. Het beschrijft de uitvoerbare code en de runtime omgeving van .NET. Voorbeelden van alternatieve frameworks die gebruik maken van de CLI zijn Mono16 en DotGNUPortable17 . [101]
Figuur 2.2: CLI 16 17
www.go-mono.com http://dotgnu.org/
pagina 28
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Taalonafhankelijkheid en taalinteroperabiliteit Daar waar de JVM slechts de Java-taal ondersteunt, geeft de CLR ondersteuning voor meerdere talen. Deze talen zijn deze die door de CIL gerepresenteerd kunnen worden18 . Het Common Type System (CTS) zorgt er voor dat dit mogelijk is. Vaak zijn er nog verschillen tussen de talen onderling. Zo zal Managed C++ hoofdlettergevoelig zijn en VB.NET niet. Om deze verschillen op te vangen, bestaat de Common Language Specification (CLS). Deze werkt samen met het CTS en stelt een aantal regels op waaraan men moet voldoen om taalintegratie op een correcte manier toe te passen. Het bevat de minimale subset die .NET voor elke taal ondersteunt. De ondersteunde talen voldoen eveneens allemaal aan deze specificatie en kunnen dan ook zoals gezegd onderling gemixt worden. De CLS werkt dan in twee opzichten. Ten eerste betekent het dat compilerbouwers niet verplicht zijn alle features van .NET te ondersteunen door de basisset - waaraan minimum moet voldaan zijn - die de CLS is. Ten tweede zal men, als je je aan de regels houdt, jouw code kunnen gebruiken vanuit een andere taal die door de CLS ondersteund wordt [67, 101]. Sterke datatypering Het typesysteem overkoepelt de verschillende talen. Dit zorgt voor typeveiligheid, kruisbestuiving en integratie van de talen en services voor de uitvoering zoals garbage collection, just-in-time compilatie, excepties en dergelijke meer. Alle types zijn subklassen van de System.Object klasse. Er wordt een onderscheid gemaakt tussen waarde -en referentietypes. Primitieve types en waardetypes in het algemeen zijn intern op de stack geallo-
Figuur 2.3: Typesysteem ceerd. Boxing en unboxing laat toe primitieve data om te zetten naar en van zijn objectvorm. C# laat de creatie van gebruikersgedefinieerde waardetypes toe. Hiervoor gebruikt men het struct sleutelwoord. Het zijn een soort lichtgewicht klassen. Applicatiedomeinen Dit begrip werd ingevoerd in .NET om de overhead te beperken wanneer applicaties met elkaar willen communiceren, maar toch ge¨ısoleerd van elkaar dienen te 18
Lijsten met door .NET ondersteunde talen kan je vinden op http://www.startvbdotnet.com/ dotnet/languages.aspx, http://msdn2.microsoft.com/en-us/netframework/aa497336.aspx en http://en. wikipedia.org/wiki/Microsoft_.NET_Languages
pagina 29
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
blijven. Zonder de applicatiedomeinen dienden deze applicaties ofwel eenzelfde proces te delen of elk in een eigen proces te draaien. Applicatiedomeinen zijn dus veilige en veelzijdige proceseenheden die de CLR kan gebruiken om de isolatie tussen applicaties te verzekeren. In het eerste geval bestond het gevaar dat een fout in het ene proces het andere dan ook deed foutlopen. In het tweede geval was er overhead. Het idee dat hier wordt gevolgd, is om een proces op te splitsen in een aantal applicatiedomeinen. Verschillende threads worden dan ook in verschillende applicatiedomeinen uitgevoerd. Communicatie is eenvoudig daar de verschillende processen elkaars data zien. Dit althans in theorie. In de praktijk zal de CLR er voor zorgen dat de verschillende applicaties uit elkaars vaarwater blijven. Dit lijkt moeilijk daar men niet op voorhand weet wat een programma zal doen zonder het ook effectief uit te voeren. Toch kan dit door het sterk getypeerd zijn van de IL. Dit zorgt er namelijk voor dat het geheugen niet op ongepaste wijze wordt benaderd. Uitzonderingen door gebruik van onveilige code met bv. pointers zijn natuurlijk steeds mogelijk. Als er dan communicatie nodig is tussen verschillende applicaties dienen deze gebruik te maken van de .NET Remoting Services. Assemblies Volgens [101] zijn assemblies elementaire gebruikseenheden waarvan verschillende versies kunnen bestaan. Assemblies zijn de .exe of .dll bestanden die gedeployed worden. Ze bestaan uit een manifest, ´e´en of meer modules (gecompileerde bronbestanden) en een optionele set systeembronnen of resources. De assemblies zijn dus eerder logische eenheden dan fysieke. De CLR zal programma’s in de assemblies uitvoeren. Deze programma’s die draaien binnen de .NET omgeving verschillen van gewone Windows programma’s in die zin dat ze ook metadata (welke bestanden erin vervat zitten, beveiligingsinstellingen, versieinfo, afhankelijkheden, ...) bevatten. Ze worden ook portable executables (PE) genoemd. Deze metadata wordt in een manifest opgeslagen. De versie info in de assembly zorgt er voor dat je makkelijk twee verschillende versies van een assembly naast elkaar kunt uitvoeren op dezelfde runtime. Een assembly ondersteunt plug and play op vergelijkbare wijze als de meeste hardwarecomponenten en kan een aantal typen bevatten of ernaar verwijzen. Assemblies kunnen statisch of dynamisch zijn. Statische assemblies zijn .NET PE bestanden die tijdens het compileren worden gemaakt. Dynamische assemblies daarentegen worden dynamisch at runtime aangemaakt. Verder maakt men nog een onderscheid tussen private en publieke assemblies. Private assemblies zijn statische assemblies die door ´e´en specifieke applicatie worden gebruikt. Public of shared assemblies zijn openbare of gedeelde statische assemblies die een unieke gedeelde naam moeten hebben en door elke applicatie kunnen worden gebruikt. Installatie is eenvoudig. De assembly dient gewoon naar de juiste map gekopieerd te worden en het programma is klaar om te worden uitgevoerd. Verwijderen kan door de betreffende assembly weer te verwijderen. Er is dus geen gedoe meer met registratie in het het Windows register. Wanneer je echter assemblies wil delen met andere applicaties is het iets ingewikkelder. Voor private assemblies zijn er twee opties. Ofwel neemt u ze op in dezelfde map ofwel plaatst u ze een specifiek voorziene map. Voor gedeelde assemblies, dient er een registratie bij de global assembly cache te worden uitgevoerd.
pagina 30
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
.NET basisbibliotheek C# maakt gebruik van de .NET bibliotheek. Het heeft zelf geen runtime library. De .NET bibliotheek is ge¨ımplementeerd in C#. Er wordt vaak naar gerefereerd als de Base Class Library (BCL)19 of de Framework Class Library (FCL). Deze bibliotheek is eigenlijk een laag boven de CLR. Het is een vergelijkbare verzameling klassen als die in de STL (zie ook sectie 2.1.8), MFC, ATL of Java API. Ze vormen een soort basis voor elementaire toepassingen. Daarboven kan men nog een verzameling vinden die zich specifiek richt op het databeheer en xml-manipulatie. Uitbreidingen van de voorgaande classes kan men vinden in 3 technologi¨en, nl. de Web Services, Web Forms en Windows Forms. De bibliotheek bevat dus een aantal voorgecodeerde oplossingen die ondersteuning bieden voor onder meer user interfaces, datatoegang, databankconnectiviteit, cryptografie, ontwikkeling van webapplicaties, numerieke algoritmen, web forms, bestandssysteem -en registertoegang, attributen en reflectie, toegang tot windows besturingssysteem voor zaken als beveiliging en omgevingsvariabelen, COM interoperabiliteit en netwerkcommunicatie. [134] Uitvoering Belangrijke componenten in de CLR voor de uitvoering van programma’s zijn de class loader, de verifier, Just In Time compiler (JIT) en andere componenten die zorgen voor foutcontrole, beveiliging, garbage collection en dergelijke meer. De classloader is verantwoordelijk voor het laden van programma’s en assemblies nodig tijdens de uitvoering. De verifier staat in voor het bekrachtigen van de typeveiligheid van de IL code en dit at runtime. De verifier maakt onderdeel uit van de JIT compiler en wordt dus enkel uitgevoerd wanneer een methode of codefragment aangeroepen wordt. De JIT compiler staat in voor het converteren van IL code in de .NET PE bestanden naar beheerde machinecode voor het onderliggende besturingssysteem (managed native code). Het beheerd zijn is nodig opdat mechanismen als garbage collection, exeception handling, beveiliging en andere zouden werken. JIT compilatie vindt enkel plaats de eerste keer dat het stuk code gebruikt wordt. Code die dus niet gebruikt wordt, zal ook niet worden gecompileerd. 2.2.5.3
.NET Framework Versies
Hieronder volgt een klein overzicht van de versies van .NET enkele onderlinge verschillen. [134, 67, 118, 141]
20 ,
de features die ze bevatten en
.NET 1.0 De eerste versie van .NET werd gereleased rond eind januari, begin februari 2002 als onderdeel van de release van Microsoft Visual Studio .NET. .NET 1.1 De release viel samen met de Windows 2003 Server en verscheen in april 2003. Het was de eerste echte upgrade sinds 1.0 en opnieuw onderdeel van de release van een Visual 19
De Base Class Library is een bibliotheek van types en functionaliteiten beschikbaar voor alle talen die van het .NET Framework gebruik maken. Naar de BCL wordt soms verkeerdelijk gerefereerd als de Framework Class Library (FCL) die een superset is die ook de Microsoft.* namespaces bevat [108]. 20 Meer informatie hieromtrent kan gevonden worden in de MSDN bibliotheek via http://msdn.microsoft.com/ of http://www.microsoft.com/net/
pagina 31
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Versie
Versienummer
Release Datum
1.0 1.1 2.0 3.0 3.5
1.0.3705.0 1.1.4322.573 2.0.50727.42 3.0.4506.30 3.5
2002-01 2003-04 2005-11-07 2006-11 2007-04
RTM RTM RTM RTM Beta 1
Tabel 2.2: .NET Versies Studio .NET. Het is de eerste versie die deel uitmaakte van een Windows besturingssysteem, nl. Windows Server 2003. Verschillen met 1.0 • Built-in ondersteuning voor mobiele ASP.NET controls. Vroeger enkel als add-on verkrijgbaar • Beveiligingsaanpassingen in verband met het uitvoeren van Windows Forms assemblies vanop het internet en codetoegangbeveiliging voor ASP.NET applicaties • Built-in ondersteuning voor ODBC en Oracle databanken. Vroeger enkel als add-on verkrijgbaar • .NET Compact Framework, een versie van het .NET Framework voor kleine toestellen zoals PDA’s • Ondersteuning voor het Internet Protocol versie 6 (IPv6) • Talrijke API aanpassingen .NET 2.0 Deze versie werd ge¨ıntroduceerd op 7 november 2005 samen met Visual Studio 2005, Microsoft SQL Server 2005 en Biztalk 2006. Deze release was en is een grote en belangrijke verandering ten opzichte van de eerste versie geweest [67]. Verschillen met 1.1
[134, 118]
• SQL server integratie. Bij gebruik van de SQL Server 2005 kan deze als host voor de CLR optreden. Dit heeft als voordeel dat men gemakkelijker met de SQL server kan werken. Zowel .NET 2.0, Visual Studio 2005 als SQL Server 2005 zijn nu met elkaar verbonden, aangezien applicaties vaak van voorgaande gebruik maken. • 64-bit support. Overschakelen naar 64 bit cre¨eert nieuwe toepassingsmogelijkheden. Zo beschikt men over een grotere adresseringsruimte waar men gebruik kan van maken. • Generics. Ze laten de gebruiker toe om een generieke collectie te maken die nog steeds sterk getypeerd is. Generics lijken op C++ templates maar zijn toch ietwat verschillend.
pagina 32
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
• Anonieme methoden. Deze laten ons toe stukken code te schrijven waar we ze direct nodig hebben. We hoeven geen aparte methode meer te maken. We kunnen de code daar meegeven in een delegate om ze dan later uit te voeren. • Nullable types. Nullable types laten ons toe om aan bepaalde types die dit anders niet toelaten null te assigneren, bv. een int. Door het ”?” na het type te plaatsen, maken we duidelijk dat we een nullable type wensen. • Iterators. Deze laten ons toe een collectie elementen af te lopen, bv. adhv een foreach. • Parti¨ele Klassen. Dit laat ons toe om puur op syntactisch niveau een klasse op te splitsen in verschillende bestanden die bij compilatie tot eenzelfde klassebestand worden gecompileerd. • Covariantie en contravariantie voor delegatesignaturen. Ondersteuning voor covariante return types en parameter covariantie voor delegates. Geen covariantie of contravariantie voor methode overschrijving (zie sectie 3.2.2.6).[118] • Nieuwe hosting API voor applicaties die een instantie van .NET wensen te hosten. Deze API geeft meer controle over het runtimegedrag mbt multithreading, geheugenallocatie, laden van assemblies, ... . • Verbetering van bestaande en introductie van nieuwe ASP.NET web controls. • Nieuwe data controls met declaratieve data binding. • Nieuwe personaliseringsfeatures voor ASP.NET, zoals ondersteuning voor thema’s en skins. • .NET Micro Framework, een versie van het .NET Framework gerelateerd aan de Smart Personal Objects Technology. • Talrijke API aanpassingen. .NET 3.0 Op 6 november 2006 verscheen dan het .NET 3.0 framework (het vroegere WinFX) of het managed code programmeermodel van Microsoft. Deze vormt integraal een deel van Windows Vista en de Windows Server ”Longhorn” besturingssystemen. Versie 3.0 is een superset van de vorige versie 2.0. Zo combineert dit framework de huidige 2.0 versie met enkele nieuwe technologi¨en waarbij de nadruk op de gebruikerservaring, naadloze en veilige communicatie en businessprocesmodellering ligt. Deze versie 3.0 introduceert de vier onderstaande technologie¨en of foundations: WPF Windows Presentation Foundation. Deze technologie introduceert enkele nieuwe benaderingen tov presentatie van applicaties (xaml, aero ondersteuning, ...) met de nadruk op gebruikerservaring.
pagina 33
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
WCF Windows Communication Foundation (vroegere ”Indigo”). Deze technologie is een service geori¨enteerde technologie en een evolutie van de webservices in versie 2.0. Het dient de ontwikkeling van geconnecteerde systemen te vereenvoudigen, alsook de veiligheid, betrouwbaarheid en interoperabiliteit te verhogen. WWF Windows Workflow Foundation. Deze technologie wordt gebruikt voor modellering van business processen (workflow) om zodoende bv. eenvoudiger webservices te implementeren. WCS Windows Cardspace Hierbij staat de beveiliging van de digitale identiteit van de eindgebruiker centraal. Er is beveiliging tegen onder andere spoofing, waarbij derden zich voordoen als de eindgebruiker en zo zijn identiteit kunnen overnemen. [141]
Figuur 2.4: .NET 3.0
.NET 3.5 Deze versie zal een nieuwe compiler bevatten die de nieuwe features als Language Integrated Query (LINQ) zal bevatten. Deze versie zal gereleased worden samen met de ”Orcas” versie van Visual Studio. Beta 1 was reeds gereleased op 27 april 2007. .NET Compact framework Dit is een subset van het .NET framework. Het bevat de basisbibliotheek en enkele additionele bibliotheken. Er zijn twee versies, nl. 1.0 (2003/2003SE) en 2.0 (5.0)
pagina 34
2.2 C#
2.2.5.4
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Andere features
Datatoegang .NET heeft zoals gezegd uitgebreide voorzieningen voor datatoegang. Dit kan gaan van het lezen van files, het werken met xml-bestanden tot het gebruik van databanken en de collections. In de BCL zijn dan ook tal van klassen te vinden die hierbij helpen. Verder is er ook nog ActiveX Data Objects for .NET of kortweg ADO.NET, een programmeerinterface om (web)applicaties met een willekeurige database te laten communiceren [111]. Met de nieuwe versie van .NET zal datatoegang nog eenvoudiger worden door het gebruik van de LINQ-technologie. Beveiliging Code-identiteit geeft aan elk codefragment een eigen identiteit. De CLR kan hiermee verifi¨eren of een assembly toegang heeft tot bepaalde systeembronnen of andere assemblies. Verder wordt het begrip codetoegang ge¨ıntroduceerd. Hierbij worden at runtime aan de hand van een reeks rechten bekeken of een bepaalde assembly toegankelijk is. Metadata Metadata vormt een gedetailleerde beschrijving van onder andere een assembly. Zo bevat het info over de identiteit van de assembly (naam, versie, cultuur, openbare sleutel, ...), gebruikte typen, beveilingseisen, ... . Metadata wordt intensief gebruikt in de CLR, Class Loader, JIT compilers en bij gereedschappen als debuggers en profilers. Reflectie maakt hier ook zeer intensief gebruik van. Reflectie .NET heeft een Reflection API. Dit is een verzameling klassen die ons toelaten om metadata op te vragen en te manipuleren. De CLR gebruikt deze reflection classes om onder andere zaken als garbage collection, beveiliging en typecontrole mogelijk te maken. Vaak gebruikte klassen zijn deze uit de namespaces System.Reflection en System.Reflection.Emit . Aan de hand van de klassen in Emit kan men eenvoudig een programma schrijven dat at runtime een nieuwe .NET-assembly maakt. Hierdoor is het in feite mogelijk om zelf een .NET-compiler te ontwikkelen. Namespaces Het directief #using maakt alle typen uit de aangegeven namespace toegankelijk. Dit gebeurt op eenzelfde wijze als men zou doen in C++ met de include en using sleutelwoorden. Het is een hulpmiddel om conflicten tussen namen te voorkomen. Nesting van namespaces is toegelaten en indien er geen namespace werd gebruikt, dan zal de code automatisch aan een naamloze globale namespace worden toegevoegd. 2.2.5.5
Kritieken
Er zijn altijd kritieken en dus ook in verband met het .NET Framework. Hieronder zijn er enkele verzameld. • De introductie van het .NET Framework bracht met zich mee dat de oude VB taal vervangen werd door de nieuwe VB.NET. Deze verschilt in heel wat opzichten van de vorige en dit deed dan ook veel stof opwaaien.
pagina 35
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
• Er blijven nog een aantal achterwaartse en voorwaartse incompatibiliteitsproblemen tussen de verschillende versies. Deze zijn echter wel goed gedocumenteerd, in beveiligingsupdates opgenomen of als overbodig aangeduid. Het toelaten van meerdere versies naast elkaar is hier ook een vaak gebruikte oplossing voor. • Een vaak terugkerende klacht is dat het .NET Framework te nauw met het Windowsbesturingssysteem zou verbonden zijn. Dit kan echter gezien worden als louter een zakelijke beslissing van Microsoft. De ontwikkeling van alternatieve raamwerken voor andere platformen dan Windows laat hier dan ook zien dat het zeker ook anders kan. • Een andere zorg is deze van reverse-engineering21 . Het is eenvoudig om een .NET assembly te bekijken en alles te weten te komen over hoe deze in elkaar zit. Dit is ook het geval met de Java bytecode. Dit vormt mogelijk een probleem voor bedrijven daar via de bytecode bedrijfsgeheimen kunnen uitlekken. Vaak wordt dan ook de toevlucht gezocht in obfuscatie22 en andere techieken om de software te beschermen. Microsoft heeft hier zelfs een tool voor in Visual Studio 2005, nl. dotfuscator community edition. • Verder had Microsoft patenten genomen op enkele delen van het .NET Framework. Een overeenkomst werd echter gesloten met bepaalde bedrijven zodat deze niet vervolgd zouden worden voor het gebruik van deze gepatenteerde technologie. Mono kon hier handig van gebruik maken. Dit werd echter bekritiseerd door de open-source community aangezien dit indruist tegen de principes van gelijke rechten voor alle gebruikers van een specifiek programma (Patent Agreement Microsoft).
2.2.6
Versies en standaarden
[67, 50, 64, 28, 27, 88, 119] De eerste wijd verspreide versie van C# zag het licht in juli 2000. De taal maakte deel uit van het .NET framework. Standaarden voor de taal ontwikkelden zich snel. In september 2000 vormde zich Task Group 2 (TG2) om een standaard voor de taal te ontwikkelen. Een andere Task Group (TG3) werd opgericht die zich zou richten op een standaard voor de CLI. De C# programmeertaal werd dan dus uiteindelijk gestandaardiseerd door de ECMA International23 en door de ISO. 2.2.6.1
C# 1.0
In de eerste versie kon je bovenop de features gebruikt uit het .NET framework features vinden waaronder properties, indexers, delegates, events en attributes. 21
Hierbij gaat men omgekeerd te werk en vertrekt men van een bepaald afgewerkt product waar men de werking van wil achterhalen. 22 Hiermee wordt het encrypteren van broncode bedoeld. Deze techniek helpt om reverse engineering van de bytecode tegen te gaan [137]. 23 European association for standardizing information and communication systems. Vroeger gekend onder de naam ECMA, European Computer Manufacturers Association, http://www.ecma-international.org/
pagina 36
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Versie
Release Datum
C# C# C# C# C# C# C#
juni-juli 2000 13-12-2001 28-03-2003 juli 2003 september 2005[29] november 2005 2006
ECMA International 334 ISO (ISO/IEC 23270:2003) 2.0 beta 3.0 alfa 2.0 ISO (ISO/IEC 23270:2006)
Tabel 2.3: C# Versies 2.2.6.2
C# 2.0
Versie twee voegde nog een aantal zaken toe die we in sectie 2.2.5.3 reeds bespraken. Dit waren zaken zoals anonieme methoden, iterators, generics en nullable types. 2.2.6.3
C# 3.0
De nieuwe features in C# 3.0 zijn ontwikkeld om het maken en gebruiken van hogere orde functionele stijl bibliotheken mogelijk te maken. Met C# 3.0 gaan we dus meer de functionele toer op. De nieuwe features zullen beschikbaar zijn zonder enige veranderingen aan de CLR. Dit betekent dan ook dat versie 2.0 en 3.0 binair compatibel zullen zijn. Nieuwe features Impliciet getypeerde lokale variabelen (var) Deze impliciet getypeerde variabelen worden aangeduid door het nieuwe sleutelwoord ”var”. Dit zorgt er voor dat de programmeur tijdens het coderen niet zelf het juiste type moet aangeven. 1
var mijnVar = ” Dit i s mijn v a r i a b e l e ” ; De compiler zal bij compilatie dan zelf het juiste type afleiden uit de context en dit vast aan de variabele koppelen. De variabele kan daarna dus niet meer van type veranderen in tegenstelling tot bv. het gebruik van dynamische variabelen in scriptingtalen. Zodoende behouden we een sterke typering terwijl de programmeur de voordelen van ontwikkelen in een dynamisch getypeerde taal ervaart. Anonieme types Met anonieme types is het niet meer nodig om zelf het type aan te geven. De compiler maakt zelf uit welk type het is of maakt voor de gebruiker zelf een nieuw type aan. Dit is vooral handig bij bijvoorbeeld databanktoegang. Indien men projecties en unions van tabellen wenst uit te voeren en een resultaat terugkrijgt, diende men vroeger exact te weten hoe dit resultaat er zou uitzien. Er moest dan ook een type geimplementeerd worden om hier verder mee te kunnen werken. Nu hoeft dit niet meer en kan je verder werken met het anonieme type terwijl de compiler nog steeds waakt over de typeveiligheid.
pagina 37
2.2 C#
1
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
var x = new { Name = ” James ” } ; Verder kan men ook nog impliciet getypeerde arrays maken, waarbij het type weggelaten kan worden. Vroeger schreven we
1
int [ ] a r r = new int [ ] { 1 , 2 , 3 } ; en nu
1
var a r r = new [ ] { 1 , 2 , 3 } ;
Extensies Met extensions wordt het mogelijk om bijkomende instantiemethoden te schrijven voor een bepaald type object. Deze instantiemethoden worden gedeclareerd aan de hand van speciale statische methoden, de extensiemethoden. Deze worden verzameld in een aparte klasse die we bij gebruik moeten importeren via het using sleutelwoord. De extensiemethoden worden dan aanzien als methoden van dit bijhorende type. In het voorbeeld hieronder cre¨eren we een eigen klasse met een extensiemethode voor de string klasse. In onze testklasse kunnen we de extensiemethode dan gewoon gebruiken als een string methode, mits het importeren van de extensieklasse. 1 2 3 4 5 6 7 8 9 10
// E x t e n s i e k l a s s e d i e e x t e n s i e voor de s t r i n g k l a s s e d e f i n i e e r t namespace MijnExtensieMethoden { public static c l a s s Extensie { // door g e b r u i k ” t h i s s t r i n g ” weten we d a t h e t een // u i t b r e i d i n g van de s t r i n g k l a s s e moet z i j n p u b l i c s t a t i c void G e e n S p a t i e s ( t h i s s t r i n g data ) { return data . R e pl a ce ( ” ” , ” ” ) ; } } }
11 12 13 14 15 16 17 18 19
// Mijn t e s t k l a s s e u s i n g MijnExtensieMethoden ; namespace MijnNamespace { public c l a s s MijnTestKlasse { s t r i n g data = ” Dit i s mijn s t r i n g ” ; s t r i n g g e e n S p a t i e s = data . G e e n S p a t i e s ( ) ; } }
Objectinitialisaties In C# 3.0 wordt een nieuwe manier van initializeren van objecten ge¨ıntroduceerd. De ontwikkelaars van een klasse hoeven op deze manier geen groot aantal verschillende constructoren te implementeren of zelfs geen. Het meegeven van de juiste argumenten
pagina 38
2.2 C#
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
valt daarmee volledig bij de gebruiker van de klasse. Een neveneffect van deze manier van initializeren is een beter leesbare code bij de clientcode. 1 2
1 2 3
Persoon mijnPersoon = new Persoon { Achternaam = ” Botte ” , Voornaam = ” Jan ” } ;
MyList l i s t = new MyList ( ) ; l i s t . Add ( 1 ) ; l i s t . Add ( 2 ) ; kan geschreven worden als
1
MyList l i s t = new MyList { 1 , 2 } ; waarbij MyList verondersteld wordt om System.Collections.IEnumerable te implementeren en een publieke Add methode te hebben. Automatische properties : De compiler zal automatisch alle private instantievariabelen en de bijhorende getters en setters genereren. De code voor een property wordt dan:
1
p u b l i c s t r i n g Name { g e t ; p r i v a t e s e t ; }
Lambda expressies Lambda expressions vormen een voortzetting van de anonieme methoden in C# 2.0 waar je inline een code block kon defini¨eren en gebruiken daar waar een delegate (zie sectie 3.1.5.1 en verder) verwacht werd. Hier wordt met dit concept nog een stapje verder gezet en een compactere syntax aangeboden. Wat we eerst met een anonieme delegate zouden schrijven zoals 1
l i s t O f F o o . Where ( d e l e g a t e ( Foo x ) { return x . s i z e > 1 0 ; } ) kan nu geschreven worden als
1
l i s t O f F o o . Where ( x => x . s i z e > 1 0 ) ; Met deze lambda expressies gaat C# meer de functionele toer op. Dit is vooral wenselijk voor het gebruik met parallelle systemen. Daar er normaal geen globale toestand wordt bijgehouden bij functionele talen (zie sectie 1.6.2.1) is er geen gemeenschappelijke data en leent dit zich tot het gebruik van concurrente processen. Expressiebomen Er kan gebruik worden gemaakt van expressiebomen. Expressiebomen zijn een andere voorstelling van de lambda-expressies. Ze bestaan uit een hierarchie van componenten. Deze componenten zijn de delen waaruit de lambda-expressie bestaat. De properties van een expressieboom geven ons gedetaileerde informatie over de lambda-expressie. Door lamdaexpressies op deze manier voor te stellen kan men ze at runtime gaan analyzeren. Databank LINQ (DLINQ) maakt gebruik van at runtime vertaling van lambda-expressies naar SQL. Je
pagina 39
2.3 Java
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
kan ook zelf een expressieboom gaan opbouwen en deze dan compileren om een delegate te verkrijgen. Deze delegate kan dan aangeroepen worden waardoor de gegenereerde code zal worden uitgevoerd. [39] Language integrated query (LINQ) Hiermee wenst men het mogelijk te maken om allerhande datatoegang op een eenduidige manier te doen. Zo zal men een databank op dezelfde manier als een xml document kunnen inlezen. Deze techniek werd geimplementeerd aan de hand van de voorgaande C# 3.0 features. Zo zijn alle select, where, ... sleutelwoorden in de taal zelf en worden ge¨ımplementeerd aan de hand van extensions die samen gebracht werden in ´e´en bepaalde klasse. Doordat dit nu in de taal zelf ingebakken is, hoeven de tekstuele SQLstatements niet meer. Hierdoor kan de compiler ook het type controleren, wat bij fouten in tekstuele SQL-statements zeker niet kon. Er bestaan voor de verschillende soorten data verschillende specifieke bibliotheken. Zo is er DLINQ voor databanktoegang en XLINQ die zich richt op xmltoegang. Ook PLINQ is een extensie van de LINQ technologie. PLINQ zal query operaties automatisch optimaliseren en parallel maken aan de hand van runtime informatie.
2.2.7
Ondersteuning
Voor C# is er een uitgebreide ondersteuning door middel van documentatie, IDE’s, tools en tutorials. Zo is er de MSDN library en bestaan er reeds verschillende versies van de Visual Studio .NET.
2.2.8
De toekomst
Over de volgende versies kan meer gevonden worden op de msdn website.24
2.3 2.3.1
Java Java in het kort
Java is een object-geori¨enteerde imperatieve programmeertaal. Het is een statisch en sterk getypeerde taal. Er is wel een uitzondering op het sterk getypeerd zijn. Tussen types in een klassenhi¨erarchie kan met expliciete casts een variabele wel van type veranderen. Java gebruikt geen pointers en is volledig garbage-collected. Bijgevolg is Java ook een taal waar memory leaks zo goed als onbestaande zijn. 24
Meer specifiek op http://msdn2.microsoft.com/en-us/vcsharp/aa336745.aspx en http://msdn2. microsoft.com/en-us/netframework/aa569284.aspx
pagina 40
2.3 Java
2.3.2
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Geschiedenis
Java begon in 1991 als een project - ”Oak”25 genaamd - bij Sun Microsystems onder leiding van James Gosling. Pas toen bleek dat er al een programmeertaal bestond met die naam werd de naam Java gekozen26 . Oorspronkelijk was de intentie software voor settop-boxen te schrijven. Bijgevolg waren de doelstellingen voor Java om heel betrouwbaar, portabel en compact te zijn. In 1995 kwam de eerste publieke implementatie uit : Java 1.0. Java was dus initieel bedoeld voor allerhande elektronische apparaten zoals televisies, koelkasten en PDA’s. De interactieve TV en PDA markt brak echter niet open zoals was verwacht. Toen het World Wide Web meer aan populariteit begon te winnen, besefte Sun dat ze hun taal ook goed konden gebruiken in een webomgeving. Dankzij de open specificatie en de mogelijkheid om een Java-programma als applet veilig in een webpagina in te bedden werd Java plots heel snel populair. Het gebruik van Java voor kleine apparaten werd later toch nog gerealiseerd met het uitbrengen van de Java Card technology in 1997. Hiermee konden chipkaarten in Java geprogrammeerd worden. Deze techniek werd al snel na introductie in de GSM-wereld geadopteerd voor gebruik in de SIM-kaart. In 2005 werd in ongeer 80% van de SIM-kaarten gebruik gemaakt van de Java-technologie. Vanaf ”Java 2” (zie verder) waren er drie verschillende versies. Naast de Standard Edition (J2SE) kwam de Enterprise Edition (J2EE) bedoeld voor bedrijfsapplicaties en de veel kleinere Micro Edition (J2ME). Op 13 november 2006 gaf Sun delen van Java vrij als Open Source Software, onder de ”GNU General Public License”. Ondertussen is heel de JDK Open Source[60].
2.3.3
Filosofie
De vijf belangrijkste doelstellingen bij de creatie van Java waren volgens [128] de volgende : • Het gebruiken van het object-ge¨orienteerde paradigma • Platformonafhankelijkheid • Built-in support voor het gebruik van computer netwerken • Code moet veilig uit te voeren zijn vanop remote bronnen • Het moet makkelijk zijn in gebruik Java heeft geen nieuwe concepten ge¨ıntroduceerd. Er is in het hele offici¨ele Java-platform niets te vinden dat niet al eens ergens anders al een keer gedaan is. De meeste software, zeker in zakelijke toepassingen, werd tot 1997 geschreven in C, C++ of Pascal-dialecten. Java bracht enkele vernieuwingen bij de niet-academische softwareontwikkelaar zoals de virtuele machine, 25
Die werknaam werd gekozen daar bij het zoeken van een naam hun oog viel op een eik die aan de bureau’s van Sun stond. 26 Naar de bij programmeurs populaire Indonesische Java koffie
pagina 41
2.3 Java
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
de garbage collector en het ontbreken van pointers. Deze idee¨en waren echter niet nieuw: in diverse (al dan niet academische) programmeertalen waren ze al te vinden en beproefd. [129]
2.3.4
Object-ori¨ entatie
Java is een op klassen gebaseerde object-ge¨orienteerde taal. Een instantie van elk type is ook een object behalve bij de primitieve types. Dit is bewust gekozen door de ontwerpers omwille van performantiedoeleinden. Objecten komen immers niet op de stack terecht, primitieve types wel. Daarom wordt van Java soms gezegd dat ze niet puur object-ge¨orienteerd is. Sinds Java 5.0 kan je dankzij auto(un)boxing toch zonder problemen primitieve types als hun wrapper class (een object) gebruiken, wat voor meer flexibiliteit zorgt. [128]
2.3.5
Platform-onafhankelijkheid : de Java Virtual Machine
[133, 129] Het platformonafhankelijke concept van Java wordt bewerkstelligd door Java broncode te vertalen (compileren) naar bytecode. Deze bytecode kan op elk willekeurig besturingssysteem waar een JVM voor beschikbaar is, worden uitgevoerd. Als gevolg hiervan wordt ieder Javaprogramma conceptueel geschreven voor maar ´e´en platform : de JVM. Conceptueel is de JVM een echte computer, alleen niet uitgevoerd in hardware. De machine beschikt over opslagcapaciteit, een eigen geheugenmodel, een gespecificeerd gedrag van zijn rekeneenheid en een interne machinetaal. Ook is de basismachine conceptueel uitbreidbaar met nieuwe mogelijkheden en modules, die overeenkomen met klassen, API’s en dergelijke – opgesteld in bytecode. Een direct gevolg van het gebruik van het Virtual Machine model voor het uitvoeren van computerprogramma’s is dat er voor het uitvoeren van ieder programma een extra vertaallaag nodig is ten opzichte van een programma dat direct gecompileerd wordt naar de machinetaal van de onderliggende hardware. Hierdoor zal een Java programma in eerste instantie langzamer draaien dan vergelijkbare programma’s die direct naar machinetaal gecompileerd zijn. Door het invoeren en optimaliseren van de JIT compiler en het versnellen van de processoren zijn die verschillen tegenwoordig dikwijls te verwaarlozen. In de beginjaren van Java was dit echter niet het geval en hierdoor heeft Java een serieus imagoprobleem opgelopen dat nog steeds voortduurt, ondanks het feit dat Java tegenwoordig competitief presteert.
2.3.6
Versies
Hieronder volgt een overzicht van de releases van de standaard edities gebaseerd op [131, 63] JDK 1.0
De eerste release van de Java Development Kit.
JDK 1.1 Er werden verbeteringen toegevoegd waaronder betere event handling, binnenklassen en een verbeterde JVM. Het swing packet met betere grafische mogelijkheden dan het huidige awt pakket komt uit. Het is echter nog geen onderdeel van de standaard bibliotheek.
pagina 42
2.3 Java
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Versie
Release Datum
JDK 1.0 JDK 1.1 J2SE 1.2 J2SE 1.3 J2SE 1.4 J2SE 5.0 Java SE 6 Java SE 7
08-12-1996 19-02-1997 08-12-1998 08-05-2000 06-02-2002 30-09-2004 11-12-2006 gepland in 2008
Tabel 2.4: Java Versies J2SE 1.2 Vanaf nu wordt van de SDK (Software Development Kit) gesproken in de plaats van de JDK. Er bestaan nu ook drie versies met elk hun specifieke eigenschappen voor een bepaald type platform. Naast de standaard versie J2SE (Java 2 Platform, Standard Edition) is er nu ook J2EE (Enterprise Edition) - een superset van de SE - en J2ME (Micro Edition) - een subest van de SE. Belangrijke veranderingen zijn het toevoegen van het Collections framework en reflectie. De swing API zit nu ook in de standaard bibliotheek. De JVM is voor het eerst uitgerust met een JIT compiler. J2SE 1.3 Verschillende performantie verbeteringen waaronder de HotSpot JVM, een aangepaste virtuele machine die het voorkomen van hotspots(delen van de bytecode die herhaaldelijk uitgevoerd worden) uitbuit. J2SE 1.4
Onder andere verbeterde I/O en XML support en assert statement toegevoegd.
J2SE 5.0 of 1.5 Beide namen worden door mekaar gebruikt, maar de naam van de eigenlijke development kit is 1.5 en 5.0 wordt gebruikt voor de runtime environment. Enkele significante features werden toegevoegd waaronder generics (zie sectie 3.3), metadata door middel van annotaties (zie sectie 3.6.2), auto(un)boxing (automatische conversies tussen primitieve types - bvb int - en hun wrapper klassen - bvb Integer, variadic function(zie sectie 3.1.5.1), en een verbeterde for lus. Java SE 6 of 1.6 Er werden opnieuw nieuwe naamconventies ingevoerd. Vanaf nu spreekt men van Java SE X en wordt J2SE X niet meer gebruikt. Performantie verbeteringen waaronder opmerkelijke voor het swing pakket.
2.3.7
Namespaces : packages
Een Java package is een mechanisme binnen Java om klassen te organiseren in namespaces. Java broncode die binnen eenzelfde categorie/functie vallen kunnen hierdoor gegroepeerd worden en
pagina 43
2.4 D
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
naamconflicten worden vermeden. Dit kan door middel van een package statement bovenaan het beginbestand om aan te geven waartoe ze behoren. Java packages kunnen worden opgeslagen in gecomprimeerde JAR files. Om een package te gebruiken in een Java klasse dient het package ge¨ımporteerd te worden door middel van een import statement. [130]
2.3.8
Ondersteuning
De ontwikkelaars van Java hebben zelf ook een ontwikkelomgeving ontworpen, de Netbeans IDE27 . Deze is zeer geavanceerd en vele processen kunnen geautomatiseerd worden zoals bijvoorbeeld internationalisatie en deels documentatie genereren. Ook een geavanceerde debugger is voorhanden. Naast Netbeans zijn er ook nog andere IDE’s waarvan JCreator28 en Eclipse29 bekende voorbeelden zijn. Deze laatste IDE biedt trouwens ook ondersteuning voor andere programmeertalen. Er is over het Java-framework en de programmeertaal al heel wat literatuur verschenen en aan informatie is er dus geen gebrek. Voor het ontwikkelen zelf is er voor de standaard bibliotheek overzichtelijke informatie aan de hand van de Java API [59]. Deze standaard bibliotheek is behoorlijk geavanceerd en voor heel wat domeinen zal je er al de nodige functionaliteit in vinden. Er bestaan ook heel wat externe Java bibliotheken.
2.3.9
De toekomst
Het Java platform blijft verder evolueren en nieuwe versies blijven uitkomen. Dit alles gebeurt onder toezicht van Sun Microsystems maar ook derden kunnen de verdere evolutie be¨ıvloeden via het Java Community Proces30 (JCP) dat sinds 1998 bestaat. Voorstellen worden in een Java Specification Request (JSR) gegoten en ingediend. De volgende versie - Java SE 7 - is aangekondigd voor 2008.
2.4 2.4.1
D D in het kort
D is statisch getypeerd en compileert rechtstreeks naar machinecode. Het is een multi-paradigma taal die onder andere het imperatieve, object-ge¨orienteerde en generieke paradigma ondersteunt.
2.4.2
Geschiedenis
D is in 1999 ontworpen door Walter Bright als een opvolger voor C en C++. Walter Bright behaalde in 1979 een diploma werktuigbouw aan Caltech (California Institute of Technology) en werkte 3 jaar voor Boeing aan de ontwikkeling van het stabilisatiesysteem van de Boeing 757. 27
http://www.netbeans.org/ http://www.jcreator.com/ 29 http://www.eclipse.org/ 30 http://jcp.org 28
pagina 44
2.4 D
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Daarna schakelde hij over op het schrijven van software, in het bijzonder compilers, en dit doet hij tot op vandaag nog steeds [13]. Hij is vooral bekend als de hoofdontwikkelaar van de eerste native C++ compiler.
2.4.3
Filosofie
Zoals vermeld in vorige paragraaf is D ontworpen als een opvolger voor C en C++ maar D gebruikt ook elementen uit andere daarop gebaseerde talen zoals C# en Java. D tracht de kracht en hoge performantie van C en C++ te combineren met de productiviteit van modernere talen zoals Python en Ruby[14]. De syntax van D doet vooral aan die van C++ denken. In de ogen van Walter Bright zijn C en C++ stilaan verouderd. Ze zijn enorm succesvol omdat ze heel wat belangrijke en bij ontwikkelaars populaire features ondersteunen. Maar na zovele jaren van nieuwe functionaliteit toevoegen en achterwaartse compatibiliteit garanderen, beginnen deze mekaar tegen te werken en hebben de talen met heel wat legacy code af te rekenen. Hoe langer een taal bestaat en evolueert door het toevoegen van nieuwe features, hoe moeilijker het wordt om nieuwe features te blijven toevoegen en compatibel te blijven met eerdere versies. Volgens Walter Bright werd het tijd om een stap terug te zetten, te evalueren wat al dan niet goed werkt en een nieuw design te ontwikkelen. D elimineert features uit het design van C++ die het moeilijk maken om programma’s te schrijven, debuggen, testen en onderhouden en voegt features toe die dit alles moeten vergemakkelijken. [11] D wordt niet gestuurd door een bepaalde instantie die zijn wil kan opleggen of door ´e´en of ander theorie over programmeren. Het zijn de noden en bijdragen van de D programmeer gemeenschap die de richting waar de taal in gaat bepalen. Zowel lexicaal als syntactisch en semantisch tracht men eenvoud en duidelijkheid na te streven. E´en van de hoofddoelen van D is het elimineren van de complexiteit van C++ die het zo moeilijk maakte er een compiler voor te schrijven. Een vereenvoudigde syntax maakt het zowel voor de compiler als de programmeur eenvoudiger aangezien het de effeci¨entie van compilers verhoogt en het voorkomen van fouten verminderd. D laat bijvoorbeeld de zeer gecontesteerde syntax met haakjes (<>) voor het declareren van templates vallen (verwarrend omwille van de binaire operatoren < > en shift operatoren << en >>), wat de code meer leesbaar maakt en tevens eenvoudiger te parsen. [4] C is nog steeds de ”lingua franca” in de programmeerwereld. C’s ABI31 wordt ondersteunt door D. Alle datatypes van C zijn ook in D ingebouwd. D kan ook rechtstreeks elke C functie oproepen, elk C type doorgeven en elke C return waarde aanvaarden net zoals je het in C zou doen. Bovendien kan D elke functie in de C bibliotheek gebruiken, hoewel normaal de pure D standaard bibliotheek volstaat. Er is geen directe interface van D naar C++, maar de twee talen kunnen wel met elkaar com31
Application Binary Interface. Beschrijft de laag-niveau interface tussen een applicatie en het besturingssysteem, tussen een applicatie en zijn bibliotheken of tussen verschillende componenten van de applicatie. Je kan het vergelijken met een API, maar daar waar een API de interface tussen broncode en bibliotheken beschrijft (zodat de code kan gecompileerd worden op elk systeem dat de bibliotheken ondersteund), zal een ABI gecompileerde code toelaten te functioneren op elk systeem die de ABI ondersteund [109].
pagina 45
2.4 D
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
municeren via C interfaces die ze beiden ondersteunen. [120, 12, 14, 55]
2.4.4
Versies
De eerste versie van DMD (Digital Mars D Compiler) kwam uit op 8 december 2001. Sindsdien is taal verder geevolueerd in een richting die vooral bepaald werd door wat besproken werd op de nieuwsgroepen. Walter Bright staat open voor alle suggesties, maar voor wat uiteindelijk wel en niet in de taal terechtkomt heeft hij de eindverantwoordelijkheid. Met de taal kwam ook een standaard runtime bibliotheek, nl. Phobos uit. Begin dit jaar kwam DMD 1.00 uit. De eerste offici¨ele release. D is nog steeds volop in ontwikkeling, maar sindsdien worden niet meer zo regelmatig aanpassingen gedaan. Het ontwerp is op dit moment ”virtueel bevroren” en nieuwe versies focussen vooral op het oplossen van bestaande bugs[120]. De huidige versie is DMD 1.014. Nieuwe features - waar je overigens behoorlijk voor kan lobbyen op de nieuwsgroepen - zullen pas bij de 2.0 release toegevoegd worden.
2.4.5
Compilers, compilatie en uitvoering
Op dit moment zijn er twee implementaties. Het Digital Mars DMD package voor Win32 en x86 Linux. en het GCC D Compiler package voor verschillende platforms waaronder Windows en Mac OS X.[14] Net als bij C++ wordt D code rechtstreeks naar de native machinetaal gecompileerd. Broncode bestanden krijgen de .d extensie. Om een programma te compileren gebruik je het commando dmd - waarmee de compiler gestart wordt - gevolgd door alle nodige bronbestanden. Het uitvoerbare bestand krijgt de naam van het bestand waarin het hoofdprogramma zit - met als extensie die van een uitvoerbaar bestand van het onderliggende besturingssysteem.
2.4.6
Features
Hieronder vermelden we een aantal features die D typeren. De meesten van hen worden in het volgende hoofdstuk in detail besproken. 2.4.6.1
Object Ori¨ entatie
D gebruikt klassen om het object-geori¨enteerd paradigma te ondersteunen. Het is dus mogelijk volgens het OO principe te werken, maar dit is zeker niet verplicht zoals bij Java32 . Er wordt gebruik gemaakt van enkelvoudige overerving van klassen en meervoudige overerving van interfaces. De Object-klasse is de wortel van alle objecten. Klassen worden geinstantieerd by 32
Of zoals Dermot Hogan het zegt : ”Object orientation is treated as a tool, not as the Word Of God.”[54]
pagina 46
2.4 D
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
reference. Operator overloading is mogelijk in D. 2.4.6.2
Metaprogramming
Ondersteund door een geavanceerd template systeem, compile time functie uitvoering, tuples en string mixins. 2.4.6.3
Productiviteit
[14] Modules en Packages Broncode bestanden komen overeen met een module. Een module kan ge¨ımporteerd worden in een andere door het import sleutelwoord te gerbuiken. Modules kunnen gebundeld worden in packages. Opvallend is de mogelijkheid om modules slechts gedeeltelijk te importeren. Voorbeeld : 1
import s t d . s t d i o : w r i t e f l n ; // e n k e l w r i t e f l n g e i m p o r t e e r d
Arrays D maakt gebruik van statische en dynamische arrays. Ook met pointers werken is nog mogelijk. Daarnaast zijn er ook associatieve arrays die een map functionaliteit bieden. Met array slicing kunnen delen van arrays geselecteerd worden. Functions Nested functions, function literals, dynamic closures en inout parameters zijn mogelijk in D. 2.4.6.4
Performantie
Gestandaardiseerd inline assembler [120, 12, 14] D voorziet de mogelijkheid om binnen de D code blokken assembler code te schrijven. Deze code komt dan binnen het asm{} attribuut. Dit maakt het voor systeem programmeurs mogelijk low-level features van de processor te benaderen. Dit kan nodig zijn om programma’s te schrijven die de onderliggende hardware direct aanspreken zoals besturingssystemen en device drivers. Aangezien de inline assembler gestandaardiseerd is, is ze portabel tussen verschillende compilers. Dit is beter dan in de C/C++ wereld waar alle compiler producenten de taal uitbreiden met een inline assembler die incompatibel is met al de andere. De standaard bibliotheek van D (link naar Phobos) bewijst het nut van de inline assembler aangezien veel routines ermee ge¨ımplementeerd zijn. De code is dezelfde voor de Windows en Linux versie. De mogelijkheid om assembler code te schrijven onderscheidt D duidelijk van talen zoals C# en Java.
pagina 47
2.4 D
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
Geheugenbeheer D heeft een ingebouwde garbage collector maar het is nog steeds mogelijk om zelf het geheugen te controleren door de nodige constructoren en destructoren te gebruiken en ze op te roepen respectievelijk met het new en delete sleutelwoord. Robuustheid Het stokpaardje van Walter Bright zijn features die zorgen voor betere foutdetectie en optimalisatie van code. In zijn ogen zijn het precies deze features die als maar belangrijker worden in professionele software ontwikkeling. In D zijn dergelijke features - zoals contract programming, unittests, gegarandeerde initialistie en scope guards - dan ook rechtstreeks in de taal ingebouwd. Er is ook een systeem voor exception handling. Men hoopt dat door de testfunctionaliteit in de taal zelf te bouwen programmeurs er sneller een gewoonte van zullen maken[4]. De meeste van deze features worden in volgend hoofdstuk meer in detail besproken.
2.4.7
Ondersteuning
De twee grootste beperkingen rond de programmeertaal D die veel potenti¨ele gebruikers afschrikken zijn wellicht het gebrek aan een geavanceerde IDE en een goed boek. Voor veel bestaande IDE’s bestaan er wel plugins en er zijn ook een aantal nieuwe speciaal voor D in ontwikkeling, maar het gebruiksgemak van een geavanceerde IDE is bij weinigen te vinden. Toch zijn er projecten die rijper worden en vroeg of laat zal er wellicht wel een IDE ontstaan die naast basis features zoals code completion en een object browser ook automatisch (gedeeltelijke) documentatie genereert, een gebruiksvriendelijke debugger heeft, unittests kan genereren en uitvoeren,... De documentatie die bestaat over de taal D is ondertussen al redelijk uitgebreid, maar een goed handboek dat, naast het bespreken van de features van de taal, met voorbeelden ´e´en en ander duidelijk maakt is nog steeds niet beschikbaar. Wie echter de moeite neemt om het internet af te surfen, vindt toch wel heel wat informatie, te beginnen met de thuispagina van D. [14] Naast de standaard bibliotheek Phobos bestaat sinds februari nu ook Tango. Tango is ook een runtime bibliotheek voor D en heeft extra functionaliteit tegenover Phobos. Veel gelijkaardige functionaliteit is in Tango anders ge¨ımplementeerd en het al dan niet ”beter” zijn van beide bibliotheken leidde in de D gemeenschap soms tot verhitte discussies. Het probleem is dan ook dat beide bibliotheken niet samen gebruikt kunnen worden. Er zijn nog vele andere projecten betreffende IDE’s, systeem- en GUI bibliotheken,... De meeste van hen zijn te vinden op dsource33 .
2.4.8
De toekomst
Er zijn enorm veel programmeertalen en er zullen nog nieuwe ontwikkeld worden. En zoals in heel de computerwereld is de kans dat een technologie doorbreekt niet groot en heel moeilijk in te schatten. Veel hangt ook af van de industrie. Als die op de kar springt, kan het vlug gaan. Om een technologie levensvatbaar te maken is er meer nodig dan alleen de kwaliteit van de 33
http://www.dsource.org/projects/
pagina 48
2.4 D
Hoofdstuk 2. Overzicht van de gebruikte programmeertalen
technologie, er moet ook een business model rond gebouwd zijn, maar wat is hier het business model merkt Jeff Hammond terecht op in [78]. Ondertussen groeit de D-gemeenschap echter gestaag verder (getuige daarvan het lineair stijgende aantal posts op de nieuwsgroepen [31] en het stijgende aantal projecten op dsource[38]) en meer en meer mensen proberen de taal uit, dikwijls met positieve ervaringen tot gevolg. Ondertussen wordt D ook al hier en daar professioneel gebruikt34 .
34
http://igesundes.no/ een klein consultant bedrijf
pagina 49
Hoofdstuk 3. Theoretische vergelijking
Hoofdstuk 3
Theoretische vergelijking 3.1 3.1.1
Basisfunctionaliteit Terminologie
Hieronder zullen we wat terminologie formuleren. Maar al te vaak zijn er heel wat verschillen tussen de verschillende talen. We willen enkele van de gebruikte termen dan ook eens verduidelijken en catalogiseren gebaseerd op [67]. Identifier Een naam voor een bepaalde entiteit. Mogelijke entiteiten zijn variabelen, userdefined types, methoden, pakketten, . . . Class Member De data en functies in een klasse Data Member De members die de data bevatten voor de klasse. Dit zijn de velden, constanten en events. Ze kunnen statisch zijn of niet. • Velden zijn de variabelen geassocieerd met de klasse. • Constanten kunnen op eenzelfde manier als velden met de klasse geassocieerd worden. • Events zijn de klassemembers die een object toelaten een luisteraar te verwittigen indien iets vermeldingswaardig met het object gebeurt. Dit kan bijvoorbeeld het wijzigen van een property zijn. Function Member Dit zijn de klassemembers die functionaliteit voorzien voor het manipuleren van de data in de klasse. Hiertoe worden methoden, properties, constructors, finalizers, operatoren en indexers gerekend. • Methods zijn functies geassocieerd met een welbepaalde klasse. Deze kunnen ook statisch of generiek zijn. • Properties kunnen op gelijkaardige manier als publieke velden door gebruikers van de klasse benaderd worden. Ze geven toegang tot bepaalde eigenschappen van de klasse (en dus bepaalde private velden).
pagina 50
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
• Constructors zijn speciale functies die automatisch opgeroepen worden wanneer het object wordt ge¨ınstantieerd. Ze hebben dezelfde naam als de klasse waarin ze zich bevinden en hebben geen return type. • Finalizers zijn gelijkaardig aan constructor maar worden pas opgeroepen wanneer het object niet langer nodig is of de scope verlaat. Ze zijn het equivalent van destructors, maar hebben wel het nadeel niet-deterministisch te zijn. • Operators zijn bijvoorbeeld + en - . Je kan deze toepassen om in dit voorbeeld dan objecten op te tellen of af te trekken. Ze geven een eenvoudige voorstellingswijze voor een bepaalde actie. • Indexers laten ons toe om objecten op eenzelfde manier te benaderen als een array of een collectie. Nested types Deze kunnen naast klassemembers voorkomen in de klasse.
3.1.2
Conventies
Volgens [67] zijn conventies bepaalde regels die strikt of minder strikt gevolgd dienen te worden. Ze staan in voor de goede werking van het programma of een stuk code. Ze zorgen ook voor een meer homogene stijl van programmeren waardoor programmeurs elkaars code sneller en eenvoudiger zullen begrijpen. 3.1.2.1
Naam conventies
Er zijn verschillende manieren om identifiers te benoemen. Camel Casing Namen beginnen met kleine letter. Indien een naam bestaat uit meerdere woorden worden die aan mekaar geplakt en begint elk nieuw woord met een hoofdletter. Vb : ikBenEenIdentifier Pascal Casing Zoals camel casing maar een naam begint met een hoofdletter. Vb : IkBenEenIdentifier Hungarian Notation Men voegt als prefix ´e´en of meerdere letters toe aan de naam die verwijzen naar het datatype. Vb : bIkBenEenIdentifier voor een bool(ean) idientifier all caps Elke letter van de naam is een hoofdletter. Vb : IKBENEENIDENTIFIER no caps Elke letter van de naam is een kleine letter. Vb : ikbeneenidentifier Verder is het ook belangrijk dat de naam van een variabele duidelijk weergeeft waar de variabele voor gebruikt wordt en welke data in de variabele vervat zit.
pagina 51
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
Naamruimten zijn een belangrijk onderdeel in het software ontwerp. Zij zorgen ervoor dat alles mooi gescheiden blijft en geen naamconflicten zullen voorkomen. Naamruimten dienen zelf een gepaste en unieke naam te krijgen om problemen met bijvoorbeeld identieke klassenamen te vermijden. Een soort hi¨erarchie is hier geen overbodig luxe. C++ Een naam bestaat uit een stel letters en cijfers. Het eerste karakter moet een letter zijn, waarbij de underscore ook als letter geldt. Sleutelwoorden zijn niet toegestaan als naam voor een door de gebruiker gedefinieerde entiteit. Namen die met een underscore beginnen, zijn voor speciaal gebruik gereserveerd in de implementatie en de run-time-omgeving. Zulke namen moeten dus vermeden worden in toepassingsprogramma’s. Hoofdletters en kleine letters worden als verschillend onderscheiden. Er is dus een verschil tussen variabelen beginnende met kleine letter en deze beginnende met een hoofdletter. Zo zal de compiler mijnvariabele als een andere variabele dan Mijnvariabele zien. Het is echter niet aangeraden om variabelennamen te kiezen die slechts in een hoofd- of kleine letter verschillen. [92] C# Ook hier bestaan strikte regels voor identifiers of variabelennamen. Deze regels schrijven voor welke namen je kan gebruiken voor je variabelen, klassen, methoden, ... Deze regels worden door de C# compiler gecontroleerd en zijn dus niet zomaar richtlijnen. Identifiers zijn casesensitive net als bij C++. Identifiers moeten beginnen met een letter of een underscore (ook Unicode karakters). Ze kunnen wel numerieke karakters bevatten. Sleutelwoorden kunnen niet als identifiers worden gebruikt. Ook dient men er rekening mee te houden dat men geen sleutelwoorden uit andere .NET talen zal nemen. Indien je wel een sleutelwoord als identifier wil gebruiken, kan dit door het @ symbool vooraan te plaatsen. In dit geval zal de compiler weten dat het hier geen keywoord betreft maar wel een identifier. Camel casing wordt toegepast voor bijvoorbeeld een private member field. Zijn bijhorende property gebruikt Pascal casing. Java Voor klassen, interfaces en enums wordt Pascal casing gebruikt. Voor methoden en variabelen camel casing. Pakketten worden in no caps geschreven en constanten en enum members in all caps. D Voor klassen, interfaces, structs, unions, enums en templates wordt Pascal casing gebruikt. Voor methoden en variabelen camel casing. Pakketten en modules worden in no caps geschreven en constanten en enum members in all caps.
3.1.3 3.1.3.1
Constanten C++
De gebruiker kan met const een constante defini¨eren. Een constante moet steeds worden ge¨ınitialiseerd omdat er later niets meer kan aan worden toegekend. Men dient op te mer-
pagina 52
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
ken dat const het type verandert en beperking oplegt aan de manier waarop het object verder kan worden gebruikt. Indien men const gebruikt bij een pointer, dan zal niet de pointer constant worden maar wel het object waar de pointer naar wijst. C++ heeft een heel scala van subtiliteiten betreffende constanten. Zo kan je constante pointers, variabele pointers naar constanten, constante methoden (die de inhoud van het object waarop de methode is opgeroepen niet veranderen), constante parameters naar methoden, ... gebruiken. [92, 67] 3.1.3.2
C#
In C# zijn de subtiliteiten van C++ verwijderd. Je kan enkel lokale variabelen en velden als constant declareren. In C# dient men een onderscheid te maken tussen het gebruik van const en static readonly. Beide worden gebruikt om constanten te defini¨eren. Het verschil zit hem in het tijdstip van initialisatie. Bij const dient de initialisatie voor het compileren te gebeuren. Het kan soms gebeuren dat je een constante nodig hebt waarbij je de waarde nog moet berekenen at compile time. Daarna zal de waarde niet meer veranderen. In dit geval gebruikt men static readonly. Dit kan bijvoorbeeld zijn om bepaalde settings in te stellen (achtergrondkleur, lettergrootte, ...). Merk op dat gebruik van const automatisch ook het statisch zijn impliceert en daardoor men het sleutelwoord static niet meer hoeft te vermelden. Gebruik van const is ook equivalent aan final in Java. [67] 3.1.3.3
Java
In Java gebruikt men het sleutelwoord final om een constante aan te duiden. Indien deze constante ook gemeenschappelijk dient te zijn voor elke instantie van de klasse wordt er ook nog eens van het static sleutelwoord gebruik gemaakt. 3.1.3.4
D
In D wordt eveneens gebruik gemaakt van het const sleutelwoord om een constante te defini¨eren. Deze constante kan at runtime nog een waarde krijgen aan de hand van constructoren of statische constructoren.
3.1.4 3.1.4.1
Arrays Algemeen
Declaratie Er zijn twee notaties om arrays te declareren, prefix en postfix. Prefix declaraties komen voor de identifier, postfix declaraties erna. 1 2
int [ ] a ; // p r e f i x int a [ ] ; // p o s t f i x
pagina 53
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
Java en D ondersteunen beide notaties, C++ ondersteunt enkel postfix en C# enkel prefix. Je kan je echter de vraag stellen waarom beide notaties mogelijk zijn in Java en D, gezien dat voor enige verwarring kan zorgen. Zo zijn onderstaande lijnen code in Java en D geldig en alle equivalent. 1 2 3
int [ ] [ ] a ; int [ ] a [ ] ; int a [ ] [ ] ; E´en vastgelegde manier lijkt veel duidelijker. De makers van D argumenteren dat het behouden van de postfix notatie de overgang voor C++ gebruikers eenvoudiger moet maken. Array Bounds Checking C#, Java en D ondersteunen dit at runtime door de lengte van een array bij te houden. C++ ondersteunt dit niet daar er enkel een pointer naar de array wordt bijgehouden. D doet ook aan bounds checking at compile time bij statische arrays (zie 3.1.4.5). 3.1.4.2
C++
In C++ hangen arrays en pointers heel nauw samen. De naam van een array kan worden gebruikt als een pointer naar het eerste element. Via pointers en wat pointer arithmetriek kan men eenvoudig de elementen van een array overlopen. 1 2 3
// a r r a y van 10 ch ar ’ s // we k r i j g e n een p o i n t e r naar h e t e e r s t e e l e m e n t t e r u g char∗ p = new char [ 1 0 ] ; Het doorgeven van arrays als argument kan door het doorgeven van een pointer naar zijn eerste element. De grootte van de array dient ook te worden meegegeven, aangezien we de grootte anders niet weten en fouten kunnen optreden daar er geen bounds checking is. Arrays verschillen dus van andere typen doordat ze niet by value worden doorgegeven. Via de valarray en de bijhorende slice array uit de standaard C++ bibliotheek kan men stukken van een array bewerken. Een tweedimensionale tabel kan hiermee gesimuleerd worden in een ´e´endimensionale tabel. Meerdimensionale arrays zijn arrays van arrays. In sommige talen wordt een komma gebruikt voor multidimensionale arrays, maar in C++ is dat een operator en is de komma niet toegestaan in een constante expressie . In de standaard bibliotheek van C++ kan men de map terugvinden. Dit is een container die ook wel als associatieve array of dictionary bekend staat. De map is een container voor paren. Een waarde van het eerste type uit het paar (ook sleutel genoemd) kan gebruikt worden als index. De map geeft dan de bijhorende waarde van het tweede type terug. [92]
pagina 54
3.1 Basisfunctionaliteit
3.1.4.3
Hoofdstuk 3. Theoretische vergelijking
C#
C# ondersteunt ´e´en-dimensionale arrays, rechthoekige en jagged meerdimensionale arrays. E´ en-dimensionale arrays Een voorbeeld: 1
int [ ] numbers = new int [ 5 ] ;
Multidimensionale rechthoekige arrays Een voorbeeld: 1
string [ , ] names = new string [ 5 , 4 ] ;
Array-of-arrays (jagged, hoeft dus niet rechthoekig te zijn) Een voorbeeld: 1 2 3
byte [ ] [ ] s c o r e s = new byte [ 5 ] [ ] ; for ( int x = 0 ; x < s c o r e s . Length ; x++) s c o r e s [ x ] = new byte [ 4 ] ; Je kan ook grotere arrays maken. Hieronder bijvoorbeeld een drie-dimensionale rechthoekige array:
1
int [ , , ] b u t t o n s = new int [ 4 , 5 , 3 ] ; Al deze soorten arrays zijn instances van System.Array. Bijgevolg worden deze op de heap geplaatst, met alle performantiegevolgen van dien. Indien we echter een kleinere meer performante array nodig hebben, kunnen we beroep doen op stackgebaseerde arrays. Deze arrays kunnen we op de stack opslaan en hebben de overhead van referentieobjecten niet. Het aanmaken van een stackgebaseerde array kan aan de hand van stackalloc. Wanneer je gebruik maakt van stackalloc dien je het type data en het aantal items opgeven.
1 2
int s i z e = 2 0 ; // e v e n t u e e l at−runtime b e r e k e n t double∗ pDoubles = s t a c k a l l o c double [ 2 0 ] ; Dit commando alloceert enkel het benodigde geheugen. Het probeert onze array niet te initialiseren. We krijgen hier een pointer naar het eerste element terug. Je kan dan pointer arithmetiek gebruiken om de volgende elementen te benaderen. C# biedt verder ook een eenvoudigere syntax aan aan de hand van ”[]”. Indien p onze pointer is, dan zal p[X] ge¨ınterpreteerd worden als *(p+X) met X het volgnummer van het element. Deze stackgebaseerde arrays zijn dus identiek aan de stackgebaseerde arrays uit C++. Bounds checking gebeurt hier dan ook niet en fouten kunnen optreden. Aangezien .NET normaal niet toelaat om onveilige code te schrijven, dit wil zeggen met gebruik van pointers, dient men het stuk code als unsafe te declareren en expliciet aan de compiler duidelijk te maken dat het om unsafe code gaat. Indien men dit niet doet, zal de compiler gewoonweg niet compileren. De klasse System.Array zorgt evenwel voor effici¨ente toegang tot de elementen. Men kan de elementen onder andere eenvoudig overlopen met foreach. Een groot nadeel is dat je de grootte
pagina 55
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
van de array reeds moet opgeven. Een ArrayList uit de .NET Collections lijkt op een Array met het verschil dat de lijst uitgebreid kan worden. Het is niet mogelijk om een pointer naar een array te laten verwijzen. Dit zou problemen met de garbage collector veroorzaken. [67, 5] ArraySegment Het struct ArraySegment
stelt een segment van een array voor. Hiermee kan men een deel uit een array halen. Er zijn 3 parameters. Het eerste is de array. De volgende parameter is de offset en als laatste het aantal gevraagde elementen. Een voorbeeld van hoe een segment aangemaakt en overlopen wordt, kan u hieronder vinden [67]: 1 2 3 4
int [ ] a r r = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 } ; ArraySegment segment = new ArraySegment( a r r , 2 , 3 ) ; for ( int i = segment . O f f s e t ; i < segment . O f f s e t + segment . Count ; i ++) C o n s o l e . WriteLine ( segment . Array [ i ] ) ; Het is belangrijk hierbij te vermelden dat een arraysegment de elementen van de orignele array niet kopieert. De originele array wordt toegankelijk gemaakt via het arraysegment. Als de elementen van dit segment dus gewijzigd worden, zullen de elementen van de originele array dus eveneens wijzigen. 3.1.4.4
Java
De arrays in Java gedragen zich ongeveer zoals de jagged arrays in C#. Declaratie van meerdimensionale arrays kan wel direct zoals bij de gewone rechthoekige multidimensionale arrays in C#, bv. int bordspel [][] = new int[12][12];. Zoals zichtbaar in dit voorbeeld wordt voor de syntax bij meerdimensionale arrays wel van vierkante haakjes gebruikt gemaakt en niet van komma’s zoals bij de jagged arrays in C#. 3.1.4.5
D int *a; int[3] a; int[] a; int[char[]] a;
Pointer naar data Statische arrays Dynamische arrays Associatieve arrays
Tabel 3.1: D Array Types Er zijn 4 soorten arrays in D. Een pointer naar data is analoog aan een C pointer. Veel wordt dit in D niet gebruikt maar net omwille van de compatibiliteit met C werd dit toegelaten. Er is geen lengte met geassocieerd en er is hier dus ook geen sprake van bounds checking. Statische arrays hebben reeds een vaste lengte bij het compileren. Het voordeel hiervan is dat reeds aan bounds checking gedaan kan worden at compile time. De andere talen kunnen pas at runtime een dergelijk probleem ontdekken. Naast deze bounds checking komen de statische arrays van D het beste overeen met de arrays van de andere talen.
pagina 56
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
In tegenstelling tot de andere hier besproken talen ondersteunt D ook dynamische arrays, of met andere woorden arrays waarvan de lengte kan varieren. D heeft nog een speciaal type array, namelijk associatieve arrays die het best gezien kunnen worden als een map. Het idee is dat de index waarmee de array geadresseerd wordt niet van het type int hoeft te zijn. Hieronder volgt een voorbeeld van een frequentietabal van strings met een associatieve array. 1 2 3
int [ char [ ] ] b ; // KeyType = c har [ ] , ValueType = i n t b[ ” hallo ” ] = 3; // v o e g key ” h a l l o ” ’ t o e met v a l u e 3 b . remove ( ” h a l l o ” ) ; // v e r w i j d e r een key , v a l u e paar In D is het mogelijk een subarray van aan array te specifi¨eren door gebruik te maken van array slicing.
1 2 3 4
int [ 1 0 ] a ; int [ ] b ; b = a [ 1 . . 3 ] ; // b i s nu een a r r a y van l e n g t e 2 // en b e v a t a [ 1 ] and a [ 2 ] In theorie lijkt dit heel mooi, maar aangezien array slicing de data niet kopieert maar er enkel naar verwijst kan dit in praktijk wel eens voor problemen zorgen. Je kan dit op te lossen door de array slice te kopi¨eren met het .dup attribuut dat elke array heeft.
1
b = a [ 1 . . 3 ] . dup ;
Built-in Strings D heeft geen type String in de taal zitten maar ondersteunt wel zowat elke operatie die nodig is voor strings dankzij de dynamische char[] arrays en de std.string bibliotheek. Als we in D over strings spreken, hebben we het dan ook altijd over char[]. We kunnen strings kopi¨eren, vergelijken en concateneren. 1 2 3
str1 = str2 ; if ( str1 < str3 ) . . . s t r 4 ˜= s t r 1 ; Merk op dat ˜ wordt gebruikt en niet + voor concatenatie. Dit is om altijd een duidelijk onderscheid te kunnen zien tussen iets samenvoegen of iets optellen. In andere talen kan dat soms verwarrend worden daar de + operator in beide betekenissen gebruikt wordt.
3.1.5
Functies
De meest gebruikelijke manier om iets gedaan te krijgen is het aanroepen van een functie. Een functie specifieert de manier waarop een operatie moet worden uitgevoerd. De semantiek van het doorgeven van argumenten is dezelfde als die bij initialisatie. Het type van de argumenten wordt gecontroleerd en als het nodig is impliciet geconverteerd.
pagina 57
3.1 Basisfunctionaliteit
3.1.5.1
Hoofdstuk 3. Theoretische vergelijking
Algemeen
We geven hieronder eerst een kort overzicht van begrippen die in de context van functies belangrijk zijn en door de besproken talen al dan niet gebruikt worden. Daarna zullen we voor elke taal specifiek kijken hoe ze deze algemene begrippen ondersteunen. Functieparameters Parameters kunnen by value of by reference doorgegeven worden. Het is echter niet zo eenvoudig dat een taal ofwel by value ofwel by reference werkt. De manier waarop de verschillende talen precies werken wordt daarom verder voor elk apart besproken. Variadic function Variadic functions zijn functies die een willekeurig aantal argumenten kunnen hebben. Ondersteuning voor variadic functions verschilt sterk van taal tot taal. Er zijn een aantal wiskundig en logische operaties die als vanzelfsprekend vragen om een variadic function. Het maken van een som van getallen of de concatenatie van strings en andere sequenties zijn voorbeelden van operaties die met een willekeurig aantal argumenten kunnen werken. Variadic functions worden ook vaak gebruikt voor het formatteren van de output. Deze functies kunnen in sommige talen zoals bv. C echter ook een aantal problemen met zich meebrengen in verband met typeveiligheid. Zo wordt in C toegelaten dat de functie meer argumenten van de stapel haalt dan er eigenlijk maar geplaatst werden. Dit leidt tot onvoorspelbaar gedrag. In D is dit typeveilig. [104] Function inlining Hieronder verstaan we dat de compiler elke oproep van een functie vervangt door zijn eigenlijke implementatie. Op die manier wordt een functie-aanroep uitgespaard en dit kan de uitvoeringssnelheid ten goede komen, zeker bij functies die een korte uitvoeringstijd hebben. In Java is function inlining niet voorzien. In C++ kan je het forceren door het keyword inline voor een functie te zetten. Het is echter onmogelijk om te garanderen dat de inline functie ook daadwerkelijk wordt ingevuld. Dit hangt vaak af van hoe slim de compiler is. In C# wordt function inlining toegepast achter de schermen naar goeddunken van de jit-compiler. In D is het ook de compiler die kiest maar het wordt enkel toegepast wanneer bij het compileren de parameter -inline meegegeven wordt. [92, 14] Function pointers en delegates Functiepointers zijn zoals het woord zegt pointers naar functies. Talen zoals C++ en D maken gebruik van deze functiepointers. Bij dereferentie van de pointer zal de functie aangeroepen worden. Daarnaast hebben we ook nog functieobjecten, ook functors genoemd, die gelijkaardig zijn aan de pointers. Functors zijn echter krachtiger dan gewone functiepointers, doordat ze eigen data kunnen bijhouden en op die manier closures kunnen emuleren. Een closure is een semantisch concept dat verwijst naar een functie met zijn context. Wanneer de functie opgeroepen wordt, dan kan deze de elementen uit zijn context gebruiken.
pagina 58
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
Daar verschillende talen hebben niet steeds dezelfde definitie voor de context of lexicale omgeving hebben, verschilt ook de definitie van closure ietwat van taal tot taal. Dit leidt vaak tot heel wat verwarring en discussie omtrent het al dan niet als feature in de taal vervat zijn van closures. De mening van de binding van de variabele verschilt soms. Imperatieve talen binden variabelen aan een lokatie in het geheugen. Daar kan dan een waarde in zitten. De binding in de closure kan niet veranderen en is bij creatie vastgelegd. Echter, de waarde op deze geheugenlocatie kan dit wel. Aan de andere zijde binden bijvoorbeeld vele functionele talen de variabelen direct aan de waarden. In dit geval zullen de waarden of variabelen die in de closure vervat zitten nooit veranderen. [18] Java maakt geen gebruik van functiepointers. C# maakt gebruik van delegates, dit zijn typeveilige functiepointers. Ook D kan, naast de functiepointers, van delegates gebruik maken [45]. Nested funtion Onder een nested function verstaan we niet meer dan een functie gedefinieerd binnenin de definitie van een andere functie. Nested functions laten ons toe om bepaalde informatie te verbergen. Kleine subtaken die enkel lokaal betekenisvol zijn kunnen dan als een geneste functie geschreven worden. Dit heeft als voordeel dat de code staat waar ze gebruikt wordt en dat andere delen van het programma niet bevuild worden met kleine functies. Als enige van de hier besproken talen ondersteunt D nested functions [70]. 3.1.5.2
C++
Function parameters Het doorgeven van parameters in C++ kan op twee manieren gebeuren. Default is dit by value (een kopie). Hierbij wordt de waarde van de parameter doorgegeven. Wanneer we parameters by reference wensen door te geven dienen we ”&” te gebruiken. Hier zal slechts een referentie naar de parameter worden doorgegeven. Dit is effici¨enter [92]. Function pointers Een function pointer is een pointer die het adres van een functie bevat. Function pointers in C++ zijn niet meer dan pointers naar het geheugen. Je weet niet waarnaar de pointer wijst. Dit is dus niet echt veilig. Een voorbeeld [92]: 1 2 3 4 5 6
void e r r o r ( s t r i n g s ) { void ( ∗ e f c t ) ( s t r i n g ) ; void f ( ) { e f c t = &e r r o r ; efct (” error ” ); }
... } // p o i n t e r nr f u n c t i e // e f c t w i j s t naar e r r o r // aanroep e r r o r v i a e f c t
Vaak is een array van functiepointers handig. Men kan ze gebruiken om bijvoorbeeld voor een menu de eventafhandeling te doen.
pagina 59
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
Constante functies Het sleutelwoord const plaatst men bij deze functies indien ze geen aanpassingen doen aan de de attributen van de klasse. Functieobjecten (Functors) Dit zijn objecten die zich als functies gedragen. Ze kunnen at runtime gecre¨eerd worden en kunnen toestand bijhouden. Ze kunnen wel niet impliciet lokale variabelen opnemen zoals echte closures kunnen. Er zijn wel reeds een aantal voorstellen om closures op te nemen in de nieuwe c++ standaard. Functors zijn van belang daar we hiermee ingewikkelde code kunnen schrijven die ingewikkelde operaties als parameters meekrijgt. In de standaardbibliotheek zijn er bijvoorbeeld veel algoritmen die een functie aanroepen voor ieder element in een container. Om interessante dingen te doen willen we echter zelf code aangeven die door de algoritmen dienen te worden uitgevoerd. Vaak moet een functor dan ook toestanden bijhouden van de verschillende aanroepen en moet hij soms het (gecumuleerde) resultaat van vele aanroepen teruggeven. Ze worden ge¨ımplementeerd aan de hand van een klasse met ´e´en of meerdere private members om de toestand in op te slaan. Verder dient ook de operator ”()” over te laden [92, 48, 47, 49]. Een eenvoudig voorbeeld: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// f u n c t o r k l a s s e d i e 2 i n t ’ s z a l v e r g e l i j k e n class compare class { public : // o p e r a t o r o v e r l a d i n g bool operator ( ) ( int A, int B) { return (A < B ) ; } }; ... // D e c l a r a t i e van de s o r t e e r f u n c t i e . template void s o r t i n t s ( int ∗ b e g i n i t e m s , int num items , ComparisonFunctor c ) ; ... int main ( ) { int i t e m s [ ] = { 4 , 3 , 1 , 2 } ; c o m p a r e c l a s s f u n c t o r ; // de f u n c t o r s o r t i n t s ( items , 4 , f u n c t o r ) ; // s o r t e e r i t e m s adhv f u n c t o r } 3.1.5.3
C#
Function parameters Parameters in C# kunnen by reference of by value worden doorgegeven. Default zijn alle parameters by value doorgegeven in die zin dat voor waardetypes een kopie wordt doorgegeven. Voor referentietypes die enkel een referentie zijn, wordt de referentie by value doorgegeven en niet het gehele object. Het komt dus neer op het doorgeven by reference. Het wordt echter zo aangebracht in de literatuur [67]. De parameters kunnen ook voorafgegaan
pagina 60
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
worden door enkele sleutelwoorden waaronder out en ref. Deze parameters maken deel uit van de signatuur van de functie. out Om meer dan ´e´en output waarde van een enkele methode te krijgen, kan men gebruik maken van out. De variabele dient hier niet ge¨ınitialiseerd te zijn, wat normaal door C# wel steeds verwacht wordt. ref Hiermee kan men de parameter by reference doorgeven net zoals in C++ met ”&”. De variabele moet wel een waarde hebben en dus ge¨ nitialiseerd zijn. params Soms hebben we een variabel aantal parameters nodig die aan een functie zullen worden gegeven. Men wil bijvoorbeeld een somfunctie maken die de som van de parameters berekent, hoeveel dit er ook mogen zijn. We kunnen dan het params sleutelwoord gebruiken. De syntax voor het gebruik is als volgt [77]: 1
params d a t a t y p e [ ] argument name De parameter in de functie dient dus een ´e´endimensionale array en tevens de laatste parameter te zijn. Men kan er dan als van een gewone array gebruik maken.
1 2 3 4 5
s t a t i c int som (params int [ ] x ) { int r = 0 ; foreach ( int i in x ) r += i ; return r ; }
6 7 8 9 10 11
s t a t i c void Main ( string [ ] a r g s ) { int w = 0 , x = 1 , y = 2 , z = 3 ; C o n s o l e . WriteLine ( som (w, x , y , z ) ) ; // o u t p u t : 6 }
Function pointers en delegates Een delegate in C# heeft niet direct zijn gelijke in C++. Het dient wel voor dezelfde taak als functiepointers in C++. Een delegate is een functiepointer ingepakt in een speciale klasse, samen met een referentie naar het object waarop de methode uitgevoerd dient te worden. Dit betekent dat de delegate in tegenstelling tot de functiepointer uit C++ (die slechts een pointer is) genoeg informatie bevat om een instantiemethode op te roepen. De delegate zorgt voor typeveiligheid aangezien hij zal controleren dat er geen methode aangeroepen wordt die niet overeenstemd met de signatuur die bij declaratie werd vooropgesteld. Er wordt echter niet gecheckt op welk type object de methode zal uitgevoerd worden. Ook het al dan niet statisch zijn of een instantiemethode zijn, wordt niet gecontroleerd. In het geval dat de delegate een methode voorstelt die void teruggeeft, dan is de delegate een multicast delegate en kan hij tezelfdertijd meerdere methoden voorstellen. Een voorbeeld van het gebruik van delegates voor eventhandling kan je vinden in 3.1.6.2
pagina 61
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
Tot zover moest een methode reeds bestaan opdat de delegate zou werken. Aan de hand van anonieme methoden en delegates kan dit vermeden worden. Men zorgt hiermee ook voor minder overhead aangezien de methode niet gedefinieerd dient te worden. Ze wordt enkel rechtstreeks gebruikt waar nodig. Deze anonieme delegates kunnen als een soort closures beschouwd worden. De C#-compiler zorgt voor de conversie waarbij een klasse en een object gecre¨eerd wordt die de context bevat. Dit principe wordt in C# 3.0 nog uitgebreid via de lambda expressies. De syntax wordt hier nog eenvoudiger. Voor een voorbeeld van anonieme methoden en lambda expressions zie sectie 2.2.6.3. [67, 19] 3.1.5.4
Java
Function parameters Java werkt by value. Er kunnen dan ook alleen primitieve types en object referenties doorgegeven worden. Objecten zelf worden dus nooit doorgegeven. Dit wordt gedaan omdat een object een grote hoeveelheid data kan bezitten. De opgeroepen methode krijgt dan een kopie van de primitieve waarde of de object referentie en werkt daar verder mee. Function pointers en delegates Java bezit geen function pointers of delegates. Java voorziet wel een gelijkaardige functionaliteit als delegates met inner classes. Zoals in volgend voorbeeld te zien, is de syntax echter veel uitgebreider en minder gebruiksvriendelijk dan die voor delegates in Java en C#. 1 2 3 4 5
o b j . r e g i s t e r H a n d l e r (new Handler ( ) { public void h a n d l e I t ( Event ev ) { methodOne ( ev ) ; } });
3.1.5.5
D
Function parameters [14] Parameters van een functie kunnen in, out of inout zijn. Standaard is elke parameter van het type in. Het sleutelwoord in is dus eigenlijk overbodig. Dit type parameters wordt op dezelfde manier behandeld als in Java. D werkt dus standaard by value met dien verstaande dat een kopie van de referentie naar een object wordt doorgegeven, geen kopie van het object zelf. Het standaard gedrag kan omzeild worden door parameters out of inout te maken. Parameters van het type out zijn referenties naar variabelen die worden ge¨ınitialiseerd (krijgt de standaard waarde van het type van de parameter) en een waarde krijgen toegewezen door de functie. Parameters van het type inout zijn referenties naar variabelen wiens waarde kan gebruikt en aangepast worden door de functie. Voorbeeld: 1 2
void main ( ) {
pagina 62
3.1 Basisfunctionaliteit
int x = 1 , y = 2 , z = 3 ; foo (x , y , z ) ; w r i t e f l n ( ”x = %d , y = %d , z = %d” , x , y , z ) ; // d i t g e e f t : x = 1 , y = 4 , z = 9
3 4 5 6 7 8 9 10 11 12 13
Hoofdstuk 3. Theoretische vergelijking
} void { // a b c }
f o o ( int a , out int b , inout int c ) a = 1, b = 0, z = 3 += 1 ; += 4 ; ∗= 3 ;
Wanneer een object als parameter wordt meegegeven is het gedrag anders dan bij primitieve types. Voor objecten wordt hun referentie doorgegeven dus een wijziging van het object in de opgeroepen functie heeft zowiezo effect op het object dat werd meegegeven. Hier moet je echter opletten met de out parameter. Zoals eerder gezegd wordt een parameter van dat type bij het begin van de functie ge¨ınitialiseerd. Het gevolg is dat het object nu null wordt. Met andere woorden, het parametertype out heeft weinig nut wanneer het gebruikt wordt voor een object. Nested functions [14] Er zijn meerdere niveau’s van nesting mogelijk. Een geneste functie heeft toegang tot de lokale variabelen van de omliggende functie. Naast omliggende functies kunnnen ook functies op het zelfde niveau van nesting (binnenin een omliggende functie) een geneste functie oproepen. Voorbeeld: 1 2 3 4 5 6
int bar ( int a ) { int f o o ( int b ) { return b + 1 ; } int abc ( int b ) { return f o o ( b ) ; } // ok return f o o ( a ) ; }
7 8 9 10 11 12
void t e s t ( ) { int i = bar ( 3 ) ; // ok int j = bar . f o o ( 3 ) ; // n i e t ok , b a r . f o o n i e t z i c h t b a a r }
Function pointers versus delegates [14] Function pointers en delegates wijzen beiden naar een functie, maar zijn zeker niet hetzelfde. Het grote verschil is dat een delegate naast een wijzer naar een functie ook een object referentie bevat. Dit combineren van een function pointer en context noemt men dynamic closure. Function pointers worden gebruikt voor non-member functions en static functions. Dit is logisch
pagina 63
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
want net dan is er geen nood aan een context horende bij de functie. We verduidelijken dit met voorbeelden. Voorbeeld 1: 1 2 3
int function ( ) f p ; int f u n c ( ) { return 2 ; } f p = &f u n c ; // ok Voorbeeld 2:
1 2 3 4 5 6 7 8 9 10 11 12
c l a s s OB { private : int a = 5 ; public : s t a t i c int f u n c ( ) { return 2 ; } int member ( ) { return a ; } } int function ( ) f p ; OB o = new OB( ) ; f p = &o . f u n c ; // ok f p = &o . member ; // n i e t ok Delegates worden gebruikt voor member functions. Voorbeeld 1:
1 2 3
int delegate ( ) dg ; int f u n c ( ) { return 2 ; } dg = &f u n c ; // n i e t ok Voorbeeld 2:
1 2 3 4
int delegate ( ) dg ; OB o = new OB( ) ; dg = &o . f u n c ; // n i e t ok dg = &o . member ; // ok
Function literals [14] Enkel D beschikt over function literals.
3.1.6
Events
Een event is een gebeurtenis in een computerprogramma waarop dit programma kan reageren. Vooral in GUI-omgeving is dit een belangrijk concept, maar het kan ook daarbuiten gebruikt worden.
pagina 64
3.1 Basisfunctionaliteit
3.1.6.1
Hoofdstuk 3. Theoretische vergelijking
C++
C++ biedt geen ondersteuning voor event handling. Externe bibliotheken voor bijvoorbeeld grafische toepassingen moeten dan ook hun eigen event handling systeem hebben. 3.1.6.2
C#
Events zijn gespecialiseerde delegates gebruikt om het notificatiemodel te ondersteunen. Een event is een delegate met de volgende signatuur : 1
delegate void E v e n t K l a s s e ( o b j Sender , EventArgs e ) ; Dit is de signatuur die elke eventhandler dient te hebben. Het sender object is een referentie naar het object dat het Event veroorzaakte. EventArgs (of afgeleiden hiervan) wordt gebruikt om informatie over het event mee te sturen. Om een event te declareren gebruikt men de volgende syntax:
1
public e v e n t E v e n t K l a s s e OnEvent ; Clients gebruiken de ”+=” syntax om aan multicast delegates aan te geven.
1 2
// EventBron v e r w i j s t naar de k l a s s e i n s t a n t i e d i e h e t e v e n t b e v a t EventBron . OnEvent += MijnHandler ; Aangezien het gaat om een multicast delegate, zullen alle handlers die er bij geregistreerd zijn verwittigd worden. Deze eventhandler zien er als volgt uit:
1
OnEvent ( this , new EventArgs ( ) ) ; 3.1.6.3
Java
Java beschikt over event handling. In de bibliotheek bestaat een klasse EventObject waarvan alle bestaande events in Java overerven. Een event wordt in Java opgevangen door een listener. Er kunnen ´e´en of meerdere luisteraars aan een object dat events kan veroorzaken worden toegekend. Als er een event zich voordoet wordt een event object van het gepaste type aangemaakt. Dit object wordt dan doorgegeven aan de verschillende luisteraars. Deze luisteraars moeten een interface implementeren die de gepaste methode voor deze event bevat. 3.1.6.4
D
Net als C++ heeft D geen rechtsstreekse ondersteuning voor event handling.
3.1.7 3.1.7.1
Andere String switches
Een switch wordt steeds gevolgd door een aantal ”cases” met constanten waarmee vergeleken zal worden. De constanten moeten discreet zijn. Indien de expressie in het switch argument naar een
pagina 65
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
waarde in de mogelijke cases evalueert zullen de daarop volgende codelijnen worden uitgevoerd. Daarna volgt een break statement waardoor we het switchstatement kunnen verlaten. Indien geen van de mogelijkheden overeenkomt, wordt de default case genomen. Men is niet altijd verplicht deze te voorzien. In C# zou de switch een veiligere variant van C++ moeten zijn, doordat het toelaten van doorvalpaden verboden wordt. De compiler genereert een error indien er geen break volgt. Een uitzondering op deze regel is het geval waarbij men lege cases aantreft. Indien men wel wenst dat er nog een ander deel uitgevoerd wordt, dient men gebruik te maken van het ”goto”-statement. In C# is de volgorde van de cases niet van belang. C# laat ook strings toe als de geteste variabele. Dit is niet zo in C++ en Java, wel in D. Men kan hier net als in C++ en D switches toepassen op enums. In Java kan dit niet. Men kan daar ook niet gebruik maken van strings als switch argument. De switch expressie in Java kan enkel van het type char, byte, short, of int zijn. Strings zijn dus niet toegelaten. Enum switches zijn toegelaten. [92, 67, 91] 3.1.7.2
Aliases versus Typedef
Een alias of typedef wordt meestal gebruikt om voor een complexe naam een nieuwe eenvoudige te defini¨eren. De nieuwe naam kan dan verder in het programma gebruikt worden. Afhankelijk van de gebruikte taal verschillen de betekenissen. Dit wordt vaak gebruikt bij het gebruik van templates om zo ingewikkelde constructies toch iets leesbaarder te schrijven. C++ [92] In C++ kan men een alias defini¨eren voor een namespace. Vaak zijn korte namen conflicterend en lange namen onhandig in gebruik. Met een alias kunnen deze problemen opgelost worden. 1
MijnNamespace = M i j n B e d r i j f j e O n z e K l a s s e n B a s i s k l a s s e n ; We maken gebruik van ”::” om naar klassen uit de alias te refereren. Wanneer men een type een nieuwe naam wenst te geven, maakt men gebruik van ”typedef”. Het is handig als afkorting voor een type met een lastige naam. Er wordt hier echter geen nieuw type gemaakt. De nieuwe naam is enkel een synoniem. C# [67] C# bestaat volgt dezelfde lijn als C++. Er is niet echt een sleutelwoord alias. Men kan wel gebruik maken van het ”using” sleutelwoord.
1
using MijnNamespace = M i j n B e d r i j f j e . OnzeKlassen . B a s i s k l a s s e n ; en bij gebruik:
1 2
MijnNamespace : : K l a s s e U i t B a s i s k l a s s e n Test = new MijnNamespace : : K l a s s e U i t B a s i s k l a s s e n ( ) ; Een typedef bestaat hier niet.
pagina 66
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
Java [17, 103] Dit bestaat niet in Java. Er zijn wel reeds enkele voorstellen hieromtrent geweest. D [2] D is verschillend ten opzichte van de voorgaande talen. In D zal men bij gebruik van typedef een nieuw type defini¨eren. Het typeersysteem zal dit type dan ook echt als een nieuw type gaan behandelen. 1
typedef int m i j n i n t ;
2 3 4 5 6 7
void f o o ( int x ) { . . . } void f o o ( m i j n i n t m) { . . . } ... mijnint b ; foo (b ) ; // r o e p t f o o ( m i j n i n t ) op Typedefs kunnen ook een default waarde geven die verschillend is van de default waarde van het onderliggende type: typedef int m i j n i n t = 7 ; m i j n i n t m; // g e n i t i a l i s e e r d op 7
1 2
In D wordt dan ook nog alias gebruikt. De betekenis van alias is dan weer vergelijkbaar met typedef in C++. Met alias wordt echt een nieuwe naam voor een type gedefinieerd en geen nieuw type. Dit geldt hier ook voor methoden. 3.1.7.3
Preprocessor directieven
In de inleiding op C++ (2.1.5) werd reeds kort uitgelegd wat er mogelijk is met preprocessing. De preprocessor zal de broncode scannen op preprocessor directieven en alvorens compilatie bepaalde handelingen uitvoeren. De directieven duiden aan wat er moet gebeuren of waar bij compilatie op gelet dient te worden. In C++ en C# zijn de directieven aangeduid door het ’#’ teken aan het begin van een lijn. Ze eindigen niet op een ”;”. Een preprocessor kan bijvoorbeeld gebruikt worden om marcro’s te implementeren, om externe bestanden in te voegen op verschillende plaatsen in het bestand of om blokken code aan te duiden die al dan niet naar de compiler dienen te worden gestuurd. Indien de preprocessor macro’s ondersteunt zullen alvorens de hele code naar de compiler wordt gestuurd oproepen naar die macro’s worden vervangen door de hele implementatie van de betreffende macro. Dit kan handig zijn wanneer snelheid van groter belang is dan de grootte van het binair bestand. Preprocessing kan ook gebruikt worden om portabiliteitsproblemen op te lossen. Afhankelijk van het platform waarop men werkt zal die of die code mee gecompileerd worden. [138]
pagina 67
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
C++ [26] In C++ kan men gebruik maken van preprocessor directieven. Hieronder een kort overzicht: #include Dit wordt gebruikt om bestanden te importeren. bv. #include<stdio.h> voor systeemheaderbestanden of #include ’’mijnbestand.h’’ voor gebruikersgedefinieerde bestanden. #define en #undef Deze directieven worden gebruikt voor het defini¨eren van constanten, macro’s, ... . #ifdef, #ifndef, #else, #elif en #endif Deze kunnen gebruikt worden voor conditionele compilatie. 1
#define
MIJNBESTAND
2 3 4 5 6 7
#i f d e f MIJNBESTAND #include ” m i j n b e s t a n d . h” #e l s e #include ” n i e t m i j n b e s t a n d . h” #endif De eerste lijn definieert een macro MIJNBESTAND . De code die er op volgt test of de macro gedefinieerd is. Indien gedefinieerd zal men ”mijnbestand.h” importeren. In het andere geval, ”nietmijnbestand.h”.
C# [67] Ook in C# kan men gebruik maken van preprocessor directieven. Er zijn er niet zoveel als in C++ en worden ook minder frequent gebruikt. C# heeft ook geen aparte preprocessor zoals in C++. Deze directieven worden door de compiler behandeld. Enkele voorbeelden kan u hieronder vinden: #define en #undef Hiermee kan men een symbool defini¨eren of ook weer verwijderen. Het symbool heeft geen waarde. Deze directieven vertellen aan de compiler of dit symbool bestaat of niet, bv. DEBUG symbool. #if, #elif, #else en #endif Deze directieven vertellen aan de compiler om een stuk code al dan niet mee te compileren. Met de #if wordt gekeken of een bepaald symbool bv. gedefinieerd is. #warning en #error Hiermee kan je zelf warnings of errors genereren. #region en #endregion Hiermee kan je bepaalde blokken code afbakenen. Sommige IDE’s herkennen deze stukken en laten de gebruiker dan ook toe om de schikking van broncode op het scherm beter te doen. #pragma Dit directief laat ons toe om bepaalde warnings te tonen of te onderdrukken.
pagina 68
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
Java [61] Java heeft geen preprocessor directieven zoals voorgaande talen dit hebben. Het #include directief uit C++ wordt in Java vervangen door het import statement. Java biedt een aantal alternatieven voor de prepocessormogelijkheden van C en C++. Om bijvoorbeeld conditionele compilatie toe te passen zal men in Java aan de hand van een DEBUG constante werken : 1 2 3
c l a s s Debug { public s t a t i c f i n a l boolean DEBUG = f a l s e ; }
4 5 6 7 8 9 10 11
public c l a s s PPDemo2 { public s t a t i c void main ( S t r i n g [ ] a r g s ) { i f ( Debug .DEBUG) { System . out . p r i n t l n ( ”DEBUG i s t r u e ” ) ; } } } Meer info over hoe Java alternatieven biedt voor de preprocessor mogelijkheden in C en C++ is te vinden in [61]. D [33] Macro’s in C en C++ bezitten toch enkele nadelen. Zo hebben ze bijvoorbeeld geen scope en de debugger kent ze niet. Daarom heeft D geen preprocessor, maar het biedt wel enkele alternatieven om dezelfde noden aan te pakken. Net als in Java wordt import gebruikt ipv #include. In D gebeuren import’s op een symbolische wijze1 waardoor elke file slechts ´e´en maal ge¨ımporteerd wordt. Hierdoor vermijdt men de gebruikte constructies zoals in C++ (zie het ”mijnbestand.h”-voorbeeld hierboven). D biedt alternatieven voor een preprocesoor aan de hand van constanten, enums, aliases, ... Conditionele compilatie wordt op verschillende manieren ondersteund. Zo kan men functionaliteit voor verschillende versies in aparte blokken schrijven met het version statement. Met het debug statement kan code verzameld worden die enkel zal uitgevoerd worden indien met de -debug parameter gecompileerd wordt. Meer info over hoe D alternatieven biedt voor de preprocessor mogelijkheden in C en C++ is te vinden in [33]. 3.1.7.4
Enums
[92] Een enumeratie is een gebruikersgedefinieerd integer type. De mogelijke waarden worden dus door de gebruiker opgegeven. De leden van de enumeratie hebben een naam en de enumeratie zelf kan ook een naam hebben. 1
Dit betekent dat de compiler een reeds gecompileerde symbool tabel inlaadt.
pagina 69
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
C++ [92] Default zijn de enumeratorwaarden oplopend. Het bereik van een enumeratie bevat alle waarden van de enumerators, dit zijn de constanten in de enumeratie gedefinieerd, naar boven afgerond tot de dichtstbijzijnde macht van twee, verminderd met ´e´en. Als er geen negatieve enumerators zijn wordt 0 als ondergrens van de mogelijke waarden genomen. Indien wel negatieve enumerators, dan zal naar onderen naar de dichtstbijzijnde negatieve macht van twee afgerond worden. C# [67] De C# enumeraties zijn krachtiger dan hun C++ tegenhangers. Je kan de waarden ook gebruiksvriendelijke namen geven. De enums in C# worden als structs ge¨ınstantieerd. Ze zijn afgeleid van System.Enum. Er is hier geen performantieverlies doordat eens de broncode gecompileerd is, de enums primitieve types zullen zijn. Java [41, 42] Enums in Java zijn iets gecompliceerder dan in de andere talen. Ze gaan uit van hetzelfde principe, maar laten ook toe gedrag toe te voegen aan de enum. Enums zijn hier klassen en de compiler voegt zelf enkele speciale methoden toe wanneer een enum gecre¨eert wordt. Zo is er bijvoorbeeld een methode die een array teruggeeft met alle waarden in de enum in de volgorde van declaratie. Deze methode wordt dan vaak gebruikt in combinatie met een for-each om te itereren over de waarden van de enum. Men dient nog op te merken dat aangezien alle enums java.lang.Enum uitbreiden en java geen meervoudige overerving ondersteunt, de enums van geen andere klassen meer kunnen overerven. Java vereist dat de constanten eerst gedefinieerd worden, nog voor andere velden of methoden. Indien er ook andere velden of methoden zijn, dient de enum ook op een ”;” te eindigen. Hoe zinvol deze uitgebreide enums zijn is nog maar de vraag. D
Enums in D gedragen zich hetzelfde als in C++ [40].
3.1.7.5
Operatoren en casts
C++ C++ laat twee mogelijke stijlen van casts toe. De oude stijl waarbij het nieuwe type tussen haakjes staat en de nieuwe stijl waarbij de variabele die gecast dient te worden tussen haakjes staat. Casts tussen primitieve datatypes zijn toegelaten en kunnen waarschuwingen genereren indien er kans is op verlies van gegevens. De gegenereerde waarschuwingen zijn ook afhankelijk van je instellingen in verband met uitzonderingen. Bij de ”=” operator zal C++ voor eenvoudige datatypes de data kopieren. Voor gebruikersgedefinieerde klassen laat C++ het aan de programmeur over om aan te geven wat met ”=” bedoeld wordt. Default zal C++ dus een zogenaamde shallow copy uitvoeren. Ook gebruikers gedefinieerde casts zijn mogelijk. C# In C# wordt er meer dan in C++ gelet op typeveiligheid. C# is dan ook minder flexibel voor het converteren van datatypes. Het formalizeert ook het principe van expliciete en impli-
pagina 70
3.1 Basisfunctionaliteit
Hoofdstuk 3. Theoretische vergelijking
ciete casts. Sommige casts worden aangeduid als zijnde impliciet. Hierbij kan je dan zowel de impliciete als expliciete syntax gebruiken. Voor expliciete conversies dien je steeds de expliciete syntax te gebruiken. Indien je dit niet doet zal de compiler een error genereren en dus niet enkel een waarschuwing zoals in C++. Bij boxing en unboxing wenst men een value type om te zetten naar zijn bijhorende referentietype of omgekeerd. Syntactisch komt dit gewoon neer op het uitvoeren van een cast. De inhoud van de variabele zal worden gekopieerd naar de heap en een referentie zal worden gecre¨eerd. In C# kan men de ”=” operator niet overladen worden zoals in C++. De betekenis die er aan gegeven wordt, is dus steeds impliciet. Voor eenvoudige datatypes worden de waarden gewoon gekopieerd zoals in C++. Voor structs heeft een shallow copy plaats. Voor klassen wordt de referentie gekopieerd en dus niet het object. Dit is verschillend van de aanpak in C++. 1 2 3
int j = 1 0 ; o b j e c t boxedJ = j ; // b o x i n g int k = ( int ) boxedJ ; // u n b o x i n g Ook gebruikers gedefinieerde casts zijn mogelijk. Hier zijn ze echter wel wat restrictiever dan in C++. Zo kan men geen casts defini¨eren tussen klassen die van elkaar overerven, aangezien dit impliciet reeds voorzien is.
1 2 3 4 5
M i j n B a s i s K l a s s e M i j n B a s i s = new M i j n B a s i s K l a s s e ( ) ; // d i t z a l n u l l ge ve n MijnAfgeleideKlasse MijnAfgeleide1 as ( MijnAfgeleideKlasse ) MijnBasis ; // d i t z a l een f o u t m e l d i n g g e ve n MijnAfgeleideKlasse MijnAfgeleide2 = ( MijnAfgeleideKlasse ) MijnBasis ;
Java Java expliciete casts zijn syntactisch zoals in C# met de haakjes (). Ook impliciete casts zijn mogelijk in Java. De (un)boxing functionaliteit naar Object zoals in C# heb je hier niet. Het is wel mogelijk om een primitief type aan een ander referentie type toe te kennen, namelijk aan zijn wrapper klasse. Ook de omgekeerde operatie is correct. 1 2 3
int x = 1 0 ; Integer obj = x ; int y = o b j ; De ”=” werkt in Java op dezelfde manier als bij C#. D
1
Ook in D zijn expliciete en impliciete casts mogelijk. De syntax is hier wel anders.
cast (B) a ; D ondersteunt ook operator overlading voor een aantal operatoren waaronder de cast operator en de ”=” operator. Deze laatste operator werkt in D op dezelfde manier als bij Java en C#.
pagina 71
3.2 Object-geori¨enteerd programmeren
3.2
Hoofdstuk 3. Theoretische vergelijking
Object-geori¨ enteerd programmeren
Objectori¨entatie bestaat uit een aantal fundamentele concepten. Een aantaldaarvan zal hier dan ook besproken worden, waaronder klassen en interfaces (en de vergelijking met structs), data abstractie, overerving, polymorfie, ... [35, 34]
3.2.1
Klassen en Interfaces
Klasse Een klasse definieert de karakteristieken (met velden) van een conceptueel ”ding” en zijn mogelijke gedrag (met methoden). Ook geneste en binnenklassen kunnen voorkomen. Zowel Java als D ondersteunen dit. In C# en C++ komen enkel de geneste klassen voor. Binnenklassen kunnen eveneens manueel nagebootst worden [8]. Interface Een interface definieert een blauwdruk die een bepaalde functionaliteit impliceert door de definitie van een aantal methode signaturen. Elke klasse die een interface implementeert moet deze funcionaliteit invullen en dus de methoden implementeren. Abstracte klasse Een abstracte klasse kan conceptueel gezien worden als een middenweg tussen een interface en een klasse. Ze kan methodesignaturen bevatten - die ge¨ımplementeerd moeten worden in afgeleide klassen - en tevens reeds ge¨ımplementeerde methoden. C#, Java en D beschikken over klassen, abstracte klassen en interfaces. C++ beschikt enkel over klassen en abstracte klassen maar het concept van een interface kan gemakkelijk gesimuleerd worden door een abstracte klasse met enkele methode definities in. 3.2.1.1
Zichtbaarheid
Aan klassen en in het bijzonder onderdelen ervan (velden en methoden) kan een niveau van zichtbaarheid toegekend worden. Hiertoe wordt gebruik gemaakt van een groot aantal verschillende sleutelwoorden. Enkele van de belangrijkste sleutelwoorden worden hieronder kort uitgelegd. private Members die als private zijn gedeclareerd zijn enkel zichtbaar in de klasse waar ze gedefinieerd zijn. Private members van een klasse zijn niet zichtbaar in een afgeleide klasse. protected Members die als protected zijn gedeclareerd zijn niet zichtbaar buiten de klasse. Afgeleide klassen kunnen wel aan de members van de basisklasse. internal Members die als internal zijn gedeclareerd zijn niet zichtbaar buiten de assembly. protected internal Members die als protected internal zijn gedeclareerd zijn zichtbaar binnen afgeleide klassen in de assembly. public Members zijn overal zichtbaar.
pagina 72
3.2 Object-geori¨enteerd programmeren
Hoofdstuk 3. Theoretische vergelijking
friend Een klasse kan niet-member functies en klassen als friend declareren waardoor ze aan haar private attributen kunnen [43]. Er is hierbij geen overhead van methoden die aangesproken dienen te worden. 3.2.1.2
Parti¨ ele klassen
In C# kan men gebruik maken van parti¨ele klassen. Parti¨ele klassen laten ons toe om een klasse, struct of interface over meerdere bestanden te spreiden. Dit is vooral handig wanneer men met verschillende ontwikkelaars gelijktijdig aan een zelfde klasse werkt. Verder wordt dit ook gebruikt om bijvoorbeeld de automatisch gegenereerde code te scheiden van de handmatig ingevoerde. Het enige wat men hier voor hoeft te doen is gebruik maken van het ”partial” sleutelwoord aan het begin van de klasse. Als een klasse echter als public, private, protected, sealed, . . . wordt aangeduid in ´e´en van de bestanden, dan dient dit in elk bestand zo te zijn. [67] 3.2.1.3
Function overloading
Met ”function overloading” wordt het overladen van functienamen bedoeld, of met andere woorden het toelaten van functies met dezelfde naam. De hier besproken talen ondersteunen dit allemaal. De voorwaarde voor function overloading is dat de functies een verschillende signatuur hebben. Met de signatuur wordt de functienaam, het aantal parameters en het type van die parameters bedoeld. Soms wordt ook het return-type tot de signatuur gerekend maar niet in de context van function overloading. Twee functies die dezelfde functienaam en parameter(type)s hebben maar een verschillend returntype zijn dus niet toegelaten. Voor alle duidelijkheid, in de talen waar met verschillende soorten parameters gewerkt kan worden - door bij de parameters sleutelwoorden als in, out, inout, . . . te plaatsen - heeft dit geen effect op de signatuur. Het overladen van namen slaat naast functies ook op constructoren en blijkt in de praktijk vooral daar nuttig. [44, 14]
3.2.2
Overerving en polymorfie
Overerving is een fundamenteel concept in de objectori¨entatie. Het laat ons toe abstractie van functionaliteit toe te passen. Afgeleide klassen dienen deze functionaliteit dan zelf in te vullen, te overschrijven of uit te breiden. Overerving wordt gebruikt om code te hergebruiken tussen aanverwante klassen. Zo zal de afgeleide klasse, afhankelijk van de public, protected, private, ... sleutelwoorden van de members in de basisklasse, de eigenschappen van de basisklasse overerven. De afgeleide klasse kan dan nog eigenschappen toevoegen. De basisklasse wordt hierdoor niet be¨ınvloed [37]. Het modelleert een ”is een”-relatie (zie ook covariantie 3.2.2.6). Hiermee wordt bedoelt dat de afgeleide klasse een variatie is van de originele klasse. Polymorfie is tevens een belangrijk objectgeori¨enteerd concept. Een polymorfe referentievariabele kan naar verschillende types objecten verwijzen. Wanneer bewerkingen worden uitgevoerd
pagina 73
3.2 Object-geori¨enteerd programmeren
Hoofdstuk 3. Theoretische vergelijking
op deze objecten, zal dan automatisch de juiste bewerking voor dat type object gekozen worden. Men noemt dit ook late of dynamische binding daar de effectieve binding slechts at runtime plaatsvindt in tegenstelling tot de statische binding die reeds at compile time kan gebeuren. Er zijn verschillende soorten overerving zoals we hieronder zien. 3.2.2.1
Implementatie versus interface overerving
Implementatie overerving De functionaliteit van de basisklasse wordt door de subklasse overge¨erfd en kan eventueel worden overschreven. Interface overerving Er wordt een soort contract opgesteld waarbij de interface aangeeft welke de functionaliteit is die de klasse moet kunnen aanbieden. Een interface is een soort abstracte klasse en bestaat enkel uit abstracte methoden en constanten. C#, Java en D hebben zowel implementatie als interface overerving. C++ is sterk gebaseerd op implementatie overerving waarbij enkel klassen overerven. Er is niet zoiets als een interface in C++. Er zijn wel abstracte klassen. Indien alle methoden in deze klasse puur virtueel zijn, is deze abstracte klasse vergelijkbaar met een interface. [67] 3.2.2.2
Meervoudige versus enkelvoudige overerving
Verder ondersteunen bepaalde talen meervoudige overerving. Dit wil zeggen dat een klasse of interface van verschillende andere klassen en/of interfaces kan overerven. Andere talen houden zich dan weer aan enkelvoudige overerving. Een klasse of interface kan slechts van ´e´en andere klasse of interface overerven. Implementatie van meerdere interfaces is dan weer wel toegestaan. C#, Java en D laten enkelvoudige implementatie overerving en meervoudige interface overerving toe. C++ laat meervoudige overerving toe waardoor zeer gesofisticeerde doch compacte code kan geschreven worden, zie bijvoorbeeld de C++ ATL library. Aan de andere zijde is dit dan weer moeilijk te begrijpen en te debuggen [67]. Het kan bijvoorbeeld gebeuren dat basisklassen methoden met dezelfde signatuur implementeren maar met een totaal andere betekenis en implementatie. Welke methoden dienen dan gebruikt te worden? Dit zijn zaken die met behulp van de scope operator opgelost dienen te worden. Evident is het geenszins. We vermelden hier ook nog even de aanwezigheid van template mixins in D. Daarmee is het mogelijk om stukke extra functionaliteit in een klasse te mengen zonder daarvoor over te erven van een klasse. Op die manier is de nood aan meervoudige implementatie overerving nog minder voorkomend dan het reeds was. Een voorbeeld : 1 2 3
template Foo ( ) { void f u n c ( ) { p r i n t f ( ”Foo . f u n c ( ) ” ) ; } }
4 5 6
c l a s s Bar { mixin Foo ;
pagina 74
3.2 Object-geori¨enteerd programmeren
7
Hoofdstuk 3. Theoretische vergelijking
}
8 9 10 11 12
void t e s t ( ) { Bar b = new Bar ( ) ; b . func ( ) ; }
3.2.2.3
// r o e p t Foo . f u n c ( ) op
Public, protected of private overerving
C++ maakt, in tegenstelling tot C#, Java en D, een onderscheid tussen public, protected of private overerving. Men kan in C++ bv. schrijven: 1
c l a s s A f g e l e i d e K l a s s e : public B a s i s K l a s s e { /∗ . . . ∗/ } ; Dit wil zeggen dat alle attributen en methodes die public zijn in de basisklasse ook public zullen zijn indien ze via objecten van de afgeleide klasse worden aangesproken. Dit geldt ook voor het protected sleutelwoord. Wanneer men nu bij de basisklasse private ipv public voegt, zullen alle attributen die public of protected zijn in de basisklasse private zijn als ze via objecten van de afgeleide klasse worden aangesproken. Private overerving is default in C++. Tussen voorgaande scenario’s bestaat ook nog de protected overerving. Hierbij zullen alle attributen en methoden die public zijn in de basisklasse protected zijn als ze worden aangesproken door objecten van de afgeleide klasse. Private en protected members van de basisklasse behouden hun status. [36] 3.2.2.4
Syntax
In deze sectie zullen we even kijken hoe elke taal overerving mogelijk maakt. We bekijken tevens welke sleutelwoorden voorhanden zijn en wat de betekenis hiervan is. C++ [36] Om over te erven van een klasse of interface maakt men gebruik van het ”:” teken. Constructoren, destructoren, copy constructors en ”=”-operator worden niet overge¨erfd. Abstracte functies kunnen gedeclareerd worden via de ”= 0”-notatie. Ze worden ook wel pure virtuele functies genoemd. Ze zijn placeholders en dwingen de afgeleide klasse een implementatie te voorzien. Interfaces zijn zoals gezegd abstracte klassen met enkel puur virtuele functies. Een abstracte klasse bevat op z’n minst ´e´en puur virtuele functie. Polymorfisme wordt in C++ ondersteund door gebruik van het virtual sleutelwoord. Functies zijn namelijk default niet virtueel. Wanneer je een methode als virtueel declareert, wordt aan de compiler duidelijk gemaakt dat hij deze methode niet statisch mag binden at compile time, aangezien een afgeleide klasse de methode mag overschrijven. De juiste methode die opgeroepen dient te worden, moet at runtime worden opgezocht (dynamisch binden of late binding genoemd). De implementatie hiervan maakt gebruik van een virtuele functietabel en pointers. Als een methode echter niet virtual is gedeclareerd zal de compiler de methode statisch binden (ook wel early binding genoemd). Virtuele functies hebben een belangrijk nadeel en dit is de
pagina 75
3.2 Object-geori¨enteerd programmeren
Hoofdstuk 3. Theoretische vergelijking
overhead. Er is namelijk meer ruimte nodig voor de virtuele functie tabel en er is een tragere uitvoering door de late binding [35]. Indien ze niet nodig zijn, gebruikt men ze dus best niet. C# [67] Om over te erven van een klasse of interface maakt men net als C++ gebruik van ”:”. Een methode van de basisklasse kan worden opgeroepen via het base-sleutelwoord. Klassen kunnen als sealed gedeclareerd worden. Hierdoor kan men niet meer van deze klassen afleiden. Dit is hetzelfde als het gebruik van final in java. Dit kan echter omzeild worden door gebruik van de extensions in C# 3.0 . Klassen en functies kunnen als abstract worden gedefinieerd. Een abstracte klasse kan niet worden ge¨ınstantieerd en een abstracte functie moet worden overschreven in een niet-abstracte afgeleide klasse. Een abstracte functie is dus automatisch virtual. Polymorfisme wordt hier ondersteund door gebruik van het virtual en override sleutelwoord. Functies zijn default niet virtueel en moeten bijgevolg als dusdanig aangeduid worden als men ze wil kunnen overschrijven. Dit toont de intentie van de programmeur en is effici¨enter zoals uitgelegd voor C++. In de afgeleide klasse moet de methode expliciet het sleutelwoord override gebruiken om de methode te overschrijven. Dit vermijdt runtime bugs die in C++ kunnen voorkomen door het ongewild verschillen van de signatuur van de functie. De C# compiler zal hierop reageren door te melden dat er geen basisfunctie voor de overridemethode bestaat. Java [37] Om een klasse uit te breiden maakt men gebruik van het extends sleutelwoord. Om een interface te implementeren gebruikt men implements. Java maakt gebruik van het sleutelwoord final voor methoden en klassen die respectievelijk niet overschreven of uitgebreid mogen worden. Door gebruik van het super sleutelwoord kan men constructoren en methoden van een bovenliggende klasse aanroepen. Men kan de implementatie van een methode gedeeltelijk overschrijven door in de implementatie de methode van de bovenliggende klasse eerst aan te roepen. Het abstract sleutelwoord laat ons toe abstracte methoden en klassen te declareren. Abstracte methoden hebben geen implementatie Methoden in Java zijn steeds virtueel (late binding). Er wordt dus default steeds de juiste methode opgeroepen. D Om over te erven van een klasse of interface maakt men net als C++ en C# gebruik van ”:”. Wat overerving en polymorfie betreft werkt D zoals Java. Elke methode is per definitie virtueel ... override wel ook gebruikt ... voordeel bij wijziging bovenliggende methode zien aan compilerfout dat nie meer impl
pagina 76
3.2 Object-geori¨enteerd programmeren
3.2.2.5
Hoofdstuk 3. Theoretische vergelijking
Hiding
Hiding of het verbergen van methoden doet zich voor wanneer men in een afgeleide klasse een methode met dezelfde signatuur als een niet-virtuele methode in de bovenliggende basisklasse schrijft. Dit is echter niet volgens de principes van objectgeori¨enteerd programmeren. Er is dan namelijk een potentieel gevaar dat de verkeerde methode opgeroepen zal worden. C++ In C++ is hiding mogelijk. Normaal is het verbergen van niet-virtuele methoden in C++ het gevolg van onoplettendheid en niet echt de bedoeling. Managed compilers zullen hier dan ook voor waarschuwen. C# [53, 67] C# laat hiding van methoden toe via het sleutelwoord new. Dit geeft meer mogelijkheden aan de ontwikkelaars van klassebibliotheken. Stel dat men na verloop van tijd een methode aan de basisklasse wenst toe te voegen die per ongeluk dezelfde signatuur heeft als deze in de onderliggende klasse. Nu zal het programma dat er van gebruikt maakt hoogst waarschijnlijk niet meer weten welke functie het dient aan te roepen. De compiler komt hier ter hulp en herinnert je er aan om het new sleutelwoord te gebruiken indien je hiding wenst. De bestaande clientcode zal nog steeds correct werken en de juiste methode vinden, nl. die van de afgeleide klasse. Problemen zullen echter ontstaan in nieuwe clientcode die zal geschreven worden. Hier moet men goed opletten dat men de methode niet via de basisklasse zal opgeroepen. Java Daar in Java elke methode per definitie virtueel is bestaat hiding niet op een manier zoals hierboven beschreven. Methode hiding kan enkel gebeuren door de methode in de basisklasse en de afgeleide klasse static te declareren. D In D werd niet veel concrete info gevonden over method hiding maar zoals verwacht (aangezien ook in D alle methoden per definitie virtueel zijn) kan het net als in Java toegepast worden door methoden static te declareren. 3.2.2.6
Covariantie en Contravariantie
Covariantie en contravariantie in objectgeori¨enteerde context beschrijven hoe types zich ten opzichte van elkaar verhouden. In de wiskunde kan covariantie gezien worden als de mate waarmee twee variabelen dezelfde lijn volgen in hun waarden, maw de ordening van de waarden. Als de ene waarde omhoog gaat, zou de andere moeten volgen. Deze term werd in de objectori¨entatie overgenomen en omgezet naar de situatie hoe een afgeleid type zich verhoudt tot zijn basistype (subtype relatie of ”is een” relatie). Het afgeleide type zal gevonden worden op een plaats waar het basistype verwacht wordt. Contravariantie is het omgekeerde scenario. Daar zal het basistype gevonden worden waar het afgeleide type wordt verwacht. Indien geen van voorgaande gelden, gebruikt men het begrip invariant. Er zijn een aantal gevallen waarin we van deze begrippen gebruik maken, bv. array covariantie, returntype variantie, ... [134, 118]
pagina 77
3.2 Object-geori¨enteerd programmeren
Hoofdstuk 3. Theoretische vergelijking
C++ Volgens [92] moet het type van een overschrijvende functie hetzelfde zijn als dat van de oorspronkelijke virtuele functie, behalve het returntype. Als het oorspronkelijke returntype B* is, dan mag het returntype van de overriding functie D* zijn, mits B een public base class is van D. Ook het returntype B& mag in de overriding functie D& worden. Deze regels gelden echter niet voor de typen van de argumenten. Dit zou leiden tot een schending van het typesysteem. C# [23, 22] Arrays met referentietypes zijn covariant: string[] is een subtype van object[]. Er zijn wel enkel opmerkingen. 1 2
// A i s een a r r a y van System . S t r i n g van 1 g r o o t . string [ ] a = new string [ 1 ] ;
3 4 5
// B i s een a r r a y van System . O b j e c t object [ ] b = a ;
6 7 8 9 10 11
// We p r o b e r e n een i n t e g e r aan b t o e t e kennen . D i t zou m o g e l i j k z i j n // moest b e c h t een a r r a y van o b j e c t e n z i j n i p v een a r r a y van s t r i n g s . // we k r i j g e n een ArrayTypeMismatchException met de v o l g e n d e b o o d s c h a p : // ” Attempted t o s t o r e an e l e m e n t o f t h e i n c o r r e c t t y p e i n t o t h e a r r a y ” . b [ 0 ] = 1; Uit b lezen geeft geen problemen. Het is enkel wanneer we in b wensen te schrijven dat we het echte type dienen te kennen. Arrays van waardetypes zijn invariant: int[] is geen subtype van double[]. Er is ondersteuning voor returntype covariantie en parameter contravariantie voor delegates. Er is geen covariantie of contravariantie voor methode overriding. Java De situatie in Java voor covariante returntypes is gelijklopend met die van C#. Arrays van objects zijn covariant in overeenstemming met de referentietypes van C#. Arrays van primitieve types zijn invariant in overeenstemming met de waardetypes van C#. In Java bestaat ook exceptiecovariantie. Voor parametertypes is er invariantie bij methode overriding. D Referenties: [46] Ook hier zijn covariante return types ondersteunt. Dit gebeurt op dezelfde wijze als in C++. 3.2.2.7
Operator overloading
Het gebruik van een precieze notatie voor veelgebruikte operaties is nauwelijks te overschatten [92]. Vaak wenst men niet enkel gegevens of methoden van een object oproepen. Vaak dien je ook objecten op te tellen, te vermenigvuldigen, te vergelijken, ... . Voor built-in types zijn
pagina 78
3.2 Object-geori¨enteerd programmeren
Hoofdstuk 3. Theoretische vergelijking
reeds operatoren gegeven. Door het overladen van de operatoren kan je zelf met betrekking tot zelfgedefinieerde types defini¨eren wat bv. een optelling of vermenigvuldiging moet doen. Het operator sleutelwoord duidt aan dat het om een operator overlading gaat. [67, 92] C++ [35, 92] In C++ kan men gebruik maken van operatoren. Voor gebruikersgedefinieerde operatoren zijn er enkele voorwaarden. Zo moeten de operatoren ”=”, ”[]”, ”()” en ”->” niet-statische memberfuncties zijn. Het eerste operand is dan zeker een lvalue. Een lvalue is een expressie die naar een object verwijst. Het betekent letterlijk ”iets dat aan de linkerkant van een toekenning staat”. Een object is een aaneengesloten stuk geheugen. Een operator kan of binnen een klasse of binnen een naamruimte gedefinieerd zijn. C# [67, 75] Operator overloading in C# is meer restrictief dan het C++ overladingsmechanisme. De overladingsmethoden zijn altijd statisch en niet virtueel (statische binding). C# laat in tegenstelling tot C++ dan ook niet toe dat men bv. de ”=”-operator zal overladen. Om dan onder andere ”+=” te kunnen gebruiken, is het enkel nodig om de ”+”-operator te overladen. De compiler zal dan hiervan gebruik maken om de ”+=”-operatie succesvol te voltooien. Bij het overladen van vergelijkende operatoren vereist C# dat men dit doet in paren. Indien men ”==” wil overladen, zal ook ”!=” overladen moeten worden. Indien dit niet het geval is, zal de compiler een error genereren. Java
Java ondersteunt geen operator overlading voor gebruikersgedefinieerde typen.
D In D is operator overlading mogelijk door voorgedefinieerde klasse methoden te implementeren. Zo zal bijvoorbeeld class A { int opNeg(); } er voor zorgen dat de - operator op instanties van de klasse A kan gebruikt worden. [14]
3.2.3
Structs
Java ondersteunt geen structs maar C++, C# en D wel. Een struct wordt in tegenstelling tot een klasse (behalve in C++ waar dit ook op de stack kan) gealloceerd op de stack en kan daardoor de effici¨entie en performantie verhogen. Het is wel enkel aan te raden voor kleinere objecten. Structs zouden gebruikt moeten worden als een verzameling kleine data (datastructuur). Bij de structs zijn memberfunctions ook toegelaten. Ook statische memberfuncties behoren tot deze groep. C++ [16, 92] Een struct (aangeduid met het sleutelwoord struct) is een eenvoudige vorm van een klasse en een verzameling van willekeurige typen. Er zijn natuurlijk wel wat verschillen tov een klasse. Struct members zijn default public. Die van een klasse zijn default private. Structs kunnen wel
pagina 79
3.2 Object-geori¨enteerd programmeren
Hoofdstuk 3. Theoretische vergelijking
net als klassen overerven. De grootte van een struct dient niet overeen te komen met de som van de groottes van zijn members. Dit is een gevolg van alignatie die sommige machines toepassen. Er komen geheugengaten in de structure te zitten. Verspilling van geheugenruimte kan u voorkomen door de grootste members eerst op te nemen. C# [67] Het grote verschil in C# is dat structs hier geen implementatie overerving toestaan. De enige uitzondering hierop is dat structs net als elk ander type in C# via System.ValueType uiteindelijk van System.Object overerven. Hierdoor heeft een struct ook toegang tot de methoden voorzien door System.Object en kan men ze ook overschrijven. Interface overerving is wel degelijk toegestaan. De compiler zal default in een constructor voorzien die geen parameters heeft. Deze mag je echter zelf niet vervangen. Een poging om dit te omzeilen via default waarden wordt ook niet toegestaan. Ook hier kan je zelf bepalen in welke volgorde de velden in het geheugen zullen worden geplaatst. Een reden waarom men de nadruk legt op het gebruik voor kleine structuren geldt wanneer men bv. de structuur zou gebruiken en meegeven als parameter of aan een andere struct assigneren. In deze gevallen zal de gehele structuur doorgegeven worden wat net kan leiden tot performantieverlies. Je kan dit dan wel omzeilen door gebruik te maken van het ref sleutelwoord. Je dient er dan evenwel ook op te letten dat de opgeroepen methode de struct mogelijk kan wijzigen. D [3, 97, 79] Structs worden in D gezien als ”lichtgewicht klassen”. Noch interface noch implementatie overerving is mogelijk. Ze laten dus geen virtuele memberfunctions toe. Ook hidden members zijn niet toegestaan. Niet-virtuele memberfunctions zijn wel toegestaan. Ze hebben tevens geen constructoren of destructoren. De nadruk ligt hier wel degelijk op de opslag van kleine hoeveelheden data. We spreken ook van Plain Old Data (POD)2 . Door gebruik van het align attribuut kan men net als in C++ en C# de alignatie van de opeenvolgende velden bepalen. Ook unittests kunnen hier gebruikt worden.
3.2.4
Data-abstractie
Data-abstractie is het doorvoeren van een duidelijk scheiding tussen de abstracte eigenschappen van een datatype en de eigenlijke implementatie ervan. De abstracte eigenschappen zijn deze eigenschappen die zichtbaar zijn voor de clientcode die gebruik maakt van het datatype terwijl de implementatie van deze eigenschappen privaat blijven. De clientcode kan dus niks te weten komen over deze implementatie. Deze implementatie kan dan ook veranderen zonder dat de clientcode moet veranderd worden. Dit is de kern van data abstractie. [110] 2
Plain Old Data refereert naar een struct dat geen verborgen members bevat, geen virtuele functies heeft, geen overerving heeft en ge¨ınitialiseerd en gekopieerd kan worden door eenvoudige bitkopie¨en. D structs zijn POD [108, 14].
pagina 80
3.2 Object-geori¨enteerd programmeren
3.2.4.1
Hoofdstuk 3. Theoretische vergelijking
Properties
Wat is een property en wanneer gebruiken we dit? Wat is het verschil met een methode en wanneer gebruiken we het n of het ander? Dit zijn enkele veelgestelde vragen die bij vele programmeurs spelen. Properties zijn eerder een algemeen concept in programmeertalen. Ze zorgen mede voor de encapsulatie van data (data hiding). Dit staat los van enig syntaxvorm die deze properties kunnen aannemen. Zo zijn de C# properties evengoed in Java en C++ met methoden te implementeren. Beide stellen hetzelfde concept voor. Een property moet een naam, type en waarde hebben. Eventuele access rights kunnen gespecifieerd zijn. De ware toedracht van properties zou evenwel volgens DB liggen in reflectie en introspectie. Reflectie bestaat er in dat men at runtime een object kan inspecteren en info opvragen met betrekking tot de velden, methoden, ... Introspectie slaat dan weer op een low-level implementatie van reflectie (bv. in Java spreken we van introspectie). Aan properties kunnen soms observer objecten worden gehecht. Deze objecten zullen dan at runtime verwittigd worden van veranderingen van de property. [67, 107, 1, 87, 51, 65, 7] Een property in C# Properties in C# zijn overgenomen uit de Visual Basic syntax. De idee is om een methode of paar methoden te gaan inkleden zodat ze er voor de client of gebruiker van de klasse uit zien als een publiek veld. Voor de programmeur blijven het ingepakte methoden. Je kan er heel eenvoudig waarden aan assigneren en waarden opvragen. Verder zorgen we hierdoor ook voor de encapsulatie van data zoals het objectgeorienteerde paradigma voorschrijft. Binnen de property hebben we een get en set accessor. Je kan ook n van beide weglaten en zo readonly of writeonly Properties creeeren. Dit laatste wordt echter afgeraden. Een methode is hier dan beter geschikt. Het zou voor clientcode tot verwarring kunnen leiden. In clientcode dient er vooral van de property gelezen te worden. De compiler zal bij de set accessor aannemen dat er n parameter opgegeven is. Naar deze parameter wordt met value gerefereerd. 1 2 3 4 5 6 7
// p r i v a t e member f i e l d private string m i j n S t r i n g // de b i j h o r e n d e p r o p e r t y public string M i j n S t r i n g { g e t { return m i j n S t r i n g ; } set { mijnString = value ; } } De accessor methoden kunnen ook verschillende access levels hebben, zolang maar n van de twee eenzelfde access level als de property zelf heeft. Indien niet zal de compiler een fout genereren. Ook statische properties zijn mogelijk. Deze zijn dan geldig voor de hele klasse en niet voor een enkele instantie van die klasse. Properties zijn ook overerfbaar en kunnen overschreven worden in afgeleide klassen.
pagina 81
3.2 Object-geori¨enteerd programmeren
Hoofdstuk 3. Theoretische vergelijking
Properties in C# bundelen het concept van getters en setters samen. Ze maken de clientcode makkelijker leesbaar en geef de ontwikkelaar n concept en dus ook n plaats waar code eventueel verandert dient te worden. Een nadeel aan het gebruik van deze propertysyntax is dat men niet kan zeggen of men met een publiek veld of met een property aan het werk is. Indien je met een methode werkt, zoals met getters en setters in Java weet je wel met welk type je aan het werk bent. Het zou echter voor de gebruiker van de klasse bij een goed ontwerp niet mogen uitmaken hoe de klasse geimplementeerd is. Aan de andere zijde is er wel een informatieverlies. De broncode is niet meer zo zelfdocumenterend als waar een duidelijk onderscheid is. Het gebruik van properties heeft trouwens weinig of geen invloed op de performantie. Men zou kunnen denken dat het aanroepen van een property een bepaalde overhead met zich meebrengt. Dit is niet zo. Een property die enkel een andere methode aanroept of een privaat veld teruggeeft, wordt tijdens het compilatieproces omgezet naar inline code. In C# 3.0 kent de automatische property zijn opgang. Deze properties vereisen dat zowel get als set aanwezig zijn. De programmeur hoeft in de code dan zelf geen private velden meer aan te maken. Dit alles gebeurt automatisch achter de schermen. Het contract met eventuele clientcode blijft hierbij geldig en de programmeur heeft weer wat lijnen code minder te schrijven. Indien we bijvoorbeeld een eenvoudige klasse Persoon wensen te schrijven : 1 2 3 4 5
public c l a s s Persoon { public string Voornaam { g e t ; s e t ; } public string Achternaam { g e t ; s e t ; } public int L e e f t i j d { g e t ; s e t ; } } We kunnen ons hierbij wel afvragen in hoeverre dit nog opgaat indien we een abstracte klasse wensen te maken. Hoe dient voorgaande stuk dan te worden geinterpreteerd? Voorstellen werden gemaakt om het via attributen of een property.
Wanneer gebruiken we een property of methode? De regels hieromtrent zijn niet strikt. Je kan dus zelf kiezen welke methode je kan gebruiken. Er zijn wel enkele richtlijnen (uit Professional C# 2005) die je eventueel in acht kan nemen. Client code zou de waarde van een property moeten kunnen lezen. Write-only properties worden dan ook afgeraden. Het lezen van de waarde van een property zou niet veel tijd in beslag mogen nemen. Ook neveneffecten zouden niet mogen optreden. Wijzigingen aan de property mogen enkel betrekking hebben op waar de property op slaat. Properties moeten ingesteld kunnen wor-
pagina 82
3.3 Generiek programmeren
Hoofdstuk 3. Theoretische vergelijking
den in een willekeurige volgorde. Verschillende op elkaar volgende opvragingen van de property dienen hetzelfde antwoord te geven. Indien de property geregeld en onvoorspelbaar van waarde veranderd is het aangewezen een methode te gebruiken. Properties in D Referenties: [81] Properties in D zijn gelijkaardig aan deze in C# 2.0. 1 2 3 4 5 6
c l a s s Foo{ public : // l e e s p r o p e r t y int data ( ) { return m data ; } // s c h r i j f p r o p e r t y int data ( int v a l u e ) { return m data = v a l u e ; }
7
private : int m data ;
8 9 10
}
3.3
Generiek programmeren
Generiek programmeren is een vorm van programmeren waarbij algoritmes worden geschreven in een bepaalde syntaxis waardoor de algoritmes adaptief zijn en kunnen worden genstantieerd door de compiler. Generiek programmeren ligt dicht tegen meta-programmeren aan, een techniek waarbij aan de hand van bepaalde broncode weer nieuwe broncode wordt geprogrammeerd, die daarna wordt gecompileerd. Bij generiek programmeren gaat het echter om een syntactische en semantische uitbreiding in de programmeertaal en wordt er niet direct nieuwe broncode gegenereerd. Generiek programmeren is ook te vergelijken met programmeren met macro’s - zoals in C++ -, maar deze laatste slaat op tekstueel zoeken en vervangen in de code en is geen onderdeel van de grammatica van de taal maar ge¨ımplementeerd door een pre-processor. [124, 123] Een van de meest eenvoudige voorbeelden van een generiek algoritme is een geparameteriseerde functie. Hierbij is nog niet bekend wat het type van de parameter(s) zal zijn maar wordt wel al het algoritme beschreven. Dit kan vaak toegepast worden aangezien in de praktijk eenzelfde functionaliteit voor verschillende types parameters vaak voorkomt. Een functie die bijvoorbeeld het maximum van 2 getallen moet berekenen doet dit op dezelfde manier voor elk type getallen. C++, C#, Java en D ondersteunen elk generiek programmeren maar er zijn wel verschillen wat betreft de syntax, de uitvoering en de mogelijkheden. We geven een kort overzicht. Het systeem van Java en C# leunt dicht tegen elkaar en ook C++ en D hebben een gelijkaardig systeem. Bij de eerste twee wordt meestal over generics gesproken bij de laatste 2 over templates. C++ en D gebruiken dan ook het sleutelwoord template, C# en Java niet. Templates worden in C++ en D reeds at compile time ge¨ınstantieerd. Ze laten op die manier
pagina 83
3.4 Robuustheid
Hoofdstuk 3. Theoretische vergelijking
compile time function evaluation (CTFE) toe. Zo is het in deze talen bijvoorbeeld mogelijk om reeds at compile time een fibonacci getal recursief te berekenen. Het grootste verchil tussen het generics systeem in Java en C# is dat in Java gebruik gemaakt wordt van type erasure at compile time. De generische code wordt vervangen door de nodige code met casts gelijkaardig aan de code die je zou geschreven hebben als je niet over generics zou beschikken. In C# echter zit het generics systeem ingebakken in de CLR. Templates worden dus ge¨ıstantieerd at runtime. Generieke programmeerconcepten kunnen zeer krachtig zijn en zijn dus ook een belangrijk punt waarop programmeertalen vergeleken kunnen worden. Een doorgedreven vergelijking zou ons hier te ver leiden maar is zeker op zijn plaats. Zo beweren de makers van D dat ze het template systeem van C++ verbeterd hebben. Om dit na te gaan zouden doorgedreven praktische experimenten gedaan moeten worden. Voor meer info verwijzen we naar de referenties. [123, 124, 83, 105, 15, 14]
3.4
Robuustheid
Het woord robuust, gebruikt met betrekking tot computer software, verwijst naar een besturingssysteem of ander programma dat niet alleen in normale omstandigheden goed presteert maar ook in ongewone omstandigheden die de ontwerper zijn veronderstellingen op de proef stellen.[85] Met de steeds stijgende omvang van programma’s en steeds stijgende kost van bugs, zal alles wat kan gedaan worden om de robuustheid van het eindproduct te verbeteren winstgevend zijn.[12] Robuustheid is iets dat van in het begin van het ontwerp in software betrokken moet worden. Het is niet iets dat je op een later tijdstip even kunt toevoegen.[85] Bij het ontwikkelen van programmeertalen moet hier dan ook rekening mee gehouden worden. We geven nu een algemeen overzicht van een aantal concepten die bijdragen tot de robuustheid van software. Daarna volgt een beknopt overzicht van de concrete mogelijkheden in de programmeertalen. Unit Testing [142] Een procedure om de correctheid van individuele eenheden broncode te testen. Een eenheid is de kleinst mogelijke component die men kan testen, en dit kan verschillen voor verschillende (soorten) programmeertalen. Voor proceduraal programmeren kan een eenheid bijvoorbeeld een individueel programma, een functie of een procedure zijn. In object-ge¨ orienteerd programmeren daarentegen is de kleinste eenheid een klasse. Contract Programming 3 [117, 14] Een contract is een ”expressie” die waar moet zijn. Indien dit niet zo is, dan is het contract verbroken en zit er een bug in het programma, tenminste als de contracten goed opgesteld zijn. De drie meest voorkomende contracten zijn precondities, postcondities en invarianten. Precondities zijn voorwaarden die voldaan moeten 3
De metafoor van het sluiten van contracten komt uit de zakenwereld, waar de klant en de leverancier met een contract een overeenkomst sluiten[117]
pagina 84
3.4 Robuustheid
Hoofdstuk 3. Theoretische vergelijking
zijn voor een blok statements uitgevoerd mag worden. Ze worden typisch gebruikt voor functieparameters die aan bepaalde voorwaarden moeten voldoen. Postcondities zijn dan weer voorwaarden die voldaan moeten zijn na de uitvoering van een blok statements. Deze worden typisch gebruikt om de return waarde van een functie en haar eventuele neveneffecten te valideren. Invarianten zijn voorwaarden die altijd voldaan moeten zijn. Meestal gebruikt binnen een klasse. Code Coverage Analyse Een procedure die nagaat in welke mate programmacode getest wordt. Volgens [21] bestaat de procedure uit het vinden van stukken code die niet getest worden, het cre¨eren van bijkomende testen - om de mate waarin de code ”bedekt” wordt te verhogen - en een kwantitatieve maat bepalen voor code coverage die indirect ook een kwalitatieve maat is. Optioneel kunnen ook nog redundante test cases getraceerd worden. Resource Acquisition Is Initialisation [139] RAII is een techniek die het verwerven en het vrijgeven van bronnen combineert respectievelijk met de constructie (initialisatie) en destructie van variabelen. Exception Handling Een procedure om afwijkingen van het verwachtte programma verloop op te vangen.
3.4.1 3.4.1.1
C++ Unit Testing
Ook in C++ kan men unit testing toepassen. Dit is niet in de taal ingebakken, maar dient via aparte applicaties en bibliotheken te gebeuren. Enkele gekende frameworks die hiervoor gebruikt kunnen worden zijn hieronder opgesomd: CppUnit CppUnit is een C++ unit testing framework. Het ontstond als een port van JUnit naar C++ gemaakt door Michael Feathers [25]. CppTest CppTest is een portabel, eenvoudig maar krachtiga unit testing framework voor C++ [24]. TUT Template Unit Test is een klein en portabel unit test framework voor C++ [102]. 3.4.1.2
Contract programming
Contracts in C++ kunnen verwezenlijkt worden zonder wijzigingen in de taal. Er wordt hier gebruik gemaakt van assert statements en macros om bijvoorbeeld pre- en postcondities te simuleren. Het resultaat is echter niet heel proper [14].
pagina 85
3.4 Robuustheid
3.4.1.3
Hoofdstuk 3. Theoretische vergelijking
Code coverage
Voor C++ zijn er niet veel tools die ons kunnen helpen met code coverage. Bullseye is hier wel ´e´en van 4 . Ook gcov is een code coverage tool voor C++ 5 . 3.4.1.4
RAII
In C++ wordt RAII vooral aan de hand van destructoren ge¨ımplementeerd. Volgens Stroustrup in [82] is het niet nodig om in C++ een finally blok te voorzien zoals in andere talen om resources op te kuisen. Het achterliggende idee bestaat er in een resource voor te stellen door een lokaal object. De destructor van dit object zal instaan voor het opkuisen van dit object. Er is dus geen nood meer aan een finally blok. De programmeur zal hierdoor ook niet kunnen vergeten de resource vrij te geven 6 . 3.4.1.5
Exception handling
In C++ is exception handling aanwezig. Men kan gebruik maken van try-catch blokken. Er is hier geen finally blok zoals in de vorige sectie werd uitgelegd.
3.4.2 3.4.2.1
C# Unit Testing
In C# kan men aan unit testing doen door gebruik te maken van aparte frameworks en applicaties. Vaak is er wel een goede integratie met de Visual Studio IDE mogelijk. Voorbeelden van frameworks kan u hieronder vinden: .NET Reflector Reflector is een class browser, explorer, analysetool en documentatie viewer voor .NET. Het laat ons toe om op eenvoudige wijze assemblies in C#, Visual Basic en IL te bekijken, er in te navigeren en zoeken, te decompileren en analyzeren [71, 72]. NUnit 2.2.9 NUnit brengt unit-testing naar de .NET talen. Initieel werd NUnit geporteerd van JUnit, maar de huidige versie werd herschreven om voordeliger van .NET gebruik te kunnen maken [74]. Testdriven 2.7.2111 TestDriven.NET maakt het makkelijk om unit tests uit te voeren vanuit Visual Studio solutions. Het ondersteunt alle versies van Microsoft Visual Studio .NET en integreert met .NET tools als NCover, NCoverExplorer, Reflector, TypeMock, dotTrace, NUnit, MbUnit, ZaneBug, MSBee en Team System [100]. In de volgende versie van Visual Studio zullen unit tests aanwezig zijn en dien je niet meer gebruik te maken van aparte tools. 4
Meer informatie kan u vinden op http://www.bullseye.com/coverage.html. Meer info kan u vinden op http://gcc.gnu.org/onlinedocs/gcc-3.0/gcc_8.html 6 Meer informatie over het gebruik van raii in C++ kan u vinden op http://en.wikibooks.org/wiki/C++_ Programming/RAII 5
pagina 86
3.4 Robuustheid
3.4.2.2
Hoofdstuk 3. Theoretische vergelijking
Contract Programming
C# heeft geen ondersteuning voor contract programming. Er zijn wel extensies die dit wel ondersteunen. Een voorbeeld hiervan is Extensible C# (XC#). Er wordt hierbij gebruik gemaakt van attributen (zie sectie refAttriAnno)7 . 3.4.2.3
Code Coverage
Tools waarmee men in C# aan code coverage kan gaan doen zijn bv.: NCover NCover, een code coverage tool voor .NET. NCover genereert statistieken over de broncode. Deze statistieken bevatten onder andere het aantal keren dat een codelijn werd uitgevoerd [68]. NCoverExplorer 1.3.6.15 NCoverExplorer is een kleine tool gemaakt door Grant Drake. Het toont de output van NCover coverage in een Windowsapplicatie. Het is daarbij een stuk sneller dan de html output [69]. In de nieuwe Visual Studio zijn hier net als met unit testing enkele voorzieningen voor. 3.4.2.4
RAII
C# struct of klasse members krijgen een default-waarde indien er geen initialisatie plaatsvindt. Gedeclareerde variabelen die niet gebruikt worden, zullen door de compiler aangeduid worden door een warning. Aan de hand van het using statement kan men er voor zorgen dat automatisch de Dispose methode van een objectinstantie waarop het statement betrekking heeft, wordt opgeroepen. Dit gebeurt wanneer de instantie uit de scope gaat. Het is gelijkaardig aan de C++ destructor. De klassen dienen hiervoor wel de interface IDispose te implementeren [67]. Het using statement laat ons toe om het verwerven van de bron samen te smelten met het opkuisen ervan. Dit gebeurt door de speciale syntax. Men maakt gebruik van een blok. Dit wil zeggen dat je heel fine-grained te werk kan gaan en bronnen niet langer dan nodig zal kunnen gebruiken, daar waar dit nodig is. 3.4.2.5
Exception handling
C# ondersteunt excepties en heeft ook een gecontroleerde executie. Zo zal er gecontroleerd worden op onder andere array-out-of-bounds excepties. Excepties zijn hier op eenzelfde wijze als in C++ gebruikt. We hebben hier wel een finally block dat steeds uitgevoerd kan worden en ook als een soort RAII kan optreden. Alle excepties zijn hier afgeleid van System.Exception. 7
Meer informatie hieromtrent is te vinden op http://www.codeproject.com/cs/design/DbCwithXCSharp.asp en http://www.resolvecorp.com/products.aspx
pagina 87
3.4 Robuustheid
3.4.3
Hoofdstuk 3. Theoretische vergelijking
Java
De meeste concepten zijn niet ingebouwd in Java. Er bestaan echter wel meestal externe, dikwijls open source, bibliotheken en tools. 3.4.3.1
Unit Testing
Dit kan met externe bibliotheken zoals JUnit die ook gebruikt wordt in Netbeans. JUnit is een open source test framework geschreven door Erich Gamma and Kent Beck.[62] 3.4.3.2
Contract Programming
Java heeft enkel het assert contract in de taal ingebouwd. Pre- en Postcondities en invarianten kunnen echter gesimuleerd worden met het assert contract en if -constructies. Meer uitleg is te vinden in de documentatie van Sun8 . 3.4.3.3
Code Coverage
Op Java-Source.net9 zijn heel wat tools te vinden. Specifiek voor Netbeans bestaat er ondertussen ook een plugin10 die code coverage deels automatiseert. 3.4.3.4
RAII
In Java is er geen sprake van RAII, daar alles door de garbage collector opgeruimd wordt. 3.4.3.5
Exception handling
Java beschikt over een hi¨erarchie van exceptions klassen met als basisklasse java.lang.Exception. Bij een fout wordt de specifieke Exception opgeworpen. Fouten opvangen gebeurt met het trycatch-finally blok waarbij het finally blok optioneel is.
3.4.4
D
In volgende paragrafen bespreken we de verschillende concepten met betrekking tot robuustheid die ingebouwd zijn in de taal. D is eigenlijk de enige van de hier besproken talen die zo intensief ondersteuning biedt. En naast het betrouwbaarder maken van software zorgen bijvoorbeeld de contracten die in de code staan dat de compiler nog optimaler kan werken daar hij via de contracten meer info over de data kan vergaren. Daarnaast kunnen concepten zoals contracten en unittests ook voor de gebruiker veel duidelijk maken over hoe bepaalde code werkt en wat ze moet doen. [12, 14] 8
http://java.sun.com/j2se/1.4.2/docs/guide/lang/assert.html http://java-source.net/open-source/code-coverage 10 http://codecoverage.netbeans.org/ 9
pagina 88
3.4 Robuustheid
3.4.4.1
Hoofdstuk 3. Theoretische vergelijking
Unit Testing
Unit tests zijn een reeks test cases die op een klasse worden toegepast om de correcte werking ervan na te gaan. In een ideaal scenario zouden de tests uitgevoerd moeten worden telkens het programma wordt gecompileerd. De beste manier om ervoor te zorgen dat de tests zeker worden uitgevoerd en onderhouden parallel met de ontwikkeling van de klasse is door de testcode in de broncode van de klasse zelf te schrijven [142]. In D kan een blok testcode als member functie aan een klasse worden toegevoegd of alleenstaand in een module. Om de tests uit te voeren dient bij het compileren de extra parameter -unittest toegevoegd te worden. De tests worden dan mee gecompileerd in het uitvoerbaar bestand. Wanneer het programma wordt uitgevoerd zullen eerst de unittests uitgevoerd worden. Indien een test faalt zal het programma niet starten. Jammer genoeg stopt het programma al bij de eerste test die faalt. Het zou veel beter zijn mochten alle test cases doorlopen worden en een foutboodschap voor alle elke falende tests gegenereerd worden. Voorbeeld : 1 2
c l a s s Sum{ int add ( int x , int y ) { return x + y ; }
3
unittest { Sum sum = new Sum ; assert ( sum . add ( 3 , 4 ) == 7 ) ; assert ( sum . add ( −2 ,0) == −2); }
4 5 6 7 8 9
}
3.4.4.2
Contract programming
[117] D heeft in de taal voorzieningen ingebouwd voor contract programming. Het maakt het toepassen ervan eenvoudig. De basis van alle contracten is het assert-contract. Dit controleert of een expressie waar is. 1
assert ( e x p r e s s i o n ) ; Precondities en postcondities vormen een contract dat beschrijft welke voorwaarden vervuld moeten zijn respectievelijk voor een blok statements uitgevoerd mag worden en na het uitvoeren van dat blok statements. Voor precondities moet een in-clausule gedefinieerd worden, voor postcondities een out-clausule. De twee clausules kunnen alleen en samen voorkomen. De volgorde waarin ze voorkomen is onbelangrijk maar ze moeten wel voor de body-clausule komen die de eigenlijke functionaliteit bevat. Zoals in onderstaand voorbeeld te zien is kan aan een outclausule een resultaat teruggegeven worden wanneer ze bij een functie hoort. De variabele(die eender welke naam kan hebben) krijgt de return waarde en het return type van de body van de functie. Voorbeeld :
pagina 89
3.4 Robuustheid
1 2 3 4 5 6 7 8 9 10 11 12
Hoofdstuk 3. Theoretische vergelijking
char [ ] d a t e T o S t r i n g ( int d , int m, int y ) in { assert ( d > 0 && d <= 31 && m > 0 && m <= 12 && y >= 0 ) ; } out ( r e s u l t ) { assert ( r e s u l t . l e n g t h <= 1 0 ) ; } body{ return ” ” ˜ t o S t r i n g ( d ) ˜ ” / ” ˜ t o S t r i n g (m) ˜ ” / ” ˜ t o S t r i n g ( y ) ; }
3.4.4.3
Code coverage
De D code coverage analyser is ingebouwd in de D compiler. Het voordeel daarvan is dat hij altijd gesynchroniseerd is met de implementatie van de taal. Het is ge¨ımplementeerd door een teller toe te voegen aan elke lijn in elke module die gecompileerd wordt met de extra parameter -cov. Er wordt code achter de schermen code ingevoegd bij elk statement die de bijhorende teller moet verhogen. Als het programma eindigt, wordt alle info verzameld en voor elke module een .lst bestand als resultaat gegeven. 3.4.4.4
RAII
D gebruikt wel degelijk garbage collection en het is dus niet noodzakelijk om RAII toe te passen. Het kan echter wel zorgen voor veiligere en performantere code. D heeft hiervoor het scope attribuut. Het wordt gebruikt bij lokale variabelen en bij de declaratie van een klasse. Als een lokale variabele met scope gedeclareerd wordt zal de destructor van het object automatisch opgeroepen worden wanneer de omliggende scope van de referentie ernaar eindigt. Ook wanneer de scope vroegtijdig wordt beeindigd door een exceptie zal de destructor opgeroepen worden. Het scope attribuut wordt dus gebruikt om bronnen op te kuisen. 1 2
scope Foo f o o = new Foo ; scope f o o = new Foo ; Bovenstaande statements doen hetzelfde met Foo een reeds gedefinieerde klasse. Op het einde van de scope waarin deze declaraties staan (dit kan een een functie, een itererende lus, een tak van een conditionele lus, . . . zijn) wordt het object vernietigd en worden de geheugenbronnen die erdoor bezet werden vrijgegeven. De garbage collector zou dit zelf ook doen, maar je hebt geen garantie dat het direct gebeurt. Een garbage collector zal eerder bronnen opkuisen wanneer er bronnen nodig zijn.
pagina 90
3.5 Memory management en garbage collection
3.4.4.5
Hoofdstuk 3. Theoretische vergelijking
Exception handling
D werkt net als java met een hi¨ archie van exceptions klassen met als basisklasse object.Exception. Bij een fout wordt de specifieke Exception opgeworpen. Fouten opvangen gebeurt met het bekende try-catch-finally blok waarbij het catch of finally blok mag weggelaten worden (niet beiden).
3.5 3.5.1
Memory management en garbage collection Pointers
Pointers kan men zowel in C++, C# als D gebruiken. Voor het gebruik van pointers in C# zijn er wel enkele beperkingen. Indien in C# met pointers wordt gewerkt dient men dit expliciet aan te duiden met het unsafe sleutelwoord. Men kan zowel een klasse, struct, als gewone member velden als unsafe aanduiden. Lokale variabelen kan men niet als unsafe aanduiden. Het markeren van een onveilig blok gaat dan weer wel. De compiler weet dan dat er onveilige code staat. Men zal ook aan de compiler duidelijk moeten maken dat hij deze onveilige code mag compileren. Pointers in C# mogen ook niet wijzen naar referentietypen of structs die referentietypen bevatten. Dit is een poging om data die gebruikt wordt door de garbage collector en de .NET runtime te gaan beschermen. Natuurlijk kan men zodra men van pointer gebruik maakt net als in C++ via pointerarithmetiek omwegen vinden. Pointers kunnen hier ook niet verwijzen naar variabelen in referentiedatatypes tenzij deze als fixed gedeclareerd zijn. De reden waarom dit niet mag is omdat referentietypes op de heap van plaats kunnen wijzigen. Het fixed statement zal aan de garbage collector duidelijk maken dat hij de betreffende klasse niet mag verplaatsen tijdens de duur van een fixed block. Hierdoor wordt de integriteit van de pointerwaarden behouden [67]. 1 2 3
f i x e d ( int ∗pX = M i j n K l a s s e . X, ∗pX2 = M i j n K l a s s e .X) { // g e b r u i k p o i n t e r s }
3.5.2
C++
In C++ kunnen types zowel op de stack als op de heap geplaatst zijn. Als een variabele gealloceerd werd met het new sleutelwoord, dan zal de variabele zich op de heap bevinden. Wanneer we de variabele dan wensen te verwijderen, kunnen we gebruik maken van het delete sleutelwoord. Er is geen garbage collection voorzien voor zelf-gedefinieerde types. Het zou handiger zijn indien er een automatische creatie, vernietiging en wijzigbaar kopieergedrag aanwezig zou zijn (ipv shallow copy)[35]. In de nieuwe standaardversie van C++ zal een garbage collector echter niet uitgesloten zijn.
pagina 91
3.5 Memory management en garbage collection
3.5.3
Hoofdstuk 3. Theoretische vergelijking
C#
In C# ben je niet zo vrij om variabelen te plaatsen waar je wil. Je kan een int dus niet zomaar om de heap alloceren. Waardetypes zoals int’s worden steeds op de stack gealloceerd. Referentietypen normaal op de heap. In C++ zou je wel een pointer naar een int op de heap kunnen zetten. Eenvoudig voorgedefinieerde types (behalve object en string), structs en enumeraties zijn steeds waardetypes. Je kan wel new gebruiken bij het aanmaken van een int, maar dit heeft hetzelfde effect als de int op 0 initialiseren. Object, string en alle andere klassen zijn steeds referentietypes. In C# betekent new enkel dat je de constructor aanroept. Verder heb je in C# ook geen delete zoals in C++. De .NET garbage collector zal op regelmatige tijdstippen het geheugen scannen en objecten waar geen referenties meer naar bestaan dan ook verwijderen. Het tijdstip waarop dit gebeurt is niet deterministisch. Men kan wel destructoren maken. Deze hebben een lichtjes andere betekenis als deze van C++. Voor de klassen die dit nodig hebben bestaat er de destructie uit twee delen. De klasse dient de IDisposable interface en daardoor de Dispose methode te implementeren. Verder dient er ook een gelijkaardige destructor als in C++ voorzien te worden. Dit is een soort reserve mechanisme indien dispose niet opgeroepen zou worden [67]. Aan de hand van het using sleutelwoord kan er ook voor gezorgd worden dat men Dispose niet zelf dient op te roepen. 1 2 3 4
// e n k e l r e f e r e n t i e = n i e t g e i n i t i a l i s e e r d e p o i n t e r i n C++ M i j n K l a s s e Mijn ; // i n s t a n t i e van MijnKlasse , s t a a t op de heap Mijn = new M i j n K l a s s e ( ) ;
5 6 7 8 9 10
// i n s t a n t i e van M i j n S t r u c t , maar n i e t g e i n i t i a l i s e e r d e v e l d e n , // s t a a t op s t a c k MijnStruct MijnStr ; // v e l d e n z i j n g e n i t i a l i s e e r d , s t a a t r e e d s op de s t a c k M i j n S t r = new M i j n S t r u c t ( ) ;
3.5.4
Java
Objecten worden in Java steeds by reference doorgegeven. Eigenlijk wordt hier de referentie naar het object by value doorgegeven. Java maakt gebruik van automatische garbage collection.
3.5.5
D
D lijkt hier een mengeling van C++ en C# te zijn. Het geheugen in D is meestal beheerd door de garbage collector. Specifieke objecten kunnen echter ook zoals in C# gefinaliseerd worden wanneer ze uit de scope gaan. Expliciet geheugenbeheer zoals in C++ is mogelijk door het gebruik van de operators new en delete. Je kan ook direct gebruik maken van malloc en free uit C. Voor individuele objecten of zelfs hele programma’s kan garbage collection worden uitgeschakeld. Hierdoor verkrijgt men meer controle over het geheugenbeheer [120].
pagina 92
3.6 Andere
3.6 3.6.1
Hoofdstuk 3. Theoretische vergelijking
Andere Documentatie genereren
Een documentatie systeem voor programmacode is dezer dagen onontbeerlijk. Het is belangrijk voor de programmeur zelf om een duidelijk overzicht over de code te bewaren en voor anderen om de code te begrijpen of te weten hoe de applicatie gebruikt dient te worden. De hier besproken talen hebben elk hun eigen documentatie-systeem, het ene al geavanceerder dan het andere. Voor het genereren van documentatie in C++ kan men gebruik maken van de doxygen tool. Deze ondersteunt zowel C++, C, Java, Objective-C, Python, IDL en in zekere mate PHP, C#, and D. Java heeft deJavadoc tool die HTML code genereert op basis van de declaraties en de commentaar in de code bestanden. De commentaar gebruikt voor documentatie komt tussen /** en */ in plaats van de gewone /* en */ of //. De standaard output die je bijvoorbeeld ook in de Java SE API Documentation 11 ziet kan je naar eigen voorkeuren aanpassen door zelf doclets te schrijven en de standaard doclet ermee te vervangen. Doclets zijn Java programma’s die gebruik maken van de doclet API. De commentaar schrijven in het juiste formaat gaat dankzij een goede IDE als Netbeans deels automatisch en dus ook vlot in Java. D heeft het Ddoc formaat. Er zijn een aantal regels en tags voorzien die het formaat vastleggen. De documentatie genereren is heel eenvoudig. Bij het compileren de extra parameter -D gebruiken volstaat om HTML code te genereren. Ddoc genereert echter niet zelf HTML code maar een formaat dat als input dient voor een aantal vastgelegde macro’s die de eigenlijke documentatie genereren. Als je het uitzicht van de documentatie wil veranderen of andere output dan HTML code wil verkrijgen dan kan je deze macro’s aanpassen. Zonder geavanceerde IDE ben je in D verplicht alle de nodige commentaar zelf in het juiste formaat te schrijven. Dit zorgt dat documentatie schrijven in D behoorlijk wat werk met zich mee brengt.
3.6.2 3.6.2.1
Attributen en Annotaties C++
ANSI C++ heeft geen attributen. Attributen zijn door de Windows C++ compiler wel ondersteund als Windowsspecifieke uitbreiding. 3.6.2.2
C#
Attributen laten toe om in de taal ingebouwde declaratieve constructs uit te breiden. Er bestaan voorgedefinieerde attributen afgeleid van System.Attribute. Je kan ook zelf attributen gaan de11
http://java.sun.com/javase/6/docs/api/
pagina 93
3.6 Andere
Hoofdstuk 3. Theoretische vergelijking
fini¨eren. Attributen kunnen toegepast worden op klassen, methoden, velden en parameters. De attributen en hun waarden kunen dan at runtime opgevraagd worden. Ze worden gebruikt om de acties die onze code moet nemen te gaan bepalen. Attributen worden onder andere ook voor reflectiedoeleinden gebruikt[67]. Voorbeelden van attributen zijn DllImport dat zegt dat een methode in een externe dll is gespecifieerd, StructLayout dat toelaat om een struct zich als een C++ union te laten gedragen, Obsolete dat een error of warning genereert indien de methode gebruikt wordt en Conditional dat een conditionele compilatie forceert. Attributen worden net voor het object waarop ze betrekking hebben geplaatst en dit tussen vierkante haakjes. Een voorbeeld van een attribuut dat vaak aanwezig is in C# programma’s, is het attribuut STAThread. Indien er gebruik wordt gemaakt van COM, zal dit attribuut aangeven dat een enkele thread gebruikt dient te worden, nl. Single Thread Application Thread. We kunnen ook gebruik maken van het MTAThread attribuut wat staat voor Multi Thread Application Thread [89, 90]. 3.6.2.3
Java
In Java worden annotaties gebruikt om metadata aan de broncode toe te voegen die ook at runtime beschikbaar is voor de gebruiker. Ze kunnen toegevoegd worden aan programma elementen zoals klassen, methoden, velden en parameters. Java annotaties zijn at runtime toegankelijk voor de programmeur via reflectie. Annotaties bestaan uit het @-teken gevolgd door een lijst van elementen met elk een waarde. Er bestaan ook annotaties zonder elementen en die worden markers genoemd. Zo is bijvoorbeeld @override een marker die aan de compiler laat weten dat een methode een andere methode in een bovenliggende klasse moet overschrijven. Bij het compileren van de Java broncode, bewaart de Java compiler de annotatie metadata in de .class files. Later kan de JVM of een ander programma die metadata bekijken om te bepalen hoe het moet interageren met de programma elementen of om hun gedrag te veranderen. [127, 98] 3.6.2.4
D
D heeft geen annotaties in de taal ingebouwd.
3.6.3
Reflectie
Reflectie is het proces waarbij een computerprogramma van een bepaald type at runtime gewijzigd kan worden op een manier die afhangt van de code en zijn runtime gedrag. Bij hogere niveau talen zal bij compilatie van de broncode de informatie van de structuur van het programma verloren gaan door het produceren van lagere niveau code. Als het systeem reflectie ondersteunt, zal de structuur bewaard blijven onder de vorm van metadata. Een praktisch voorbeeld waar reflectie wordt gebruikt is in de moderne IDE’s. Deze bevatten vaak een klassebrowser die ons toelaat om alle members van de klasse te bekijken. Dit gebeurt aan de hand van reflectie.
pagina 94
3.6 Andere
3.6.3.1
Hoofdstuk 3. Theoretische vergelijking
C++
C++ biedt geen ondersteuning voor reflectie. Er zijn wel enkele pogingen gebaseerd op templates, RTTI informatie, debug informatie door de compiler gegenereerd ... [84]. 3.6.3.2
C#
C# laat dan weer wel reflectie toe. Hierbij wordt ook veel gebruik gemaakt van de attributen (zie vorige sectie). Deze bevatten de metadata. Fundamentele klassen en namespace die gebruikt worden zijn de System.Type klasse en System.Reflection namespace [67]. Een voorbeeld: 1 2 3 4 5 6 7
Type t = typeof ( double ) ; // v r a a g de members op van de Double k l a s s e MemberInfo [ ] members = t . GetMembers ( ) ; foreach ( MemberInfo NextMember in Members ) { // s c h r i j f de namen van a l l e members op h e t scherm C o n s o l e . WriteLn ( NextMember . Name ) ; } 3.6.3.3
Java
Java ondersteunt runtime reflection met het packet java.lang.reflect uit de standaard bibliotheek. Reflectie in Java laat toe at runtime een Java programma te bekijken, ook introspectie genoemd, en interne eigenschappen van het programma ta manipuleren. Het is bijvoorbeeld mogelijk alle members van een klasse op te vragen en die te tonen [99]. We geven hieronder een voorbeeld. Er wordt een instantie van de klasse Foo aangemaakt met reflectie. 1 2 3
C l a s s c l s = C l a s s . forName ( ”Foo” ) ; Method method = c l s . getMethod ( ” h e l l o ” , null ) ; method . i n v o k e ( c l s . n e w I n s t a n c e ( ) , null ) ; 3.6.3.4
D
D ondersteunt nog geen reflectie maar er zijn wel plannen voor na aanvraag op de nieuwsgroepen.
3.6.4
Threading
Een thread is een onafhankelijk stroom van instructies in een programma. Het is vaak wenselijk om twee zaken naast elkaar te kunnen uitvoeren. Bij een GUI applicatie wenst men bijvoorbeeld te kunnen blijven reageren op input van de gebruiker terwijl men ondertussen bijvoorbeeld een groot bestand inleest of zware berekeningen doet. Zonder threads zou de gebruiker moeten wachten tot deze actie voltooid is, alvorens hij weer input kan verschaffen. Met threads worden deze twee zaken gescheiden en blijft de goede interactie tussen het systeem en de gebruiker behouden.
pagina 95
3.6 Andere
Hoofdstuk 3. Theoretische vergelijking
C++ C++ biedt geen ondersteuning voor threads in de standaard bibliotheek. Er bestaan wel externe bibliotheken zoals Boost die wel gebruik van threading aanbieden. C# C# maakt gebruik van Threading via de System.Threading namespace uit de .NET bibliotheek. Deze biedt de klassen Thread en ThreadPool aan [67]. Java Java ondersteunt het gebruik van threads met klassen uit de standaard bibliotheek. Meer info is te vinden op de website van Sun12 . D Ook D heeft ondersteuning voor threads in de taal zitten. Meer uitleg is te vinden in de language specification13 van D.
12 13
http://java.sun.com/docs/books/tutorial/essential/concurrency/ http://www.digitalmars.com/d/phobos/std_thread.html
pagina 96
Hoofdstuk 4. Besluiten
Hoofdstuk 4
Besluiten Na deze algemene theoretische vergelijking proberen we al enkele besluiten te formuleren.
4.1 4.1.1
Algemene besluiten Terminologie
Tussen de verschillende talen bestaat er vaak een verschil in de gebruikte terminologie waar men toch hetzelfde concept mee bedoeld. Ook in de gebruikersgroepen circuleren vaak andere benamingen voor taalfeatures en -eigenschappen. Dit zorgt vaak voor heel wat verwarring. Situaties en discussies waarbij van de ene kant wordt beweerd dat een taal de feature heeft en van de andere kant weer niet, komen regelmatig voor. Zo kwamen we het voorbeeld van de attributen in C# en de annotaties in Java tegen. In D bleek attributen dan weer de naam voor een totaal ander concept te zijn. Naast het gebruik van verschillende benamingen voor een zelfde concept, is er ook vaak discussie over de betekenis van het concept zelf. Vaak vonden we veel varianten en definities van hetzelfde concept, telkens met kleine verschillen en nuances. Dit dwong ons dan ook om te kiezen voor ´e´en van de mogelijkheden of om zelf een omschrijving van het concept te verzinnen. Voorbeelden hiervan zijn het statisch of dynamisch, sterk of zwak getypeerd zijn van programmeertalen. Ook over het concept van closures bleek veel discussie te zijn. Voorgaande zaken bemoeilijken natuurlijk het vergelijken van de talen. Voorzichtigheid is dan ook geboden.
4.2 4.2.1
De talen Programmeerstijlen
De programmeertalen die hier onderzocht werden ondersteunen allen ´e´en of meerdere programmeerstijlen of paradigma’s. Het belangrijkste paradigma dat hier bestudeerd werd is het objectgeori¨enteerde. Alle onderzochte talen ondersteunden dit. De ene taal wel wat strikter dan de andere. Zo is C# een puur objectgeori¨enteerde taal waar alle types afgeleid zijn van een Object
pagina 97
4.2 De talen
Hoofdstuk 4. Besluiten
klasse, terwijl in C++, Java en D er ook nog primitieve types bestaan die niet van zulk een klasse zijn afgeleid. Ook het generieke paradigma werd even onder de loep genomen. In C++ en D beschikken we over een krachtig templatesysteem. In Java en C# wordt gebruik gemaakt van generische klassen, methoden en collecties. Deze laatste zijn iets minder krachtig. Bij C# worden de juiste types pas at runtime bepaald. Bij de andere talen gebeurt dit reeds at compile time. We kunnen dus stellen dat de talen hier aan elkaar gewaagd zijn, op enkele zaken die hier en in de theorie werden aangehaald na.
4.2.2
Typesysteem en typering
Een onderscheid kan gemaakt worden tussen die talen waarbij het typesysteem in de taal is ingebouwd en deze die een typesysteem in de omgeving hebben. Het typesysteem van C++, Java en D zitten in de taal vervat. C# zelf heeft geen typesysteem. Het maakt gebruik van het Common Type System. Elke taal die door .NET ondersteund wordt, maakt gebruik van dit typesysteem. Dit heeft als voordeel dat de verschillende talen types van elkaar kunnen gebruiken. De verschillende soorten typeringen voor onze talen werden hieronder nog eens in een tabel weergegeven: Ook hier kunnen zien we dat de talen sterk gelijklopend zijn. Ze zijn allen sterk, typering
sterk / zwak
statisch / dynamisch
safe / unsafe
gestructureerd / nominatief
C++ Csharp Java D
sterk sterk sterk sterk
statisch statisch statisch statisch
unsafe safe / unsafe safe unsafe
nominatief nominatief nominatief nominatief
Tabel 4.1: Typering statisch en nominatief getypeerd. We zien dat C++ en D als unsafe bestempeld worden. Dit komt doordat ze onder andere het gebruik van pointers toelaten. In D is het echter niet de bedoeling pointers op regelmatige basis te gebruiken. Je kan perfect applicaties schrijven zonder pointers. Slechts heel af en toe kan dit eens nodig blijken. De mogelijkheid om pointers te gebruiken is deels ook ingebouwd om de overgang voor C++ programmeurs te vereenvoudigen. C# kan zowel safe als unsafe zijn. Zoals vermeld in de theorie kan C# unsafe code opnemen. Dit dient expliciet aangeduid worden met het unsafe sleutelwoord en ook de compiler dient hiervan op de hoogte te worden gebracht. Indien dit niet zo is zal er een fout worden getoond, aangezien men er van uit gaat dat men enkel safe code zal gebruiken. Java is hier dan ook de enige taal die geen unsafe code toelaat of kan toelaten. Java en C# kunnen dus als veiliger dan C++ en D worden beschouwd. Dit heeft natuurlijk ook een keerzijde. Hoe veiliger de taal, hoe meer restrictief ze is. Dit houdt in dat je dan ook beperkt wordt in mogelijkheden. Je boet dus in aan vrijheid. Men dient hier dus een afweging te maken van wat belangrijker is, meer veiligheid of meer vrijheid.
pagina 98
4.2 De talen
4.2.3
Hoofdstuk 4. Besluiten
Geheugenbeheer en garbage collection
Een groot verschilpunt tussen de talen is het geheugenbeheer en gebruik van garbage collection. Zo kunnen C++ en D met pointers overweg. C# kan dit, maar het gebruik is hier restrictiever. Java kent geen pointers. C++ heeft geen garbage collection. Java, C# en D hebben dit wel. Dit zorgt er voor dat de programmeur in C++ zelf verantwoordelijk is voor de opkuis. D neemt hier wel een speciale plaats in, in die zin dat het ook mogelijk is om net als in C# zelf finaliserende methoden te gaan schrijven. Bovendien laat D ook toe om op C en C++ wijze aan garbage collection te gaan doen. D geeft ons dus meer mogelijkheden voor het omgaan met geheugenbeheer dan eender welke van de andere talen. Men kan hier uit afleiden dat men vaak een keuze dient te maken tussen de veiligheid van een gecontroleerde omgeving mits enige restricties en de vrijheid die men krijgt bij de unsafe programmeertalen. Deze laten ons namelijk toe om heel performante en effici¨ente code te schrijven. Het re¨ele gevaar voor bugs dient men er dan wel bij te nemen.
4.2.4
Basisfunctionaliteit
Function pointers en delegates Java heeft geen function pointers of delegates. Daar waar C++ en D werken met gewone function pointers, heeft C# het begrip delegates. Deze hebben dezelfde functionaliteit als function pointers, maar zijn typeveilig. Ook D heeft nog een begrip delegates. Dit heeft een gelijkaardige functionaliteit als C#. Het aspect veiligheid komt hier ook naar voor. C# en D wensen de functionaliteit van een functiepointer uit te breiden met typeveiligheid (tot op zekere hoogte). Delegates en events Het gebruik van delegates en events is een belangrijk gegeven. C++ heeft wel function pointers, maar geen events. C# heeft zowel delegates als events. Java heeft enkel events en D heeft function pointers, delegates en geen events. De events in C++ en D dienen uit bibliotheken gehaald te worden. Een voorbeeld waar events van pas komen zijn GUI’s. Closures kunnen hier misschien ook nuttig zijn daar men de context mee kan nemen in de handler van het event en men zo bijvoorbeeld snel aan de andere componenten kan. Het meenemen van de context zorgt evenwel voor meer overhead dan misschien nodig is. Dynamische arrays D heeft dynamische arrays. De andere talen hebben dit niet.
4.2.5
Objectgeori¨ enteerd programmeren
Properties Het properties concept blijkt in alle talen wel implementeerbaar te zijn, zij het nu met ´e´en of andere speciale syntax zoals in C# en D of met gewone methoden zoals in C++ en Java. De leesbaarheid van de code kan echter leiden onder deze syntax. We houden het gebruik van properties dan ook maar op syntactic sugar. In C# 3.0 blijkt het properties concept echter wat uitgebreid met nieuwe functionaliteit. We
pagina 99
4.2 De talen
Hoofdstuk 4. Besluiten
hebben hier de zogenaamde automatische property. Deze komen de productiviteit alvast ten goede daar ze ons toelaten om met zeer weinig code veel uit te drukken. Hier blijkt het concept toch wel een meerwaarde te hebben.
4.2.6
Generiek programmeren
C++ en D beschikken over een templatesysteem. Dit is een zeer krachtig instrument. Java en C# maken daarentegen gebruik van generics. Ze zijn over het algemeen minder krachtig, maar hebben ook hun voordelen. Zo kan de C++ compiler niet checken op codecorrectheid, terwijl dit in Java en C# wel kan. Ook in D kan dit. Verder is het tijdstip waarop de template ge¨ınstantieerd wordt van belang. Bij C++,Java en D is dit at compile time. Voor C# is dit at runtime. Het voordeel van dynamische instantiatie is dan ook dat je maar ´e´en templateklasse hebt die at compile time zal ge¨ınstantieerd worden met de juiste types. In C++ zal voor elk verschillend gebruik een verschillende klasse worden gemaakt. Java maakt gebruik van type erasure. Elke taal heeft hier zijn voor- en nadelen. Men moet hier opnieuw de afweging tussen flexibiliteit en veiligheid maken.
4.2.7 4.2.7.1
Andere Robuustheid
Bij het onderzoeken van de robuustheid werd opgemerkt dat elke taal over exception handling beschikt. Hier en daar zijn wel kleine verschillen. RAII wordt niet door elke taal ondersteund. Vaak worden luttele pogingen ondernomen en kleine syntactische features voorzien. Ook in verband met code coverage bleek het resultaat mager. Enkel D gaf hier een minimale ondersteuning in de taal zelf. Voor contract programming geldt ongeveer hetzelfde verhaal. Dit wordt niet uitgebreid ondersteund in de talen. Als laatste beschouwden we ook nog unit testing. Ook hier zien we dat men dit voor D in de taal heeft voorzien. De andere talen blijven in gebreke. Men kan wel stellen dat in de talen zelf weinig instrumenten aanwezig zijn om bovenstaande zaken te gebruiken en toe te passen. Er dient vaak een beroep te worden gedaan op externe tools. Het is duidelijk dat D hier het beste naar voor komt met een goede poging om een aantal van deze zaken als ingebouwde features te voorzien. Doordat zulke zaken in de taal vervat zitten, zal men ook sneller de neiging hebben om ze te gebruiken. 4.2.7.2
Attributen en annotaties
Java en C# hebben attributen en annotaties. C++ en D niet. Deze attributen worden vooral gebruikt om reflectiemechanismen te voorzien. 4.2.7.3
Reflectie
Enkel C# en Java hebben reflectie. Dit kan toch als een mooie troef worden aanzien. Het reflectiemechanisme in C# is nog iets uitgebreider dan in Java. Het bevat wat meer intercessie-
pagina 100
4.3 Compilatie, installatie en uitvoering
Hoofdstuk 4. Besluiten
mogelijkheden. In D zijn er reeds voorstellen om reflectie op te nemen in de taal. 4.2.7.4
Threading
Alle talen behalve C++ hebben de mogelijkheid om gebruik te maken van threads via de standaardbibliotheek. C++ dient hier beroep te doen op een externe bibliotheek.
4.3
Compilatie, installatie en uitvoering
Java en C# worden gecompileerd naar byte en intermediaire code. Ze maken gebruik van een virtuele machine die de bytecode en intermediaire code zal uitvoeren. Door het gebruik van deze byte en intermediaire code cre¨eert men platformonafhankelijkheid. Dit is een belangrijk voordeel van deze talen. Voor C# komt hierbij nog eens de taalonafhankelijkheid. Bij de uitvoering wordt dan gebruik gemaakt van JIT compilatie om native code te produceren. C++ en D worden gecompileerd naar native code. Het uitvoerbare bestand is dan niet platformonafhankelijk. De broncode dient op elk platform te worden gecompileerd. Het voordeel van compilatie naar native code was vroeger de snelheid waarmee het uitvoerbaar bestand kon lopen. Dit is nu minder het geval door het gebruik van de JIT compilers. Ook deze zijn relatief snel. .NET heeft hier toch wel enkele mooie troeven in handen, mede door de taalonafhankelijkheid. Het enige echte nadeel is dat er nog weinig implementaties van de CLI zijn. Er is reeds Mono, maar er zijn toch nog compatibiliteitsproblemen bij het overzetten van programma’s van bijvoorbeeld Windows naar Linux. Bij het installeren van applicaties valt ook vooral .NET op met het gebruik van de assemblies. Deze hebben een goede versioning en lossen hierdoor de DLL-Hel op.
4.4
Ondersteuning
In de sectie ondersteuning zijn de verschillen heel duidelijk. In het geval van beschikbare documentatie spannen C++ en C# de kroon. Er is heel uitgebreid documentatie te vinden over deze talen. In het geval van C# kan men wel stellen dat de documentatie (bv. MSDN) ook heel wat gestructureerder is dan die van C++. Het zoeken van de juiste informatie leek dan soms op het zoeken van een speld in een hooiberg. Te uitgebreide informatie heeft dus ook niet altijd zijn voordelen. Java heeft een mooi gestructureerde API en bleek het handigste in gebruik. Voor D was er bijna geen goede documentatie te vinden. Deels doordat er vaak een nieuwe versie van de D compiler uitkwam, was de specificatie gedateerd. Er was dus enkel de website van digital mars als documentatie. We kunnen dus besluiten dat Java hier het best uit komt.
pagina 101
Deel II
Praktische proeven
pagina 102
Hoofdstuk 5. Inleiding
Hoofdstuk 5
Inleiding In de volgende hoofdstukken worden de applicaties toegelicht. Deze applicaties vormen het middel waarmee we zullen nagaan in hoeverre de verschillende talen bepaalde idee¨en en features ondersteunen en voorzien. Het zijn onze werkinstrumenten en kunnen als experimenten beschouwd worden. Er werd dan ook voortdurend aan gesleuteld om zodoende verschillende mogelijkheden uit te testen. Het is dus niet de bedoeling om hier pure staaltjes programmeerkunst te presenteren, maar wel om enkele zaken aan te duiden die uit deze testen naar voor kwamen. Dit kunnen zowel problemen als handige taalconstructies zijn. Per applicatie kwamen namelijk specifieke noden naar voor die door elke taal op een eigen manier of manieren opgelost konden worden. Hieronder volgt een opsomming van mogelijke applicaties en de gebruikte hard- en software bij het ontwikkelen en testen van de uiteindelijke applicaties.
5.1 5.1.1
Applicaties GUI applicatie : Sokoban
GUI applicaties zijn alomtegenwoordig. De gebruiker is vertrouwd met zijn vensters of andere grafische omgeving. Design komt hierbij ook steeds meer naar voor. Men wil mooiere in het oog springende en snel reagerende applicaties. Al deze mooie zaken dienen natuurlijk ook geprogrammeerd te worden. Met de bouw van zo een GUI applicatie wensen we te onderzoeken wat er allemaal komt bij kijken, gaande van ontwerp en opbouw van de visuele zaken tot het reageren op events. We bekijken dan ook hoe dit in de verschillende talen ondersteund of net niet ondersteund wordt en welke tools voorhanden zijn om ons het werk wat lichter te maken.
5.1.2
Batch applicatie : FileDigger
Naast de GUI applicaties zijn er ook een groep applicaties die zich richten op het verwerken van data. Input en output zijn hier sleutelwoorden. De uitvoertijd kan snel oplopen en vaak is er weinig interactie met de gebruiker. Deze soort applicaties dienen vooral robuust en effici¨ent te werken. Indien we onze harde schijf ’s nachts laten scannen, wensen we niet ’s morgens op
pagina 103
5.2 Hardware
Hoofdstuk 5. Inleiding
een foutmelding te stoten om als gevolg opnieuw te mogen beginnen. De talen worden dan ook getest of ze deze criteria met succes kunnen vervullen.
5.1.3
Server applicatie : Chatroom
Naast de aard van de applicatie heeft ook de omgeving waarin de applicatie draait zijn invloed op de goede werking. Vaak moet een applicatie kunnen communiceren tussen verschillende systemen. We onderzoeken dus in hoeverre de talen toelaten om dit op een eenvoudige wijze te ontwikkelen. Er werd gekozen voor een serverapplicatie. Dit laat ons toe om ook het gebruik van threading in de talen te bestuderen. Een server dient te communiceren met vele clients. Het gebruik van threads helpt ons om deze concurrente aanvragen op effici¨ente wijze af te handelen.
5.1.4
Andere applicaties
Naast de voorgaande applicaties zijn er nog tal van andere soorten. Men kan bijvoorbeeld de communicatie met databanken beschouwen of het gebruik van webservices. Ook toepassingen voor mobiele apparatuur zoals gsm, pda, pocket pc, . . . behoren tot de mogelijkheden. Deze werden echter niet beschouwd.
5.2
Hardware
De gebruikte hardware om de applicaties op te ontwikkelen en testen zijn: • Acer Aspire Intel Pentium IV 2.6 GHz, 1GB RAM • Dell Intel Pentium D 820 2.8 GHz, 1GB RAM
5.3
Software
De gebruikte tools en programmeertaalversies kan u hieronder vinden.
5.3.1
C++
ISO C++ 1998 Er werd gebruik gemaakt van de C++ standaard uit 1998 [57]. Meer hierover kan u vinden in de inleiding op C++ 2.1. Bloodshed Dev-C++ Dit is een open source IDE om te programmeren in C/C++. Het maakt gebruik van de Mingw port van GCC (GNU Compiler Collection) als compiler. DevC++ kan ook gebruikt worden in combinatie met Cygwin of een andere GCC gebaseerde compiler [9, 10]. wxDev-C++ 6.10.2 wxDev-C++ is een extensie van Dev-C++ ontwikkeld door Colin Laplace, . . . Deze extensie hielp bij het cre¨eeren van dialoogvensters, gewone vensters, . . . door het gebruik van een visuele form editor. wxDev-C++ wordt voortdurend uitgebreid met
pagina 104
5.3 Software
Hoofdstuk 5. Inleiding
nieuwe functies. Het wil een IDE/RAD1 tool zijn voor het ontwikkelen met wxWidgets [144]. WxWidgets 2.8.3 De wxWidgets vormen samen een raamwerk voor C++ (en andere talen) dat ons toelaat GUI’s te schrijven. Ook tal van andere toepassingen zijn mogelijk. Het grote voordeel van deze widgets is de portabiliteit (write once, compile/run everywhere). Versie 2 ondersteunt alle desktop versies van MS Windows, Unix with GTK+, Unix with Motif, en MacOS. Een OS/2 port is in ontwikkeling. Dit is een niet te onderschatten troef daar de ontwikkeling van een GUI toch redelijk tijdrovend is. Deze wxWidgets werden origineel ontwikkeld aan het Artificial Intelligence Applications Institute van de University of Edinburgh. Het werd intern gebruikt en voor het eerst publiek gemaakt in 1992 [145, 147]. Doxygen 1.5.1-p1 Doxygen is een documentatiesysteem voor C++, C, Java, Objective-C, Python, IDL (Corba en Microsoft varianten) en tot op zeker niveau PHP, C#, and D [32]. CppUnit CppUnit is een C++ unit testing framework. Het ontstond als een port van JUnit naar C++ gemaakt door Michael Feathers [25]. CppTest CppTest is een portabel, eenvoudig maar krachtig unit testing framework voor C++ [24]. TUT Template Unit Test is een klein en portabel unit test framework voor C++ [102]. Ook andere IDE’s zoals Netbeans, Eclipse en Borland C++ builder kunnen met C++ overweg. Netbeans en Eclipse doen dit via een extensiepakket. Borland C++ builder was niet gratis beschikbaar en werd dan ook niet verder onderzocht.
5.3.2
C#
C# 2.0 Meer informatie over C# 2.0 kan u vinden in sectie 2.2. C# 3.0 Meer informatie over C# 3.0 kan u vinden in sectie 2.2. .NET Framework 2.0 Meer informatie over het .NET Framework 2.0 kan u vinden in sectie 2.2.5. .NET Framework 3.0 Meer informatie over het .NET Framework 3.0 kan u vinden in sectie 2.2.5. Visual Studio 2005 IDE Visual Studio 9.0 (Orcas) IDE 1
Rapid Application Development. Omgevingen die van RAD gebruik maken, zorgen ervoor dat er snel resultaat verkregen wordt. De productiviteit zal hier dus hoger liggen. RAD wordt onder andere intensief gebruikt voor het maken van GUI’s. Het maken van een GUI vroeg vroeger heel wat werk door het handmatig moeten programmeren. Door de visuele hulpmiddelen en automatische codegeneratie gaat dit heel wat sneller.
pagina 105
5.3 Software
Hoofdstuk 5. Inleiding
.NET Reflector Reflector is een class browser, explorer, analysetool en documentatie viewer voor .NET. Het laat ons toe om op eenvoudige wijze assemblies in C#, Visual Basic en IL te bekijken, er in te navigeren en zoeken, te decompileren en analyzeren [71, 72]. NCover NCover, een code coverage tool voor .NET. NCover genereert statistieken over de broncode. Deze statistieken bevatten onder andere het aantal keren dat een codelijn werd uitgevoerd [68]. NCoverExplorer 1.3.6.15 NCoverExplorer is een kleine tool gemaakt door Grant Drake. Het toont de output van NCover coverage in een Windowsapplicatie. Het is daarbij een stuk sneller dan de html output [69]. NUnit 2.2.9 NUnit brengt unit-testing naar de .NET talen. Initieel werd NUnit geporteerd van JUnit, maar de huidige versie werd herschreven om voordeliger van .NET gebruik te kunnen maken [74]. Testdriven 2.7.2111 TestDriven.NET maakt het makkelijk om unit tests uit te voeren vanuit Visual Studio. Het ondersteunt alle versies van Microsoft Visual Studio .NET en integreert met .NET tools als NCover, NCoverExplorer, Reflector, TypeMock, dotTrace, NUnit, MbUnit, ZaneBug, MSBee en Team System [100]. Recentelijk werd ook een beta versie van de VS Orcas editie uitgebracht. Hierin zitten zaken als code metrics en unit testing reeds in vervat en hoeft men niet meer of toch minder naar plug-ins te grijpen.
5.3.3
Java
Java SE 6 De laatste offici¨ele release van de Java Standard Edition development kit. Netbeans 5.5 Een geavanceerde Java IDE volledig geschreven in Java zelf. JUnit Een externe bibliotheek voor unittests in Java. In Netbeans kunnen deze tests deels automatisch aangemaakt worden.
5.3.4
D
Digital Mars D compiler v1.014 De laatste versie van de D compiler waarmee onze programma’s getest zijn. Er komt echter geregeld een update van de compiler, meestal enkel om een aantal bugfixes door te voeren. SkyIDE 2 Een vrije IDE voor verschillende talen en hun compilers. Een geavanceerde IDE voor D bestaat nog niet. Entice Designer Een IDE speciaal gemaakt voor DFL waaraan een beperkte GUI-builder zit. 2
http://www.skyide.net/home.html
pagina 106
5.3 Software
Hoofdstuk 5. Inleiding
DFL D Forms Library. Een bibliotheek om forms aan te maken in D. DFL werkt enkel op Windows.
pagina 107
Hoofdstuk 6. GUI Applicatie : Sokoban
Hoofdstuk 6
GUI Applicatie : Sokoban 6.1
Inleiding
GUI applicaties zijn in de huidige computermaatschappij alomtegenwoordig. Ze kunnen voor duidelijkheid zorgen bij het voorstellen van data en daarnaast hebben mensen nu eenmaal liever programma’s die er leuk uit zien. Vanuit praktisch en esthetisch oogpunt is het voor een programmeertaal bijgevolg belangrijk om de nodige ondersteuning te bieden voor grafische componenten. Bibliotheken zijn hier vaak van groot belang.
6.2
Functionaliteit
Sokoban is Japans en betekent ”magazijnwerker”. Het is een 2D spel en bestaat uit een puzzel waar de speler diamanten door een doolhof moet verplaatsen, zodat deze op bepaalde doelen worden geplaatst. Er kan maar ´e´en diamant tegelijk verplaatst worden en de diamanten kunnen niet getrokken worden. Sokoban is in 1982 uitgevonden door Hiroyuki Imabayashi. Het spel is toen uitgegeven door Thinking Rabbit, een softwarefirma uit Takarazuka, Japan. Door Thinking Rabbit zijn later nog verschillende vervolgen uitgegeven zoals Boxxle, Sokoban Perfect en Sokoban Revenge. Er bestaan ook enkele varianten van het spel Sokoban, zoals Hexoban, Trioban en Multiban. [140]
6.3
Specificatie
De data voor het spel (een campagne bestaande uit levels) wordt ingelezen uit een databestand. Er werd gekozen voor een xml-formaat voor C# en Java en voor een gewoon tekstbestand voor C++ en D. Oorspronkelijk was het de bedoeling voor elke taal het xml-bestand in te lezen, maar dat bracht de nodige problemen met zich mee (zie verder). Eens een campagne geopend kan je een level in de lijst selecteren en zie je er een voorbeeld van. Naast het openen van de campagne kan je tevens elk level spelen en de campagne weer sluiten.
pagina 108
6.4 C++
Hoofdstuk 6. GUI Applicatie : Sokoban
Figuur 6.1: Sokoban
6.4
C++
De GUI die in C++ werd ge¨ımplementeerd bevat niet de volledige functionaliteit zoals beschreven in de specificatie. Toch werd reeds voldoende onderzocht en gebruik gemaakt wat de mogelijkheden zijn voor het ontwerpen van een GUI in C++. Het grafische gedeelte werd grotendeels afgewerkt, alsook de speellogica. De koppeling van de ingelezen levels in de lijst en het minipanel werd niet verder ge¨ımplementeerd daar het buiten het tijdsbestek viel. Een dummy level werd dan ook hard gecodeerd. Hoe de Sokoban in C++ werd gestructureerd kan u hieronder lezen.
6.4.1
Ontwerp
De GUI is opgebouwd uit een aantal klassen waaronder SokobanApp die de applicatie voorstelt. Deze zal SokobanFrm starten. Dit is het eigenlijke GUI venster. Bij het bouwen van deze GUI werd intensief gebruik gemaakt van wxWidgets. Dit is een externe bibliotheek die ons toelaat om onder andere platformonafhankelijke GUI’s te maken. Indien men gebruik zou maken van
pagina 109
6.4 C++
Hoofdstuk 6. GUI Applicatie : Sokoban
bijvoorbeeld de Windows API, zou de code enkel op een windows platform kunnen uitgevoerd worden. Het gebruik maken van een externe bibliotheek is nodig aangezien C++ in de taal zelf geen mogelijkheden biedt voor het maken van een dergelijke GUI. Meer over het gebruik van wxWidgets kan je vinden in sectie 6.4.4.1. Naast SokobanFrm hebben we ook nog PlayFrm. Dit is het speelvenster waarop het BasicPanel een plaats zal innemen. BasicPanel luistert naar de pijltjestoetsen en bevat tevens de spellogica. Verder is onze Campaign opgebouwd uit een lijst van Level s. Deze lijst is een vector uit de C++ bibliotheek. Een Level bevat een Matrix. Een Matrix is ge¨ımplementeerd als een vierkante array van ints. Dit stelt het speelveld voor. In de volgende sectie zullen we enkele specifieke zaken in verband met de taal en de syntax behandelen.
6.4.2
De taal
Hieronder zullen we enkele opmerkingen omtrent het gebruik van de taal zelf formuleren. Aangezien onze achtergrond eerder Java en C# geori¨enteerd was, kregen we bij het bestuderen van C++ dan ook meer inzicht in hetgeen waar deze voorgaande talen hun inspiratie haalden. Vooral bij C# en D werd op vlak van syntax veel uit de C++ syntax gehaald. Meervoudige overerving Er werd hier geen gebruik gemaakt van meervoudige overerving. Preprocessing In C++ wordt redelijk wat gebruik gemaakt van preprocessordirectieven. Deze zorgen onder andere voor conditionele compilatie, het importeren van files en het vermijden van fouten door meermaals importeren van een zelfde file. Reden voor het importeren van header files is het compileer en link model van C++. Dit is nodig opdat de compiler alle relevante symbolen zou kunnen herkennen. Ook in deze applicatie moeten we gebruik maken van preprocessing om bijvoorbeeld ook de juiste widgetfiles te laden of nodige bibliotheekklassen te importeren. 1 2
#i f n d e f #define
MATRIX h MATRIX h
3 4
#i f d e f
5 6
BORLANDC #pragma h d r s t o p
#endif
7 8 9 10 11 12 13
#i f n d e f WX PRECOMP #i n c l u d e <wx/wx . h> #i n c l u d e <wx/ frame . h> #e l s e #i n c l u d e <wx/ wxprec . h> #endif
pagina 110
6.4 C++
Hoofdstuk 6. GUI Applicatie : Sokoban
14 15 16
#include #include <s t r i n g >
Main methode Het startpunt van een programma in C++ is een globale functie main(). Ook in D is dit zo. In andere talen zoals C# en Java dient deze main methode binnen een klasse te staan en zijn dit statische members. We kunnen dus stellen dat de laatste twee talen dan ook strikter objectgeori¨enteerd zijn. int grootte De grootte van het type int blijkt in C++ platformafhankelijk zijn. Dit kan natuurlijk voor compatibiliteitsproblemen zorgen. Pointers
Het gebruik van pointers blijkt niet zo evident en blijft een grote bron van fouten.
Onduidelijke errors Het genereren van duidelijke errors is vaak een probleem. Regelmatig verschijnen er bij compilatie error boodschappen die ons niet verder kunnen helpen bij het vinden van een fout. Dit komt doordat ze verwijzen naar verdere stukken code die eigenlijk niks met de fout te maken hebben. Bounds checking Bij het gebruik van de set methode voor de matrix dient men afhankelijk van de API van de Matrixklasse naast de dubbele array ook de dimensies als extra parameters mee te geven. Dit moet aangezien we dit niet kunnen vragen aan de array zelf. Dit is niet echt proper maar werkt wel. Xml Het inlezen van xml kan niet rechtstreeks via de taal gebeuren. Dit kan wel door gebruik van externe bibliotheken. Een voorbeeld hiervan is Xerces 1 .
6.4.3
IDE : DevC++
De gebruikte IDE DevC++ met de extensie voor wxWidgets bleek op bepaalde vlakken een grote hulp. Op andere vlakken schoot hij dan weer tekort. Zo is de ondersteuning voor RAD redelijk goed en kan men snel een GUI bouwen op een visuele manier. Met de properties kan men het ontwerp dan verder gaan verfijnen en uitlijningen van componenten beter laten overeenkomen. Aan de andere kant kwam het ook wel vaak voor dat de IDE gewoon vast loopt en een foutmelding geeft die de gebruiker niet veel verder helpt. Of dit aan de originele IDE of het gebruik van de extensie ligt is niet geweten. Ook het debuggen kan zeker beter en intuitiever. Bij het bekijken van sommige objecten tijdens het debuggen, kon sommige informatie gewoon niet worden bekeken doordat vensters te klein waren. Een boodschap verscheen dan die de gebruiker ook 1
http://xml.apache.org/xerces-c/
pagina 111
6.4 C++
Hoofdstuk 6. GUI Applicatie : Sokoban
niet verder hielp. Het gebruik van de sneltoetsen bleek soms helemaal niet te werken. In de IDE wordt ook geen gebruik gemaakt van opvouwbare codeblokken die anders wel handig zijn. Het bestaan van headerfiles bleek hier dan weer een voordeel daar we in een oogopslag de methoden konden bekijken. Installatie en updaten van de IDE was geen probleem en was ook geen kwestie van honderden megabytes zoals dat bij Visual Studio het geval is. Bovendien is de IDE een open source applicatie en gratis. Dit is niet het geval bij MS Visual Studio en IDE’s van bijvoorbeeld Borland. Voor C++ was de zoektocht naar een IDE die de programmeur wat kon helpen bij het ontwikkelen van een GUI dus niet zo heel eenvoudig. Dit is zeker het geval indien men IDE’s als Borland C++ builder en Visual Studio die wel een betere ondersteuning voor GUI’s hebben maar die betalend zijn, niet meerekent. Visual Studio was wel beschikbaar voor ons, maar werd niet gebruikt om die reden dat er niet-standaard Windowsspecifieke code zou gegenereerd worden (nl. Visual C++). Voor C# werd Visual Studio dan weer wel gebruikt aangezien C# specifiek voor het .NET platform werd ontwikkeld.
6.4.4
Bibliotheken
Het maken van een GUI in C++ blijkt niet zo eenvoudig. Er is namelijk geen ondersteuning van de taal zelf om een GUI te kunnen maken. Dit noodzaakt ons om de toevlucht te nemen in bibliotheken van derden die dit ons wel toelaten. Een minpunt hierbij is dat men vaak gebruik dient te maken van de API voor het onderliggende native systeem. Eens men in de broncode enkel voor dat systeem gecodeerd heeft, is de broncode dan ook niet meer portabel naar andere platforms toe. Voorbeelden van bibliotheken zijn de Windows API, wxWidgets, Qt (Trolltech), Fox, GTK+, . . . Wij gebruikten in onze applicatie de wxWidgets. 6.4.4.1
wxWidgets
Wat is nu wxWidgets? wxWidgets (wxWindows) is een open source bibliotheek, eigenlijk een framework dat ons toelaat om applicaties te ontwikkelen voor verschillende platformen (cross platform). Het heeft ook een eenvoudige API voor GUI applicaties. Met eenvoudig wordt hier het gebruik bedoeld. Het leren van het framework vraagt toch enige aandacht en is hier en daar complex. Eens men dit echter onder de knie heeft, kan men snel nuttige applicaties bouwen [147]. Aangezien ze voor verschillende platformen werken, zal er ook een native look en feel zijn (Windows/Unix/Mac). Er wordt hierbij geen gebruik gemaakt van emulatie. De bibliotheek maakt zelf gebruik van de API’s voor het onderliggende native systeem. Men kan hierbij dus spreken van write once, compile / run everywhere [145]. Een wxWindows GUI programma bestaat uit : • Een applicatie object, dit is een instantie van de wxApp klasse
pagina 112
6.4 C++
Hoofdstuk 6. GUI Applicatie : Sokoban
• Een frame object, dit is een instantie van de wxFrame klasse. Een frame kan een menu, statusbalk, icoon, . . . bevatten. • Het frame kan een container zijn voor andere objecten zoals text controls, knoppen, splitters, . . . wxFrame en wxApp stellen dus een venster en een applicatie voor en werden hier dan ook gebruikt. wxApp 1
Het eerste wat ons opvalt is de macro :
IMPLEMENT APP( SokobanFrmApp ) die het SokobanFrmApp object construeert en een toegangspunt maak voor onze applicatie. Het vervangt bijvoorbeeld, bij gebruik van de Windows API, de WinMain ( . . . ) functie [148]. Ook het gebruik van resource bestanden is mogelijk. In onze applicatie werd er echter geen gebruik van gemaakt. Wanneer we hier een resource bestand willen gebruiken dienen we er steeds ´e´en te gebruiken waar onderstaande lijn in voorkomt zodat de wxWidgets deze resources ook gemakkelijk kunnen gebruiken.
1
#include ”wx/msw/wx . r c ”
wxFrame Verschillende properties van een wxFrame waaronder het sluitbaar of minimaliseerbaar zijn kunnen via preprocessordirectieven worden ingesteld. 1 2
#define PlayFrm STYLE wxCAPTION | wxSYSTEM MENU | wxMINIMIZE BOX | wxCLOSE BOX
wxOpenFileDialog Bij het openen van een file kunnen we beroep doen op wxOpenFileDialog. Deze klasse voorziet in een bestandskiezer die zeer eenvoudig in gebruik is. De methode ShowModal (lijn 6) geeft ons terug of er op OK of Cancel werd geduwt. Indien OK kunnen we het pad naar het aangeduide bestand opvragen (lijn 7) en openen met een inputstream (lijn 8 en 11). 1 2 3 4 5 6 7 8 9 10
WxOpenFileDialog1 = new w x F i l e D i a l o g ( this , wxT( ” Choose a f i l e ” ) , wxT( ” ” ) , wxT( ” ” ) , wxT( ” ∗ . ∗ ” ) , wxOPEN ) ; i f ( WxOpenFileDialog1−>ShowModal ( ) == wxID OK) { WxStaticText5−>S e t L a b e l ( WxOpenFileDialog1−>GetPath ( ) ) ; ifstream inputstream ; v e c t o r ∗ l e v e l l i s t = new v e c t o r ( ) ; WxMemo1−>AppendText ( ” Opening f i l e . . . \ n” ) ;
pagina 113
6.4 C++
i n p u t s t r e a m . open ( WxOpenFileDialog1−>GetPath ( ) ) ; i f ( ! inputstream ) { c e r r << ” Unable t o open f i l e ” << ”\n” ; exit (1); // c a l l system t o s t o p }
11 12 13 14 15 16
Hoofdstuk 6. GUI Applicatie : Sokoban
} De mogelijkheden van deze dialog zijn echter niet zo uitgebreid als in andere talen wel het geval is. Events Om de interactie te voorzien in GUI’s wordt vaak gebruik gemaakt van events. Ook hier werden events gebruikt [146]. Zoals je in onderstaand voorbeeld kan zien is de eventtabel die gebruikt wordt niet zo moeilijk te verstaan. Het is een collectie van macro’s. De eerste (lijn 1) geeft het begin van de tabel aan. De volgende twee (lijn 2 en 3) linken een contante (het event) aan een memberfunctie (de handler van het event). De laatste eindigt de tabel. De eventtabel mapt events zoals EVT PAINT op memberfuncties zoals BasicPanel::OnPaint. We kunnen zoveel eventtabellen als nodig in ons programma opnemen. Het begin van de tabel geeft dan ook aan dat de tabel behoort aan BasicPanel dat is afgeleid van wxPanel. We dienen ook aan te geven dat de klasse de tabel zal gebruiken. Dit doen we aan de hand van de DECLARE EVENT TABLE macro die zich in het klasprototype in het interfacebestand bevindt. Als laatste dienen we dan natuurlijk ook de memberfuncties te voorzien die het event zullen afhandelen [148].
1 2 3 4
BEGIN EVENT TABLE( B a s i c P a n e l , wxPanel ) EVT PAINT ( B a s i c P a n e l : : OnPaint ) EVT KEY UP( B a s i c P a n e l : : OnKeyUp) END EVENT TABLE( )
wxBufferedPaintDC buffer via : 1
Bij het tekenen van het speelvenster werd gebruik gemaakt van een
wxBufferedPaintDC dc ( t h i s ) ; Dit zorgt er voor dat alle wijzigingen worden bijgehouden in een buffer en dan alles in ´e´en keer geupdate wordt. Het gebruik hiervan was nodig om hertekeneffecten te vermijden. Indien je de werker zou verplaatsen zou je het hele scherm zien hertekenen en dit is niet echt bevorderlijk voor het spelen. Afbeeldingen Ook afbeeldingen kunnen via wxWidgets worden voorgesteld. Voor elk bestandstype dient men wel een handler te declareren. Men kan ook via ´e´en eenvoudig commando handlers voor alle bestandstypen laten genereren. Het gebruik van een afbeelding kan u hieronder vinden:
pagina 114
6.5 C#
1 2
Hoofdstuk 6. GUI Applicatie : Sokoban
wxImage img1 (wxT( ” t a r g e t d i a m o n d . JPG” ) , wxBITMAP TYPE JPEG, 0 ) ; wxBitmap bmpdiamond ( img1 ) ;
6.5
C#
Bij het leren van de C# taal en zijn omgeving werd al gauw duidelijk dat het hier ging om een totaalpakket. C# en .NET zijn duidelijk ge¨ıntegreerd. Vaak zijn delen van .NET geschreven in C# en gebruikt C# de mogelijkheden van .NET dan opnieuw. Om C# dan ook goed te begrijpen en te plaatsen in zijn context, was het ook nodig om die omgeving wat nader te onderzoeken. Naast de integratie van .NET en C# viel ook de grote omvang van de ondersteuning op. Dit omvat onder andere een uitgebreide bibliotheek met voldoende (soms zelfs overmatig aanwezige) documentatie (MSDN), alsook een uitgebreide IDE waarmee de ontwikkelaars aan de slag kunnen. Hieronder zullen we dus niet enkel de taal op zich onder de loep nemen maar ook de omgeving en tools waarmee het interageert. Ook voor de volgende toepassingen geldt dit zelfde verhaal. Een niet te verwaarlozen aspect bij de overstap naar een nieuwe taal is het leren van de bijhorende terminologie (zie ook sectie 3.1.1). Al te vaak hebben twee talen een andere terminologie voor eenzelfde begrip. Dit kan echter voor verwarring zorgen. Ook hier deed dit zich voor. Zo spreken we bijvoorbeeld in C# van attributen en in Java over annotaties, terwijl in D met attributen de modifiers uit C++, C# en Java bedoeld worden. We zullen nu even bekijken hoe de Sokoban in C# werd gestructureerd.
6.5.1
Ontwerp
De Sokoban applicatie werd opgesplitst in drie delen. Er werden twee bibliotheken en ´e´en uitvoerbaar bestand aangemaakt. De bibliotheken SokobanCtrlLib en SokobanLib bevatten respectievelijk de User Controls (zie sectie 6.5.4) en de ondersteunende klassen zoals de CampaignReader die een campagne zal inlezen, de Campaign en Level klasse en het SokobanModel en de mogelijke SokobanTypes (in een enum). Het uitvoerbaar bestand bevat dan het hoofdprogramma dat op zijn beurt de twee WinForms bevat. Dit is het hoofdvenster en dan ook nog bijkomend het speelvenster waarin het Sokobanspel gespeeld zal worden. Inlezen van de campagne De xml-file met daarin de campagne zal bij het openen worden ingelezen door een instantie van de klasse CampaignReader. Hiervoor wordt de methode Read gebruikt die een FileStream object als parameter zal meekrijgen. Bij het inlezen zal een Campaign object aangemaakt worden. Hierin wordt dan de naam van de campagne en een lijst van Level s bijgehouden. Deze lijst wordt dan ook bij het inlezen ingevuld. Een level bestaat uit een vierkante matrix van int’s. De aangemaakte Campaign wordt dan door de Read methode teruggegeven.
pagina 115
6.5 C#
Hoofdstuk 6. GUI Applicatie : Sokoban
Deze Campaign-instantie kan men dan meegeven aan het model. Dit model is in deze context zeer eenvoudig daar het niks anders dan een container voor een Campaign is. Het model kan dan als DataSource van een BindingSource instantie worden ingesteld. Andere componenten kunnen gebruik maken van deze BindingSource voor het verkrijgen van de data die ze gebruiken. Meer informatie over BindingSource en het gebruik ervan kan u vinden in 6.5.3.3.
6.5.2
De taal
Hier werd gebruik gemaakt van C# 2.0. Naast heel wat gelijkenissen met de andere talen zijn er ook heel wat verschillen. Veel van deze verschillen werden in het vergelijkende hoofdstuk reeds aangehaald. Hieronder zullen we voor zaken die in onze praktijktesten naar voor kwamen toelichten hoe C# bepaalde zaken gemakkelijker, moeilijker of gewoon anders aanpakt. 6.5.2.1
Syntax
De mengeling van de syntax van verschillende talen die C# eigenlijk wel is, zorgt er voor dat het voor veel programmeurs een toegankelijke en direct begrijpbare taal is. Aan de andere zijde kunnen deze soms minuscule verschillen ook voor enige verwarring zorgen. 6.5.2.2
Broncode en documentatie
Aangezien we slechts xml-documentatie kunnen laten genereren met Visual Studio, gingen we op zoek naar een andere mogelijkheid om toch een ietwat leesbaarder documentatie te verkrijgen. We vonden dit in de tool Doxygen die we ook voor C++ konden gebruiken. Het ontbreken van een automatische mogelijkheid om dit in VS te doen is toch wel een minpunt. 6.5.2.3
Parti¨ ele klassen
Bij de GUI worden parti¨ele klassen gebruikt om de zaken die met functionaliteit te maken hebben te scheiden van zaken die met het uitzicht te maken hebben. Zo zal de automatisch gegenereerde code van de GUI in een apart bestand worden opgeslagen gescheiden van de code die door de gebruiker wordt toegevoegd. De compiler zal bij het compileren deze twee dan weer samen brengen in ´e´en klasse. Deze werkwijze is proper en overzichtelijk. De programmeur wordt niet verward met code die met de GUI te maken heeft. 6.5.2.4
Preprocessor Directieven
Wat vaak gebruikt werd in de Sokoban applicatie en ook in de andere C# applicaties was het #region directief. Dit directief bleek heel handig in samenwerking met de IDE. Het zorgt voor een afbakening van stukken broncode. Na het directief kan je dan ook nog een korte commentaar over de region kwijt. De IDE herkent deze regions en kan ze ook dichtvouwen, net zoals hij dat met methoden en klassen kan. Vooral bij lange methoden bewees dit zijn nut. De code werd leesbaarder. In dichtgevouwen stand en met gebruik van de korte commentaar kan
pagina 116
6.5 C#
Hoofdstuk 6. GUI Applicatie : Sokoban
in het kort worden weergegeven wat de onderliggende code doet. Het algoritme kan zo beter worden overzien. 6.5.2.5
Memory management
In de GUI parti¨ele klasse zorgt de InitializeComponent methode ervoor dat alles ge¨ınitialiseerd wordt en op de panels wordt gezet, handlers aangemaakt, ... De Dispose methode daarentegen zorgt ervoor dat resources opgekuist zullen worden. 6.5.2.6
RAII
We hebben in deze context enkele keren het using keyword gebruikt voor het benaderen van streams. Dit blijkt een handige en propere oplossing. De streams worden automatisch weer afgesloten. De code die de stream gebruikt, staat in een mooi codeblok. Je kan dit heel precies gaan aanduiden en wordt dan ook fine-grained genoemd. Dit is fijner dan in C++. 6.5.2.7
Drag ’n Drop
Drag ’n drop werd eenvoudig mogelijk gemaakt door gebruik van delegates. We hebben een delegate DelegateOpenFile(String s) waar we een instantie van zullen maken. We hebben verder een methode: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// Van z o d r a men een b e s t a n d o v e r de a p p l i c a t i e s l e e p t // z a l de methode worden aangeroepen . Als h e t een g e l d i g // b e s t a n d i s z u l l e n we h e t k o p i e r e n , a n d e r s g e b e u r t e r n i k s DragEnter ( o j b e c t s e n d e r , DragEventArgs e ) { i f ( e . Data . GetDataPresent ( DataFormats . F i l e D r o p ) ) e . E f f e c t = D r a p D r o p E f f e c t s . Copy ; e l s e e . E f f e c t = D r a g D r o p e f f e c t s . None ; } ... // I n d i e n h e t b e s t a n d dan ook l o s g e l a t e n wordt // z a l men de naam van h e t b e s t a n d o p v r a g e n en naar // een s t r i n g s c h r i j v e n . We ge ve n d e z e dan mee met een // d e l e g a t e d i e de methode z a l u i t v o e r e n d i e h e t b e s t a n d o p e n t DragDrop ( object s e n d e r , DragEventArgs e ) { ... Array a = ( Array ) e . Data . GetData ( DataFormats . F i l e D r o p ) ; // bestandsnaam string s = a . GetValue ( 0 ) . T o S t r i n g ( ) ; // OpenFile asynchroon oproepen t h i s . B e gi n I nv o k e ( m DelegateOpenFile , new Object [ ] { s } ) ; ...
pagina 117
6.5 C#
22
Hoofdstuk 6. GUI Applicatie : Sokoban
}
Bij het laden van de sokoban maken we een nieuwe delegate aan via new DelegateOpenfile(this.Openfile);. De methode Openfile zal de file dan laden. In bovenstaande codefragment zijn hier en daar stukken weggelaten die weinig relevant zijn voor het begrijpen van de drag ’n drop zoals bv. het opvangen van exceptions. 6.5.2.8
Allerlei
DoubleBuffer Voor de graphics tijdens het spelen werd gebruik gemaakt van een DoubleBuffer. Indien we dit niet zouden doen, zou men net zoals bij C++ het geval was het refreshen van het venster zien en zou de applicatie veel te traag reageren. Move en hide Door gebruik van een Move methode die voor een verplaatsing moest zorgen werd ongewild bijna een methode van System.Windows.Forms.Control verborgen. De compiler reageerde hierop met de onstaande warning: Warning 1 ’Sokoban.PlayForm.Move(int, int)’ hides inherited member ’System.Windows.Forms.Control.Move’. Use the new keyword if hiding was intended. PlayForm.cs 114 18 SokobanMain
6.5.3 6.5.3.1
.NET bibliotheek OpenFileDialog
Voor het openen van een file werd de klasse OpenFileDialog gebruikt. Deze bezit enkele handige instellingen waar we gebruik kunnen van maken. Zo kunnen we een filefilter opgeven via een expressie: 1
” sok f i l e s ( ∗ sok ) | ∗ . sok | A l l f i l e s ( ∗ . ∗ ) | ∗ . ∗ ” Ook zaken als een filterindex, multiselect, restoredirectory, title, . . . kunnen worden ingesteld. 6.5.3.2
Datatoegang
Het inlezen van onze xml-file werd geprobeerd via een Dataset object (System.Data), daar dit handig zou zijn om aan een BindingSource object te geven als bron voor het vullen van onder andere de lijst. De dataset zou dan fungeren als een soort model. Dit was echter niet succesvol door het formaat van onze xml-file. De datakolommen in de dataset kwamen niet overeen met het door ons beoogde resultaat [30]. Er zat niks anders op dan het manueel inlezen van de xml via de xml-klassen uit de namespace System.Xml. De klassen die daartoe gebruikt werden zijn XmlDataDocument, XmlNode en XmlNodeList. Via enkele xpath queries konden de levels dan goed ingelezen worden. Hoewel dit eens gekend niet zo heel moeilijk is, kan het natuurlijk nog eenvoudiger. Via xlinq uit C# 3.0 zou het nog makkelijker moeten worden [64]. Deze technologie werd hier echter niet meer uitgeprobeerd.
pagina 118
6.5 C#
6.5.3.3
Hoofdstuk 6. GUI Applicatie : Sokoban
BindingSource
Met de Sokoban applicatie konden we ook eens testen hoe het MVC2 patroon in de verschillende talen kon worden ge¨ımplementeerd. Dit kan heel eenvoudig in .NET door gebruik van de BindingSource klasse. Deze klasse vereenvoudigt het binden van controls (bv. lijsten) van een form aan data door het voorzien van een currency management, notificatie bij veranderingen en andere services tussen Windows Forms controls en databronnen. Wat we hiervoor dienen te doen, is het binden van het BindingSource object aan de data aan de hand van de DataSource property. Voor complexe bindingen kan je ook nog DataMember gaan gebruiken die specifieke kolommen of lijsten uit de bron zal binden. Daarna dien je ook de control nog aan de BindingSource te binden. Vanaf nu zal alle interactie met de data dan ook zichtbaar zijn voor de controls daar deze via de BindingSource aan de data gebonden zijn. Met dit systeem hoeven we dus zelf niet meer te voorzien in een implementatie van het MVC patroon. 6.5.3.4
Collections
Hier werd gebruik gemaakt van de generieke collectie List. Voor het type T werd het Level genomen, zodat we in onze Campaign een lijst van Levels konden bijhouden, nl. List. Deze generieke vorm van levels zorgt voor wat meer typeveiligheid en zorgt ook voor grotere performantie. Indien we gewone lijsten zouden gebruiken voor het opslaan van deze primitieve types zoals int’s, dan zal er steeds boxing en unboxing plaats vinden. Indien we het generieke type gebruiken is dit niet meer nodig en hebben we tijd uitgespaard.
6.5.4
IDE : Microsoft Visual Studio
Met MS Visual Studio 2005 hadden we bij het ontwikkelen van deze applicatie toch wel een krachtig en eenvoudig te gebruiken ontwikkelomgeving in handen. Door de uitgebreide mogelijkheden van de ontwikkelomgeving was het soms wel even zoeken waar een feature zich bevond of hoe men de feature kon gebruiken in de eigen context. Voorbeeld hiervan is de mogelijkheid om zelf User Controls te defini¨eren. Dit zijn eigen ontwikkelde componenten die een bepaalde functionaliteit op zich nemen. Deze User Controls kan men dan invoudig inpassen in een bestaand ontwerp. Doordat ze aparte componenten zijn, zijn ze natuurlijk ook herbruikbaar. De handelingen om deze User Controls te ontwikkelen en te gebruiken in onze toepassing vergde dus enig zoekwerk, maar blijkt achteraf betrekkelijk eenvoudig en handig. De uitgebreide documentatie en bijhorende voorbeelden hielpen ons hier vooruit. Eens goed aan de slag met Visual Studio kan men dus snel resultaat krijgen. Hieronder worden een aantal zaken aangehaald die uit de ontwikkeling van de toepassing als zijnde goed of slecht naar voor kwamen. Het is geenszins de bedoeling om een volledig overzicht van de mogelijkheden van Visual Studio aan te bieden. 2
Model-View-Controller is een architecturaal patroon gebruikt in de software engineering [108].
pagina 119
6.5 C#
6.5.4.1
Hoofdstuk 6. GUI Applicatie : Sokoban
Integratie
Visual Studio heeft een groot aantal mogelijkheden. Allerlei tools zijn ge¨ıntegreerd in het pakket of kunnen worden ge¨ıntegreerd in het pakket. Verder bevat VS mogelijkheden om te debuggen, het aanmaken van xml-documentatie, gebruik van intellisense, een objectviewer, toolbox voor drag ’n drop design van GUI’s, gescheiden code voor ontwerp en functionaliteit, SQL server integratie, Reflector, unit tests, feedback en help via de community. Nadelen zijn natuurlijk het prijskaartje, de grootte die het programma op de schijf inneemt en de afhankelijkheid van automatische dit-en-dat tools die hier gecre¨eerd wordt. Door die afhankelijkheid zou men al kunnen spreken van een vendor-lockin. Door al deze ge¨ıntegreerde tools probeert men de gebruiker onvoorwaardelijk aan het product te binden. Projectproperties VS laat ons toe de eigenschappen van een project eenvoudig te beheren. Alle eigenschappen worden mooi gebundeld en zijn eenvoudig aanpasbaar. Zo kan je snel aan je resources, deployment settings, settings die geladen of weggeschreven dienen te worden. Debugger De Debugger en compiler in VS laat ons toe in 2 modi te werken. De eerste modus is de debug modus, waarbij allerlei informatie over de objecten wordt bijgehouden. De tweede modus is de releasemodus die geoptimaliseerd is voor het beste resultaat. 6.5.4.2
WinForms
De WinForms zijn de vensters van Windows. Ze vormen de basis van de GUI applicaties. We gebruikten ze hier dan ook. Op de WinForms werden zoals bij het PlayForm User Controls geplaatst. VS zorgt er voor dat we onze WinForm visueel kunnen ontwerpen. De achterliggende code wordt dan automatisch in een aparte parti¨ele klasse gegenereerd. 6.5.4.3
User Controls
User controls zijn componenten die UI-mogelijkheden hebben. De User Controls werden hier gebruikt om bepaalde herbruikbare stukken te maken. Deze konden aan de toolbox worden toegevoegd en op een WinForm worden geplaatst.
6.5.5
Microsoft Developer Network (MSDN)
Belangrijk bij het ontwikkelen van programma’s is een goede API en duidelijk documentatie hieromtrent. Met de MSDN library heeft men hier onnoemelijk veel informatie ter beschikking. Men kan soms wel spreken van een overvloed aan informatie ten opzichte van andere talen en hun documentatie. Onlangs werd de structuur van de library aangepast. Deze zou nog intu¨ıtiever van aard moeten zijn. Ook Ajax-technieken werden er in aangebracht. Handig is het selectief kunnen zijn ten aanzien van de taal. Indien je enkel in C# ge¨ınteresseerd bent, vink je de andere talen uit en krijg je enkel C# voorbeelden op de betreffende pagina’s. De andere voorbeelden worden weggefilterd.
pagina 120
6.6 Java
6.6 6.6.1
Hoofdstuk 6. GUI Applicatie : Sokoban
Java IDE : Netbeans - Matisse
Sinds Netbeans 5.0 is in de IDE het project Matisse 3 ingebouwd. Dit is een geavanceerde GUI builder waarmee je componenten vrij op een form kan plaatsen. Visuele hulplijnen maken het eenvoudig de verschillende componenten op mekaar af te stemmen wat betreft grootte en positie (op de form en relatief tegenover elkaar). De code voor de layout wordt automatisch gegenereerd in de achtergrond waardoor je niet meer handmatig met de swing layout managers moet werken hetgeen een secure en tijdrovende bezigheid was. In Netbeans zit ook een Palette Manager waarmee je zelf componenten aan het Palette kan toevoegen naast de beschikbare componenten. Op die manier kan je ook met zelfgemaakte componenten van de GUI builder gebruik maken. Van deze functionaliteit is ook hier gebruik gemaakt. Het PlayPanel (gebruikt om een level te spelen) en MiniPanel (gebruikt om een preview van de level te geven) erven beide over van een basisklasse BasicPanel en zijn toegevoegd aan het Palette. Op events in de GUI reageren is ook heel eenvoudig met de GUI builder. Je selecteert de component en de event(s) die je wil gebruiken en in de code wordt de methode automatisch aangemaakt, je hoeft enkel nog de logica in te vullen.
6.6.2
Ontwerp
Naast de verschillende panels die hierboven besproken werden volgt hier uitleg over de rest van het ontwerp. Een Campaign wordt ingelezen van een xml-bestand, de Level s worden eruit geparst en bijgehouden in de campagne. De verschillende afbeeldingen zijn ge¨ımplementeerd als klassen die een basisklasse MyIcon uitbreiden. MyIcon is een klasse die de interface Icon uit de swing bibliotheek implementeert. Om te reageren op events in de GUI werd het MVC-patroon ge¨ımplementeerd met een model en luisteraars (eng. listeners) die luisteren naar wijzigingen in het model.
6.6.3
MVC - Events - Listeners
We hebben een LevelListModel geschreven dat de abstracte klasse AbstractListModel uit de swing bibliotheek uitbreidt. Het model bestaat enkel uit een tabel die de levels bijhoudt. We hoeven nu zelf niet meer in te staan voor de registratie van de luisteraars. Luisteraars die zich registreren bij het model worden automatisch verwittigd als er zich in het model een verandering heeft voorgedaan. Ze kunnen dan op de gepaste manier reageren op die verandering. Er wordt nog gebruik gemaakt van een ander model, namelijk een DefaultListSelectionModel dat standaard in de swing bibliotheek zit. Dit dient om te reageren op een selectieverandering in de lijst. In onze applicatie is dit nodig om de juiste level weer te geven in het MiniPanel als er een andere level in de lijst wordt geselecteerd. 3
http://form.netbeans.org/
pagina 121
6.7 D
1 2 3 4 5 6 7
Hoofdstuk 6. GUI Applicatie : Sokoban
J L i s t l i s t = new J L i s t ( ) ; L e v e l L i s t M o d e l l l m = new L e v e l L i s t M o d e l ( ) ; D e f a u l t L i s t S e l e c t i o n M o d e l dlsm = new D e f a u l t L i s t S e l e c t i o n M o d e l ( ) ; dlsm . s e t S e l e c t i o n M o d e ( D e f a u l t L i s t S e l e c t i o n M o d e l . SINGLE SELECTION ) ; l i s t . setModel ( l l m ) ; l i s t . s e t S e l e c t i o n M o d e l ( dlsm ) ; l i s t . a d d L i s t S e l e c t i o n L i s t e n e r ( mi n i Pa n el ) ;
6.6.4 6.6.4.1
De taal Enum
Sinds Java 5 werd een enum type aan de taal toegevoegd. Deze functionaliteit werd hier nog op de oude manier ge¨ımplementeerd, namelijk door een publieke interface SokobanTypes met publieke statische variabelen. 6.6.4.2
ExtensionFilter
Er werd een klasse ExtensionFilter geschreven die de abstracte klasse FileFilter uitbreidt. Door deze FileFilter mee te geven aan de JFileChooser (een swing component om een bestand op schijf te selecteren), zorgen we dat enkel documenten met de gepaste .sok extensie geopend kunnen worden.
6.6.5
Bibliotheek : javax.swing
Java heeft in de standaard bibliotheek het package javax.swing zitten voor grafische toepassingen. Het package voorziet een set ”lichtgewicht” componenten die zoveel mogelijk op dezelfde manier werken voor elk platform [59].
6.7 6.7.1
D Ontwerp
Zoals vermeld in de inleiding wordt een tekstbestand ingelezen en geen xml-bestand. In de standaard bibliotheek van D zit er geen ondersteuning om xml-bestanden in te lezen maar wel voor het in- en uitlezen van gewone tekstbestanden. Daarom wordt de campagne als een sequentieel tekstbestand ingelezen. Met externe bibliotheken zou het echter ook mogelijk zijn het xml-bestand in te lezen. In Tango (zie 2.4.7) bijvoorbeeld zit er ondersteuning. De tekst die uit het bestand is gelezen wordt meegegeven aan een nieuw Campaign object dat op zijn beurt de data per level uit de tekst filtert en Level objecten aanmaakt. Een instantie van de klasse is dan ook niets meer dan een abstractie van een aantal levels opgeslagen in een array. Ook de naam van de campagne wordt bijgehouden. Voor de afbeeldingen werd hier gebruik gemaakt van de klasse Picture uit de DFL bibliotheek.
pagina 122
6.7 D
Hoofdstuk 6. GUI Applicatie : Sokoban
We hebben deze klasse uitgebreid met een eigen klasse MyPicture om zo de gezamenlijke functionaliteit van de 6 mogelijke afbeeldingen te verzamelen. Een level bestaat naast de naam uit een 2-dimensionale array van MyPictures. Om een level voor te stellen worden panels gebruikt met daarop de MyPictures getekend. Naast het BasicPanel dat de algemene functionaliteit bevat, is er ook een PlayPanel (dat de speelfunctionaliteit bevat) en een MiniPanel.
6.7.2 6.7.2.1
De taal string switch
Er werd hier gebruik gemaakt van de string switch mogelijkheden in D om uit het ingelezen campagnebestand (een tekstbestand) de verschillende types af te leiden. Hierdoor werden heel wat cast opdrachten uitgespaard. Het bepalen van de types aan de hand van het tekstbestand gebeurd in de klasse Level. 1 2 3 4 5 6 7 8
// l i n e i s een l i j n u i t h e t s p e l b e s t a n d switch ( l i n e [ i . . i +1]){ case ” 0 ” : i c o n s [ i ] [ j ] = new T i l e ( ) ; case ” 1 ” : i c o n s [ i ] [ j ] = new Wall ( ) ; // . . . }
6.7.2.2
break ; break ;
cast
In D is er niet zoiets als een instanceof in Java. Er bestaat wel een gelijkaardige functionaliteit. Als een cast opdracht faalt is het resultaat van de opdracht null. En null komt overeen met een waarheidswaarde false. Dir wordt onder andere gebruikt om de spellogica te implementeren in PlayPanel. i f ( cast ( Diamond ) l e v e l . g e t P i c t u r e ( i , j ) ) // l e v e l . g e t P i c t u r e ( i , j ) i s een Diamond else // l e v e l . g e t P i c t u r e ( i , j ) i s geen Diamond
1 2 3 4
6.7.2.3
Gedeeltelijke imports
In deze applicatie werd dikwijls gebruik gemaakt van de mogelijkheid om slechts een deel van een module uit een bibliotheek te importeren. 1 2
// p i c s . a l l b e v a t a l l e SokobanTypes import p i c s . a l l : Wall , Worker ;
pagina 123
6.7 D
Hoofdstuk 6. GUI Applicatie : Sokoban
// d f l . a l l b e v a t a l l e componenten u i t de DFL b i b l i o t h e e k import d f l . a l l : Panel , PictureBox , PictureBoxSizeMode , Rect ;
3 4
6.7.3
Bibliotheek : DFL
In de standaard bibliotheek van D zit geen ondersteuning voor grafische toepassingen. Je kan vanuit D wel de native systeem bibliotheken aanspreken en op die manier dus ook een GUI maken. Als je geen kennis hebt van die bibliotheken is dit anders geen eenvoudige opgave. Na enig zoeken bleek al snel dat er reeds heel wat bibliotheken geschreven zijn door derden. Die bibliotheek die we gekozen hebben is DFL (D Forms Library). E´en van de belangrijkste redenen daarvoor was de aanwezigheid van een GUI Builder, namelijk Entice Designer. Je kan beiden vinden op Dprogramming4 . Nadeel van deze bibliotheek is wel dat hij alleen voor Windows is geschreven is. Er zijn echter nog andere mogelijkheden die ook voor andere platformen werken. Zo is er net als voor C++ een wxWidgets binding voor D, namelijk wxD5 . Om te reageren op events wordt over het algemeen gebruik gemaakt van delegates. 6.7.3.1
DFL PictureBox
Anders dan bijvoorbeeld in de swing Java bibliotheek (met Icons) kan je met DFL geen Pictures telkens opnieuw tekenen. Een afbeelding tonen gebeurt met een PictureBox. Een PictureBox die op een form wordt geplaatst, geraakt daar echter niet zomaar weg. Het is een Control net zoals bijvoorbeeld Label. Als er hertekend wordt, zal hij dan ook niet overschreven worden. Om de afbeelding te kunnen veranderen moet het image attribuut van de PictureBox veranderd worden. Dit maakt het geheel toch iets complexer dan het systeem met Icons in Java. Een gevolg was bevoorbeeld dat in het PlayPanel wanneer de werker iets verplaatst niet alleen het levelobject moet aangepast worden, maar ook de PictureBox in het Panel. Dubbel werk dus. 6.7.3.2
Reageren op Events
Om te reageren op events in een GUI wordt in de DFL bibliotheek veel gebruik gemaakt van delegates. Aan een event kunnen meerdere delegates toegewezen worden, wanneer de event dan plaatsvindt, zal elke toegewezen delegate opgeroepen worden. Bijvoorbeeld in onze applicatie wordt gereageerd op het klikken van een knop en het selecteren van een item in een lijst. In Sokoban is het volgende te vinden : OpenButton . c l i c k ˜= &t h i s . o p e n B t n c l i c k ; LevelListBox . selectedValueChanged ˜= &t h i s . l i s t S e l e c t i o n c h a n g e d ;
1 2 3
Ook in PlayDialog wordt van delegates gebruik gemaakt om op het indrukken van toetsen te reageren. 4 5
http://www.dprogramming.com/dfl.php http://wxd.sourceforge.net/
pagina 124
6.7 D
1
Hoofdstuk 6. GUI Applicatie : Sokoban
a dd S h o r t c u t ( Keys . LEFT, &k e y P r e s s e d ) ; In de bibliotheek ziet deze methode er als volgt uit :
1 2
public f i n a l void a d d S h o r t c u t ( Keys s h o r t c u t , void delegate ( Object s e n d e r , FormShortcutEventArgs ea ) p r e s s e d ) ;
6.7.4
IDE : Entice Designer
Voor deze applicatie is gebruik gemaakt van een IDE speciaal voor DFL gemaakt. Met de ingebouwde GUI builder is de layout snel gemaakt. De GUI builder is niet zo geavanceerd als die in Netbeans en Visual Studio, maar voor een layout aan te maken toch heel praktisch. Reageren op events en zelf componenten aanmaken zijn bijvoorbeeld dingen die je (nog) niet kan. Zelfgemaakte componenten moet je dus door gepaste programmacode te gebruiken op de juiste locatie in de GUI plaatsen. Bij onze eerder beperkte applicatie was dit geen groot probleem, maar bij meer geavanceerde GUI applicaties kan dit toch heel wat tijd kosten.
pagina 125
Hoofdstuk 7. Batch Applicatie : Filedigger
Hoofdstuk 7
Batch Applicatie : Filedigger 7.1
Inleiding
Met een batch applicatie is er vaak veel input en output gemoeid. Hierdoor heeft een batchapplicatie een lange tijd nodig om zijn taak af te werken. Interactie met de gebruiker is er typisch niet of weinig. De input wordt steeds verwerkt en terug weggeschreven of verstuurd. Dit zorgt voor veel bewerkingen en effici¨entie en snelheid steken hier als vereisten dan ook snel de kop op. De FileDigger is hier dan ook de ideale batchapplicatie. Het laat ons toe te kijken hoe snel de talen zijn, hoe ze kunnen omgaan met input en output en hoe eventuele excepties kunnen opgevangen worden.
7.1.1
Functionaliteit
De mooie naam die deze applicatie heeft meegekregen is gekozen omdat ze bestanden op ´e´en of meerdere opgegeven locaties overloopt. Gaande van ´e´en bestand tot eventueel het hele systeem worden alle directories en bestanden overlopen en geteld. Optioneel is het ook mogelijk om voor tekstbestanden van opgegeven bestandstype woord- en karakterfrequenties te tellen en nog optioneel is het tellen van het aantal lijnen, woorden en karakters. Indien voor sommige bestandstypes aangegeven werd dat lijnen, woorden en karakters en/of woord- en karakterfrequenties geteld moesten worden, wordt dit voor elk bestand van de beschouwde types gedaan. Ook de grootte wordt voor elk gescand bestand aangegeven. Op het einde wordt per locatie het totaal aantal directories en bestanden en hun grootte gegeven. Ook het gemiddeld aantal files per directory wordt berekend. Daarnaast wordt per type het aantal bestanden aangegeven. Indien er ook bestanden gescand werden wordt een totaal resultaat gegeven van de frequenties en het aantal lijnen, woorden en karakters. Als globaal resultaat wordt dan ook nog eens de som gemaakt van alle gegevens van de opgegeven locaties (indien er meer dan ´e´en locatie werd opgegeven).
pagina 126
7.2 C++
7.1.2
Hoofdstuk 7. Batch Applicatie : Filedigger
Specificatie
Het programma wordt gestart vanop de commando-lijn en met de nodige parameters wordt ingesteld wat precies gedaan moet worden. De parameters worden eventueel gevolgd door argumenten. Er is ook telkens een standaard actie voorzien. -p <path> Geeft de te onderzoeken locaties aan en kan gevolgd worden door ´e´en of meerdere paden. De paden kunnen naar een directory of een bestand wijzen. Standaard wordt heel het systeem onderzocht. -o <path> Geeft aan waar de output geschreven moet worden. Het pad moet dus naar een tekstbestand wijzen. Indien het bestand reeds bestaat, wordt het overschreven. Standaard wordt naar de console geschreven. Dit is echter af te raden aangezien er wel eens veel output kan zijn. -wc <extensions> Geeft aan voor welke types bestanden ook het aantal lijnen, woorden en karakters geteld moet worden. Standaard wordt dit enkel voor .txt bestanden gedaan. -wf <extensions> Geeft aan voor welke types bestanden ook de woord- en karakterfrequenties berekend moeten worden. Standaard wordt dit enkel voor .txt bestanden gedaan.
7.2
C++
Voor C++ werd geen FileDigger meer ge¨ımplementeerd. Men zou dit eventueel kunnen doen aan de hand van een iteratie over het filesysteem waar functoren worden meegegeven aan het algoritme. Deze functoren zijn objecten die als gewone functies uitgevoerd kunnen worden. Dit heeft als voordeel dat ze ook context kunnen bijhouden. Zo zouden deze functoren hier kunnen instaan voor het scannen van bestanden en het bijhouden van statistieken zoals woordfrequenties, karakterfrequenties, aantal woorden, lijnen en karakters, ...
7.3 7.3.1
C# Ontwerp
Het hoofdprogramma zal eerst en vooral de argumenten inlezen. Deze argumenten worden opgeslagen in een DiggerOptions object. Het bevat de nodige instellingen voor de FileDigger. Indien dit inlezen succesvol was, zal een FileDigger worden aangemaakt met het DiggerOptions object als parameter. De FileDigger kan dan worden gestart en hij zal hierbij de lijst met paden overlopen. Elk pad zal verder worden onderzocht door middel van een recursief algoritme. Afhankelijk van de opties zullen FileAnalyzer s gebruikt worden om files te scannen en onderzoeken. De statistieken vervat in een FileAnalyzerInfo object worden dan bij een FileDiggerInfo object gemerged. Aan het einde zal de array van FileDiggerInfo’s worden uitgeprint en gemerged om tot 1 resultaat van de verschillende paden te komen.
pagina 127
7.3 C#
7.3.2
Hoofdstuk 7. Batch Applicatie : Filedigger
De taal
Zowel versie 2.0 als 3.0 werden gebruikt. Met C# 3.0 werden enkel de grootste nieuwigheden eens uitgetest. Er werd niet veel verder op ingegaan. Hoofdzakelijk werd dus C# 2.0 gebruikt. Alle code van C# 2.0 is trouwens ook nog geldige code in C# 3.0 . 7.3.2.1
Properties
Ook properties werden intensief gebruikt. Ze zorgen zoals uitgelegd in 3.2.4.1 voor een eenvoudig gebruik doordat ze er als membervariabelen uitzien en zorgen tevens voor encapsulatie. Een nadeel is de minder leesbare code, daar men soms niet goed weet of het nu een echte publieke variabele is of niet. Men kan er namelijk niet zomaar vanuit gaan dat iedereen zich mooi aan de objectgeori¨enteerde regels houdt. Voorzichtigheid is geboden. In Java kan men het concept properties ook toepassen maar dan via get en set methoden. Men zou kunnen besluiten dat de properties zoals ze in C# 2.0 voorkomen niet meer dan syntactic sugar zijn. In C# 3.0 hebben ze wel een meerwaarde. Hier hoef je geen private members meer te declareren. De properties zijn voldoende en de compiler doet de rest. Waarom dan niet enkel de members declareren en geen properties denk je misschien. Wel, de properties laten ons nog toe aan te duiden of de membervariabelen bv. readonly of writeonly zijn door het weglaten van een set of get uit de declaratie. Ze kunnen ook een andere implementatie voorzien. Door het gebruik van deze automatische properties konden we het aantal regels code drastisch verminderen. Sommige klassen werden uiteindelijk half zo groot als de 2.0 variant. Dit zorgt natuurlijk voor heel wat minder typwerk en dus een grotere productiviteit. Ook de nieuwe manier van initialiseren (zie sectie 2.2.6.3) zorgt voor beduidend minder code, daar het niet meer nodig is om bij wijze van spreken 300 verschillende constructoren te typen. 7.3.2.2
Extensions
Extensions bleken hier welkom voor het mergen van twee Hashtables. Deze methode kent geen gelijke in de Hashtable klasse. Door de extensiemethode kan men dan wel op een Hashtable gebruik maken van de Merge. 1 2 3 4 5 6 7 8 9
public s t a t i c H a s h t a b l e Merge ( t h i s H a s h t a b l e ht , H a s h t a b l e ht1 ) { foreach ( D i c t i o n a r y E n t r y de in ht1 ) { i f ( ht . C o n t a i n s ( de . Key ) ) ht [ de . Key ] = ( int ) ht [ de . Key ] + ( int ) de . Value ; e l s e ht . Add ( de . Key , de . Value ) ; } return ht ; }
10 11
public s t a t i c void Main ( S t r i n g [ ] a r g s ) {
pagina 128
7.3 C#
H a s h t a b l e ht1 = new H a s h t a b l e ( ) ; H a s h t a b l e ht2 = new H a s h t a b l e ( ) ; ht1 . Merge ( ht2 ) ;
12 13 14 15
Hoofdstuk 7. Batch Applicatie : Filedigger
} Extensions hebben wel geen voorrang op de gewone methoden van een klasse. Indien de methode niet in de klasse gevonden wordt, dan pas zal er naar extensions worden gezocht. 7.3.2.3
Strings splitten
Voor het splitten van strings kan handig gebruikt worden gemaakt van de Split methode uit de String klasse. Strings behoren in C# tot de taal. In C++ is dit niet zo en maken strings onderdeel uit van de bibliotheek. 1 2 3 4 5 6 7
S t r i n g l i n e = ” Mijn l i j n met t e k s t e r o p ” ; char [ ] s p l i t t e r s = new char [ ] { ’ ’ , ’ , ’ , ’ . ’ , ’ ? ’ , ’ ; ’ , ’ ∗ ’ , ’= ’ , ’+ ’ , ’ { ’ , ’ } ’ , ’\n ’ , ’\ f ’ , ’\ r ’ , ’\\ ’ , ’/ ’ , ’( ’ , ’) ’ , ’| ’}; String [ ] tokens = l i n e . S p l i t ( s p l i t t e r s ) ;
7.3.3
’! ’ ,
’ : ’ , ’\ t ’ ,
IDE: Microsoft Visual Studio
Tijdens het ontwikkelen van de applicaties werd er gebruikt gemaakt van twee verschillende versies IDE’s. Deze versies waren de Visual Studio 2005 voor het ontwikkelen van de C# 2.0 versie en een beta-versie van Visual Studio 9.0 (Orcas) voor de C# 3.0 versie. Visual Studio 9.0 is een beta-versie en kwam daardoor ook nog met de nodige bugs. Zo was het onder andere nodig om een specifieke gebruiker (Creator Owner) aan het Windows XP besturingssysteem toe te voegen alvorens de installatie van de IDE zou kunnen uitvoeren. Verder waren nog enkele problemen met het weergeven van eigenschappen van de applicaties. Hieronder verstaan we de debug -en deploymentopties, alsook het weergeven van resources en andere zaken [106]. Een vaak gebruikte tool van VS was de batch build. Hiermee kan je het buildproces automatiseren. Je kan aanduiden welke projecten je wil builden, in welke modus en in welke volgorde indien er afhankelijkheden tussen de verschillende projecten zijn. Dit is vaak het geval met het gebruik van bibliotheken. In de nieuwe VS bevinden zich ook zaken als codeanalyse, statistieken van de code (code metrics), unit tests. Je hoeft dus geen tools meer te installeren van derden. De programmeur krijgt het weer wat makkelijker door deze uitbreidingen. De code analyse geeft tevens meldingen met betrekking tot naamconventies in C# (naming), mogelijke ontwerpfouten of hints om het beter te doen (design), het gebruik van variabelen (usage), beter gebruik van algemene trends (globalization), performantieanalyse (performance), ... De code metrieken geven een aantal resultaten
pagina 129
7.3 C#
Hoofdstuk 7. Batch Applicatie : Filedigger
met betrekking tot onderhoudbaarheid van de code, aantal lijnen code, diepte van overerving, ...
7.3.4
.NET bibliotheek
Overlopen van de directorystructuur Voor het werken met bestanden en mappen bestaan er in .NET enkele interessante klassen zoals DirectoryInfo en FileInfo. 1 2 3 4 5 6 7
D i r e c t o r y I n f o d i 1 ; // i n f o o v e r een map F i l e I n f o f i 1 ; // i n f o o v e r een b e s t a n d // Als de map b e s t a a t , v r a a g a l l e mappen en b e s t a n d e n op i f ( d i 1 = new D i r e c t o r y I n f o ( c u r r e n t P a t h ) ) . E x i s t s == true ) { DirectoryInfo [ ] dia = di1 . GetDirectories ( ) ; F i l e I n f o [ ] f i a = di1 . GetFiles ( ) ; }
Generics Generics hielpen ons hier uit de nood voor het sorteren van de hashtable op de values en niet op de keys. 1 2 3 4 5 6 7 8 9 10 11
public c l a s s MyExtensions { // G e n e r i e k e methode om de h a s h t a b e l t e s o r t e r e n op z i j n waarden // en daarna v oor g e l i j k e waarden nog e e n s a l f a b e t i s c h op de s l e u t e l s public s t a t i c void SortByValue ( t h i s H a s h t a b l e ht , T [ ] keys , int [ ] v a l u e s ) { ht . Keys . CopyTo ( keys , 0 ) ; ht . Values . CopyTo ( v a l u e s , 0 ) ; // s o r t e e r t de v a l u e s a r r a y en z o r g t e r voo r // d a t de k e y s ook m e e g e s o r t e e r d worden en de paren // dus behouden b l i j v e n Array . S o r t ( v a l u e s , k e y s ) ;
12
ReverseComparer r e v e r s e C o m p a r e r = new ReverseComparer ( ) ; int s t a r t I n d e x = 0 ; f o r ( int i = 0 ; i < k e y s . Length − 1 ; i ++) i f ( v a l u e s [ i ] != v a l u e s [ i + 1 ] ) { Array . S o r t ( keys , v a l u e s , s t a r t I n d e x , i − s t a r t I n d e x + 1 , reverseComparer ) ; startIndex = i + 1; } Array . S o r t ( keys , v a l u e s , s t a r t I n d e x , k e y s . Length − s t a r t I n d e x , r e v e r s e C o m p a r e r ) ;
13 14 15 16 17 18 19 20 21 22 23
}
pagina 130
7.4 Java
24
Hoofdstuk 7. Batch Applicatie : Filedigger
}
25 26 27 28 29 30 31
// K l a s s e d i e een omgekeerde comparer v o o r s t e l t . public c l a s s ReverseComparer : IComparer { public int Compare ( Object o b j e c t 1 , Object o b j e c t 2 ) { return −(( IComparable ) o b j e c t 1 ) . CompareTo ( o b j e c t 2 ) ; } }
7.4 7.4.1
Java Ontwerp
Er zijn twee klassen die info objecten voorstellen. FileAnalyzerInfo bevat de statistieken van ´e´en bestand. Naast de bestandsnaam en -grootte worden daar het aantal woorden, lijnen, karakters en de frequentietabellen opgeslagen naargelang wat de gebruiker opgevraagd heeft. DiggerInfo bevat de statistieken van een bepaalde locatie. Naast het totaal aantal bestanden en mappen wordt ook de som bijgehouden van alle data uit de FileAnalyzerInfo objecten van onderliggende files. Twee klassen zorgen voor het eigenlijke zoekwerk. FileAnalyzer onderzoekt een bestand en slaat de vergaarde info op in een FileAnalyzerInfo object. Het eventuele zoeken is hardgecodeerd in het Digger object. Digger onderzoekt ´e´en of meerdere locaties en slaat op zijn beurt de vergaarde info op in een DiggerInfo object. Het DiggerOptions object houdt de instellingen bij die door de gebruiker werden opgegeven. E´en instantie van dit object wordt in het begin van de uitvoering aangemaakt en doorgegeven waar nodig. Er kan aan gevraagd worden welke locaties moeten gescand worden, of een file moet bekeken worden en naar waar de uitvoer geschreven moet worden. ExtensionsFilter stelt een object voor dat specifieke bestanden kan filteren aan de hand van hun extensie.
7.4.2 7.4.2.1
De taal Generics
Om de frequentietabellen van karakters, woorden en extensies te sorteren werd een hulpfunctie geschreven die 2 Maps sorteert en de gesorteerde entries (key,value paren) in een lijst teruggeeft. Er wordt gesorteerd op basis van de values en bij gelijke values wordt gesorteerd op basis van de keys. De maps worden pas gesorteerd vlak voor het uitschrijven. Een map sorteren op basis van zijn keys is eenvoudig in Java (een TreeMap doet dat zelfs automatisch), maar om te sorteren op basis van de values moet je meer doen. We hebben een generieke methode ge¨ımplementeerd die elke map, met als keys en values objecten die de Comparable interface implementeren, kan sorteren. Het is dan ook een mooi voorbeeld van de
pagina 131
7.4 Java
Hoofdstuk 7. Batch Applicatie : Filedigger
mogelijkheden die Java Generics aanbieden. Dit voorbeeld toont echter ook meteen aan dat het gebruik van generics in Java zorgt voor niet eenvoudig te begrijpen programmeercode. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
s t a t i c , V extends Comparable > A r r a y L i s t <Entry> sortMapByValue (Map map) { A r r a y L i s t <Entry> e n t r i e s = new A r r a y L i s t (map . e n t r y S e t ( ) ) ; C o l l e c t i o n s . s o r t ( e n t r i e s , new Comparator ( ) { public int compare ( Object o1 , Object o2 ) { int r e s u l t = ( (V) ( ( Map . Entry ) o2 ) . g e t V a l u e ( ) ) . compareTo ( ( (V) ( ( Map . Entry ) o1 ) . g e t V a l u e ( ) ) ) ; i f ( r e s u l t == 0 ) { r e s u l t = −((K) ( ( Map . Entry ) o2 ) . getKey ( ) ) . compareTo ( (K) ( ( Map . Entry ) o1 ) . getKey ( ) ) ; } return r e s u l t ; } }); return e n t r i e s ; }
7.4.2.2
JUnitTests
Om de correctheid van de ExtensionsFilter en de DiggerOptions te testen werden via netbeans unittests aangemaakt. Je krijgt automatisch het skelet van de testklasse. Het is dan enkel nog zaak de methoden zo te implementeren dat de functionaliteit van de te testen klasse correct wordt getest. 7.4.2.3
Java Heap Space
De Java Virtuele machine krijgt standaard maar een beperkte hoeveelheid werkgeheugen ter beschikking. Als we met onze applicaties echter grote hoeveelheden tekstbestanden scannen en bijgevolg heel grote hashmaps moeten cre¨eren kan het gebeuren dat Java te weinig geheugen heeft. Een Java OutOfMemoryError wordt gegenereerd en het programma wordt afgesloten. Dit kan echter tot op zekere hoogte opgelost worden door bij het uitvoeren meer geheugen te alloceren. Dit kan door de extra parameter -Xmx<size> toe te voegen.
7.4.3
Bibliotheek
Voor deze applicatie wordt logischerwijs veel gebruik gemaakt van de IO mogelijkheden in de Java bibliotheken. Het packet dat die functionaliteit bevat is java.io. Naast klassen om in te lezen en uit te schrijven (zoals bijvoorbeeld FileReader, PrintWriter, ...) wordt de klasse File gebruikt als abstracte voorstelling van een pad naar een bestand of een map.
pagina 132
7.5 D
Hoofdstuk 7. Batch Applicatie : Filedigger
Om de statistieken bij te houden van de onderzochte bestanden en mappen wordt intensief gebruik gemaakt van het Collections framework uit de Java standaard bibliotheek. Vooral de HashMap wordt gebruikt (voor de vele frequentietabellen). Om een bestand te lezen en de woorden eruit te parsen werd gebruik gemaakt van de klasse Scanner. Dit is een klasse die pas sinds Java 5 aan het java.util packet werd toegevoegd. Het is een eenvoudige string parser die gebruik kan maken van reguliere expressies. 7.4.3.1
Partities van het systeem
Zoals in de specificatie vermeld, worden alle partities van het systeem overlopen als er geen paden opgegeven werden door de gebruiker. De vraag was dan nog hoe deze partities aan het systeem opgevraagd konden worden. De Java standaard bibliotheek voorziet hier gelukkig een methode voor. 1
File [ ] roots = File . listRoots ( ) ;
7.5 7.5.1
D Ontwerp
Er werd een gelijkaardige structuur gebruikt als in de Java-implementatie, maar doordat we hier niet aan het systeem met classes gebonden zijn konden we de componenten met een gelijkaardige functionalteit samen in een module steken. Er zijn twee hoofdmodules. De module FileAnalyzer bevat het FileAnalyzerInfo object als een class en daarnaast ook de methode om een file te analyseren die een FileAnalyzerInfo als resultaat teruggeeft. De andere belangrijke module is Digger. Deze bevat naast de methode om een locatie hi¨erarchisch te scannen ook het DiggerInfoObject dat gebruikt wordt om het resultaat van de scanmethode voor te stellen. Daarnaast is er nog de DiggerOptions klasse die op een gelijkaardige manier als in Java de voorkeuren bijhoudt. De ExtensionsFilter werd ge¨ımplementeerd aan de hand van reguliere expressies.
7.5.2 7.5.2.1
De taal Unittests
Om de correctheid van de ExtensionsFilter en de DiggerOptions te testen werd gebruik gemaakt van enkele unittests zoals beschreven in 3.4.4.1. Er werd een instantie van de klasse aangemaakt en de correctheid van alle methoden werd nagegaan. Door altijd te compileren met de -unittest parameter werden de testen telkens aangemaakt en uitgevoerd voor het programma zelf kon starten. De testcode staat in dezelfde module als de te testen code.
pagina 133
7.5 D
7.5.2.2
Hoofdstuk 7. Batch Applicatie : Filedigger
Output
Volgens de specificatie kan een gebruiker normaal gezien opgeven of hij de resultaten naar bestand dan wel naar standaarduitvoer wil schrijven. In de implementatie van D wordt echter enkel naar de standaarduitvoer geschreven. In de toekomst zou dit nog kunnen aangepast worden. 7.5.2.3
Frequentietabellen
Voor de frequentietabellen werd uitvoerig gebruikt gemaakt van de associatieve arrays die in D mogelijk zijn. Op dit moment wordt enkel gesorteerd op de keys voor het uitschrijven. Met templates moet het echter ook mogelijk zijn ze te sorteren op han values. Associatieve arrays zijn eenvoudiger in gebruik dan de Maps uit de Java Collections bibliotheek. Zo zijn onderstaande D en Java fragmenten equivalent. Ze voegen een woord toe aan een frequentietabel. Als het woord al aanwezig is wordt de value met 1 verhoogd, anders wordt het woord als key toegevoeg met value 1. 1 2 3 4
1 2 3 4 5 6
int [ char [ ] ] wordFreqs ; void addWord ( char [ ] word ) { wordFreqs [ word ]++; } private Map<S t r i n g , I n t e g e r > wordFreqs ; public void addWord ( S t r i n g word ) { i f ( wordFreqs . c o n t a i n s K e y ( word ) ) wordFreqs . put ( word , wordFreqs . g e t ( word ) + 1 ) ; e l s e wordFreqs . put ( word , 1 ) ; }
7.5.3
Bibliotheek
Er werd gebruikt van de standaard bibliotheken voor omgang met files (std.file), behandeling van paden (std.path), gebruik van streams (std.stream), reguliere expressies (std.regexp). Vooral in std.file werd een heel praktische methode gevonden die gebruikt werd voor het hi¨erarchisch overlopen van de mappen en bestanden. 7.5.3.1
Paden hi¨ erarchisch scannen
In de std.file zit een struct DirEntry die info bevat over een locatie, het kan zowel een bestand als een map zijn. In combinatie met onderstaande bibliotheek methode werd de recursieve zoekmethode geschreven. De listdir methode stuurt elk bestand en map (beschouwd als DirEntry) in pathname (pad naar een bestand of map), naar de callback delegate. 1
// de methode u i t s t d . f i l e
pagina 134
7.5 D
2 3 4 5 6 7 8 9 10 11
Hoofdstuk 7. Batch Applicatie : Filedigger
void l i s t d i r ( char [ ] pathname , b o o l d e l e g a t e ( DirEntry ∗ de ) c a l l b a c k ) ; // g e b r u i k voor h i e r a r c h i s c h scannen b o o l c a l l b a c k ( DirEntry ∗ de ) { i f ( de . i s d i r ) // e v a l u e e r map en b e k i j k inhoud map l i s t d i r ( de . name , &c a l l b a c k ) ; else // e v a l u e e r b e s t a n d return true ; }
pagina 135
Hoofdstuk 8. Netwerk Applicatie : Chatroom
Hoofdstuk 8
Netwerk Applicatie : Chatroom 8.1
Inleiding
Vandaag de dag zijn netwerkapplicaties niet meer weg te denken uit de computermaatschappij. Veel computers verrichten vandaag meer werk over een netwerk - dit kan een lokaal netwerk zijn of het internet - dan lokaal op de computer zelf. Het doel van deze applicatie is te onderzoeken of en hoe de verschillende talen overweg kunnen met sockets en threads.
8.1.1
Functionaliteit
We hebben ervoor gekozen een chatroom te maken. Zo een chatroom is vooral gekend draaiend in een browser maar daarnaast bestaan er ook vele stand-alone applicaties. Wij hebben voor een stand-alone applicatie gekozen met een eenvoudige GUI die naast een tekstvak dat het gesprek weergeeft en een tekstveld waar de lokale gebruiker zijn tekst ingeeft ook een overzicht geeft van de gebruikers die ’online’ zijn. Een gebruiker kan zich bovendien eenvoudig aan- of afmelden.
8.1.2
Specificatie
De Java server applicatie moet gestart worden vanop de commando-lijn met als parameter het poortnummer waar de server clients wil ontvangen. Ook de client wordt gestart vanop de commando-lijn. Hier worden als parameters het IP-adres en het poortnummer van de server meegegeven. De C# server wordt gestart met een uitvoerbaar bestand, net als de client. Eens de client gestart is kan het IP-adres en poortnummer van de server opgegeven worden.
8.2
C++
Een chatserver voor C++ werd niet meer ge¨ımplementeerd. Threading is niet voorzien in C++. Het kan echter wel door gebruik van externe bibliotheken. Een voorbeeld hiervan is de Boost bibliotheek 1 . De client-server communicatie kan ge¨ımplementeerd worden door gebruik 1
http://www.boost.org/
pagina 136
8.3 C#
Hoofdstuk 8. Netwerk Applicatie : Chatroom
te maken van sockets.
8.3 8.3.1
C# Ontwerp
Er werden drie projecten gemaakt voor de chatapplicatie. Een eerste was een bibliotheek ChatLib die enkele gemeenschappelijke klassen bevat die zowel door de client als server gebruikt worden. Een tweede de ChatServer. Als laatste de ChatClient. Er werd voor de communicatie gebruik gemaakt van TCP. Threading werd niet expliciet toegepast. Er werd gebruik gemaakt van asynchrone callbacks. Men moet bij het opstarten van de client eerst inloggen en verbinding maken met de server (via ip en poortnr). Daarna kan men chatten via de client. De boodschappen worden naar de server doorgestuurd aan de hand van Commands ge¨ımplementeerd via een enum. De server zal afhankelijk van het commando verschillende zaken uitvoeren. Zoals het verwittigen van alle clients indien er iemand uitlogt of inlogt. Het ontwerp is hier echter niet ideaal. Indien men nog een commando wenst toe te voegen dient men in de broncode aanpassingen te verrichten en dit zowel aan de client als server zijde. Beter zou het gebruik van het command design pattern zijn. We zouden hier een object per commando hebben, waarbij de code die uitgevoerd dient te worden zich in dit object bevindt. De client en server nemen gewoon zo een commando object aan of versturen dit en hoeven niet te weten wat precies het commando is om de juiste code uit te voeren.
8.3.2 8.3.2.1
De taal Struct
Voor de clientlijst op de server werd een struct geconstrueerd. Dit struct ClientInfo stelt een client voor en bevat zijn naam en een Socket object voor de verbinding. Telkens een client inlogt wordt er een instantie van ClientInfo aan de clientlijst van de server toegevoegd. 1 2 3 4
struct C l i e n t I n f o { public S o c k e t s o c k e t ; public string strName ; }
// de c l i e n t s o c k e t // de g e b r u i k e r s n a a m
Het ware beter om te werken met een generische lijst van ClientInfo’s. Dit werd tijdens de implementatie nog over het hoofd gezien.
8.3.3 8.3.3.1
.NET bibliotheek AsyncCallback
Hieronder kan je een deel van de servercode bekijken. De TCP asynchrone sockets hebben een Begin en End methode zoals BeginAccept en EndAccept. Deze zien er zo uit:
pagina 137
8.3 C#
1
Hoofdstuk 8. Netwerk Applicatie : Chatroom
I A s y n c R e s u l t BeginAccept ( AsyncCallback , object s t a t e ) ; We maken bij deze toepassing ook gebruik van asynchrone callbacks. De AsyncCallback is een delegate die wijst naar een methode die uitgevoerd dient te worden wanneer de asynchrone operatie gedaan is. In dit geval is dit de OnAccept methode (zie lijn 16). In de OnAccept methode staat er een parameter IAsyncResult. Dit is het resultaat van de asynchrone operatie. De EndAccept methode zorgt er voor dat de clientSocket afgegeven wordt. Daarna kunnen we opnieuw gaan luisteren naar inkomende requests. Verder zullen we met BeginReceive data van de client kunnen ontvangen. De clientconnectie wordt dan ook als parameter meegegeven. De AsyncCallback met OnReceive zal de clientconnectie dan ook uit de AsyncState property van IAsyncResult kunnen halen [6].
1 2 3 4 5 6 7
... private void Form1 Load ( object s e n d e r , EventArgs e ) { try { // We g e b r u i k e n TCP s o c k e t s s e r v e r S o c k e t = new S o c k e t ( AddressFamily . InterNetwork , SocketType . Stream , ProtocolType . Tcp ) ;
8
IPEndPoint ipEndPoint = new IPEndPoint ( IPAddress . Any , 3 0 0 0 ) ;
9 10
// Bind aan h e t i p en l u i s t e r s e r v e r S o c k e t . Bind ( ipEndPoint ) ; serverSocket . Listen ( 4);
11 12 13 14
// A c c e p t e e r inkomende c l i e n t s s e r v e r S o c k e t . BeginAccept (new AsyncCallback ( OnAccept ) , null ) ; } catch ( E x c e p t i o n ex ) { MessageBox . Show ( ex . Message , P r o p e r t i e s . R e s o u r c e s . C h a t s e r v e r t c p , MessageBoxButtons .OK, MessageBoxIcon . E r r o r ) ; }
15 16 17 18 19 20 21
}
22 23 24 25
private void OnAccept ( I A s y n c R e s u l t a r ) { try { S o c k e t c l i e n t S o c k e t = s e r v e r S o c k e t . EndAccept ( a r ) ;
26 27 28
// L u i s t e r v oor meer c l i e n t s s e r v e r S o c k e t . BeginAccept (new AsyncCallback ( OnAccept ) , null ) ;
29 30
// Als c l i e n t g e c o n n e c t e e r d , l u i s t e r voor commando ’ s
pagina 138
8.4 Java
c l i e n t S o c k e t . B e g i n R e c e i v e ( byteData , 0 , byteData . Length , S o c k e t F l a g s . None , new AsyncCallback ( OnReceive ) , c l i e n t S o c k e t ) ; } catch ( E x c e p t i o n ex ) { MessageBox . Show ( ex . Message , P r o p e r t i e s . R e s o u r c e s . C h a t s e r v e r t c p , MessageBoxButtons .OK, MessageBoxIcon . E r r o r ) ; }
31 32 33 34 35 36 37 38
Hoofdstuk 8. Netwerk Applicatie : Chatroom
}
8.3.3.2
.NET Remoting
.NET bevat ook nog een andere technologie die .NET Remoting heet. Het is een technologie voor communicatie tussen verschillende applicatiedomeinen. Om een applicatie te maken die over het netwerk een client met een server verbindt kan men zoals hierboven zijn toevlucht zoeken in het gebruik van sockets. Dit is echter soms omslachtig doordat we zelf data dienen door te zenden en in moeten staan voor de eventuele creatie threads. Via .NET Remoting kan men beter doen. Men maakt hierbij gebruik van channels zoals HttpChannel, TcpChannel en IpcChannel. .NET Remoting zorgt als het ware voor een abstractie van de anders omslachtige onderliggende communicatie. De technologie werd hier echter niet in praktijk omgezet of nader bestudeerd.
8.4 8.4.1
Java Ontwerp
Via netbeans werden twee projecten aangemaakt, ´e´en voor de server en ´e´en voor de client. De client zijde bestaat enkel uit een klasse ChatClient die de Runnable interface implementeert. Er wordt een thread gestart die connectie maakt met een server waarvan het IP-adres en het poortnummer gekend zijn. Tevens wordt een inputstream aangemaakt om tekst die van de server komt op te vangen en een outputstream om tekst naar de server te sturen. Bij het uitloggen worden deze streams en daarna de socket gesloten. De server zijde bestaat uit de klasse ChatServer en ClientConnection. De ChatServer is ook een klasse die de Runnable interface implementeert. Er wordt een ServerSocket aangemaakt en daarna wordt op inkomende client aanvragen gewacht in een oneindige lus. Er wordt op de server een Vector van ingelogde ClientConnections bijgehouden. We gebruiken de Vector collectie omdat ze gesynchroniseerd is. De ChatClient klasse houdt voor een client de socket bij waarnaar geschreven moet worden. Deze klasse implementeert ook de Runnable interface. Eens een client ingelogd, wordt hij gestart door een Thread aan te maken en deze te starten. Elke boodschap die van de client komt, wordt via de server naar alle clients gestuurd (gemulticast).
pagina 139
8.5 D
8.4.2
Hoofdstuk 8. Netwerk Applicatie : Chatroom
Bibliotheek
Om in Java met threads te werken zijn er twee basismogelijkheden. Ofwel breid je de klasse Thread uit en overschrijf je de methode run (die standaard geen implementatie bevat), ofwel implementeer je de interface Runnable en zijn enige methode run. Als je de klasse Thread niet fundamenteel wijzigt en dus enkel de methode run zou overschrijven kan je beter voor de interface Runnable kiezen. Dat voldoet meer aan de principes van object-ori¨entatie die zeggen dat je een klasse mag overschrijven als je een stuk functionaliteit wilt toevoegen of overschrijven. Door de enkelvoudige overerving in Java verlies je door de klasse Thread uit te breiden ook de mogelijkheid om nog een andere klasse uit te breiden. Om een Thread aan te maken met een Runnable object geef je het object gewoon mee aan een Thread constructor. De Thread klasse en Runnable interface bevinden zich in het java.lang packet van de Java standaard bibliotheek. Het belangrijkste packet voor netwerkapplicaties is java.net. De belangrijkste gebruikte klassen hieruit zijn Socket (die een client socket voorstelt) en ServerSocket (die een server socket voorstelt). 8.4.2.1
ObjectOutputStream - ObjectInputStream
Op dit moment wordt geen lijst van online gebruikers weergegeven. Dit is omdat via gewone streams enkel data gelezen kan worden (bytes, karakters, ...). Om het mogelijk te maken de lijst van gebruikers door te sturen zou er gebruik gemaakt kunnen worden van de ObjectInputStream klasse in zijn output equivalent. Deze worden gebruikt om objecten te serializeren. Deze serialisatie kan zowel over het netwerk als naar een bestand.
8.5
D
Ook D heeft mogelijkheden om netwerkapplicaties te schrijven. In de standaardbibliotheek zijn daarvoor een aantal klassen (bijvoorbeeld std.socket, std.socketstream en std.thread) met de nodige functionaliteit voorzien. De implementatie van deze applicatie viel jammer genoeg buiten het tijdsbestek.
pagina 140
Hoofdstuk 9. Besluiten
Hoofdstuk 9
Besluiten A language that doesn’t have everything is actually easier to program in than some that do. Dennis M. Ritchie
9.1
Algemene beschouwing van de talen
Hieronder proberen we in het kort even onze algemene beschouwing van de taal te formuleren.
9.1.1
C++
Bij het ontwikkelen van de applicaties werd al snel duidelijk waarom C++ een general purpose taal genoemd wordt. C++ wenst een programmeertaal te zijn die voor allerlei applicaties gebruikt kan worden. Hierdoor dient ze redelijk algemeen en eenvoudig te blijven. Door die eenvoud ontbreken natuurlijk tal van zaken die men ofwel zelf zou moeten voorzien of die men in een externe bibliotheek dient te zoeken. Dit laatste doet zich dan ook regelmatig voor. Het gebruik van externe bibliotheken zorgt er natuurlijk voor dat men de portabiliteit van de broncode enigszins in gedrang brengt. Verder laat C++ de gebruiker heel veel vrijheid. Dit is zowel haar grootste kracht en grootste struikelblok. Zoals Andrew Cooke het in [20] zei: If C++ were a car, it would be a Model T Ford with a new engine, straight from a Formula 1 car, but without the brakes, suspension or bodywork to match. No wonder it comes off the road at the first bend.
9.1.2
C#
Men kan C# het beste situeren tussen Java en C++. De taal probeert van beide kanten het beste mee te nemen. Door deze nauwe samenhang was het dan ook betrekkelijk eenvoudig om van de Java-syntax over te stappen naar C# en dan naar een C++ syntax. De C# syntax is uitgebreid. Overdaad schaadt zegt men soms, hier is dit misschien nog net niet het geval. Het scheelt alleszins niet veel. C# laat toe om sommige problemen op heel wat verschillende manieren aan te pakken en te nuanceren. Dit vloeit waarschijnlijk voort uit de ambitie om
pagina 141
9.2 De talen
Hoofdstuk 9. Besluiten
zowel voor de Java als C++ gebruikers goed te willen doen. Het zorgt wel voor een grotere expressiviteit en de mogelijkheid om specifieker te zijn, maar het maakt de programma’s en onderlinge verbanden vaak ook moeilijker begrijpbaar. Al bij al is het een productieve en eenvoudig te leren taal. De kracht van C# bevindt zich echter niet alleen in de taal zelf, maar behoort ook vooral toe aan de .NET bibliotheek die toch wel zeer uitgebreid is.
9.1.3
Java
Java kan men kort omschrijven als een eenvoudige en gebruiksvriendelijke taal. Zo voelt het alleszins aan bij ons, maar dat zal misschien deels zijn omdat we ermee opgeleid zijn. Ze is niet de meest performante en ook niet de meest productieve taal, maar wel een goede middelmaat. Ze heeft verder een goede ondersteuning. Dankzij zijn applet systeem is ze indertijd populair geworden, maar intussen is Java competitief in vele domeinen.
9.1.4
D
De programmeertaal D kwam ons naar voor als een veelbelovende taal met veel goede en interessante features. De taal is sterk gebaseerd op C++, maar probeert van het onsamenhangend geheel dat C++ is geworden weer een samenhangend geheel te maken. D probeert op vlak van robuustheid, memory management en tal van andere zaken beter te doen zonder hierbij in te moeten boeten aan effici¨entie en performantie. Het feit is dat D als programmeertaal nog in haar kinderschoenen staat. De taal is nog niet stabiel genoeg en dient nog te groeien. Vooral op vlak van ondersteuning is er ook nog werk aan de winkel. Dit komt grotendeels daar D (nog) geen commerci¨le ondersteuning heeft.
9.2 9.2.1
De talen Geheugenbeheer en garbage collection
Vooral bij het testen van de batch applicatie viel op dat Java problemen had met geheugenmanagement. Te weinig geheugen was beschikbaar met foutmelding als gevolg. De heapspace van Java diende dan ook te worden vergroot. C# had voor dezelfde taak geen problemen met het geheugen. In C++ is het eenvoudiger om effici¨ente code te schrijven dan in C#. Met effici¨ente code bedoelen we hier code die snel kan uitvoeren en waarbij we weten wanneer objecten worden opgekuist. Wanneer we in C++ objecten opkuisen, zijn ze dan ook weg. In C# is dit moeilijker om een deterministische opkuis van objecten te bekomen. In D kan je de garbage collector zijn werk laten doen, maar je kan ook zelf het geheugen onderhouden. In onze applicaties werd het werk aan de garbage collector gelaten, wellicht door de niet noodzakelijk goede gewoonte bij Java.
pagina 142
9.3 Ondersteuning
9.2.2
Hoofdstuk 9. Besluiten
Basisfunctionaliteit
Bij de GUI applicatie werd veel van events gebruik gemaakt. Closures werden hier echter niet gebruikt. Dit was ook niet echt nodig doordat we reeds genoeg informatie meekregen. Closures hebben zeker hun nut, maar waarschijnlijk eerder voor specifieke toepassingen die meer context vereisen. Met meer context bedoelen we hier meer dan wat we nu meekrijgen.
9.2.3 9.2.3.1
OO Parti¨ ele klassen
Opmerkelijk is de mogelijkheid tot gebruik van parti¨ele klassen in C#. Ze bieden ons de mogelijkheid om de broncode van een klasse te scheiden over meerdere bestanden. Dit bleek vooral handig bij het GUI ontwerp waarbij de GUI specifieke code gescheiden werd van de code die de echte functionaliteit. Andere talen hebben dit niet.
9.2.4
Generiek programmeren
Van generiek programmeren werd hier weinig gemaakt. Wel zijn enkele generieke methoden ontwikkeld om Hashtabellen te sorteren op de values in plaats van op de keys.
9.3 9.3.1
Ondersteuning Bibliotheken
In de categorie bibliotheken is C# met de .NET bibliotheek toch wel de uitblinker. Je kan gebruik maken van zeer uitgebreide API die gemeenschappelijk is voor de door .NET ondersteunde talen. In C++ diende men vaak op zoek te gaan naar een gepaste bibliotheek door derden geschreven. Dit bleek niet altijd even handig. Het gebruik van verschillende externe bibliotheken komt ook de portabiliteit van de code niet ten goede. Java vormt hier de middelmaat met een goede en duidelijke bibliotheek. Phobos, de standaardbibliotheek van D is niet zo uitgebreid. Er bestaan ook reeds een aantal goede externe bibliotheken, maar deze zijn net als D zelf nog niet zo heel stabiel en ten volle betrouwbaar. Voor de GUI applicatie in C++ werd gebruik gemaakt van de wxWidgets bibliotheek. Het gebruik ervan was heel eenvoudig. Ook de documentatie was voldoende.
9.3.2
Documentatie
Voor .NET en C# is er heel uitgebreide documentatie beschikbaar. De veelheid aan informatie zorgt er wel voor dat je er soms lang over doet om te vinden wat je zoekt. Dit is niet zo in de Java API. Voor C++ en D kan de documentatie heel wat beter. Vaak heeft men in .NET ook veel verschillende mogelijkheden om een hetzelfde resultaat te bereiken. Ook dit maakt het geheel complexer. Less is more?
pagina 143
9.3 Ondersteuning
9.3.3
Hoofdstuk 9. Besluiten
IDE
Voor het ontwikkelen van de applicaties werd ook gebruik gemaakt van verschillende IDE’s. Visual Studio en Netbeans kwamen hier uit als de meest intuitieve, geavanceerde IDE’s. Bij het gebruik van zo een geavanceerde IDE viel het op dat er een re¨eel gevaar dreigde om de voeling met de code en applicatie te verliezen. Veel code kan namelijk automatisch gegenereerd worden wat wel de productiviteit ten goede komt maar niet het inzicht in de code. Sterker nog, vaak verliest men soms de vaardigheid van eenvoudige dingen doordat men ze nooit manueel hoeft te doen. Het promoot dan ook een soort ”luie” programmeerstijl.
9.3.4
Onderhoudbaarheid
Onderhoudbaarheid van de applicaties vormen een belangrijk punt. Veel hangt af van het ontwerp van de applicatie of deze eventueel later kan worden uitgebreid. Zo werden bij onze applicaties soms zaken hardgecodeerd, wat meteen betekent dat indien er iets veranderd dient te worden we heel wat moeten aanpassen. De onderhoudbaarheid en leesbaarheid van de code kan worden verbeterd door een zelfde stijl aan te houden en voldoende te documenteren. Men zal zich houden aan bepaalde conventies. Dit werd in onze applicaties dan ook geprobeerd.
9.3.5 9.3.5.1
Andere Versioning
De programmeertaal D bevat enkele sleutelwoorden die gebruikt kunnen worden bij versioning, waarbij men een applicatie dient te schrijven voor meer dan ´e´en platform. In C++ en C# kan dit aan de hand van macro’s. 9.3.5.2
RAII
In C# bestaat er een handig sleutelwoord (using) dat ons toelaat om fine-grained de RAIItechniek toe te passen. De bronnen die je opent, worden dan ook automatisch weer gesloten. De programmeur hoeft zich daarover dan ook geen zorgen meer te maken. 9.3.5.3
Extensions
In de nieuwe versie van C#, nl. 3.0 zit zoiets als extensions vervat. Deze werden gebruikt een merge methode voor Hashtable’s te schrijven. Normaal kan je niets toevoegen aan de Hashtable klasse. Via extensie methoden kan dit wel. Dit is een heel handig gegeven.
pagina 144
Deel III
Conclusie en future work
pagina 145
Hoofdstuk 10. Conclusie
Hoofdstuk 10
Conclusie De talen die we hier besproken hebben verschillen niet zoveel op het gebied van features op een paar uitzonderingen na. Vaak komt het hier neer op een afweging die gemaakt dient te worden. Perfectie voor een bepaald gebied houdt specialisatie in. Wat voor de ene toepassing dus perfect is, is voor de andere onvolmaakt [92]. Veelal had dit in dit onderzoek te maken met veiligheid versus vrijheid of effici¨entie versus productiviteit. Hoe meer veiligheid men wenst, hoe restrictiever de taal zal worden en hoe minder vrijheid aan de programmeur zal worden gegeven. Hoe productiever je wenst te zijn, hoe meer je eventueel aan effici¨entie zal moeten inboeten. Zo kan je in C++ heel effici¨ente code schrijven, maar duurt het veel te lang om alle bugs te verwijderen. Met een andere taal zoals Java of C# had je het probleem bijvoorbeeld sneller kunnen oplossen, mits enig performantieverlies. De programmeur zal dus zelf moeten afwegen waar hij voor kiest. Met dit inzicht en de over het algemeen kleine verschillen, kan je van een bepaalde taal moeilijk zeggen dat ze beter is dan een andere. Een belangrijke conclusie die we hieruit kunnen trekken is dat om een bepaald doel te bereiken de gebruikte programmeertaal op zich niet zo doorslaggevend is. Met de ondersteuning voor de talen, waaronder we onder andere documentatie, ontwikkelomgevingen en bibliotheken verstaan, moet zeker en vast rekening worden gehouden. Of een programmeertaal reeds gekend is kan uiteraard ook een doorslaggevende factor zijn want een nieuwe taal leren is een heel tijdrovende en bijgevolg dure bezigheid. Het kan nochtans wel degelijk een investering zijn die de moeite waard is. Het is altijd nuttig om kennis te hebben van meerdere programmeertalen, want een taal die de beste is voor alle toepassingsdomeinen zal wellicht nooit bestaan. Door nieuwe programmeertalen te leren, leer je trouwens ook veel bij over verschillende programmeerstijlen en zo voel je beter en beter aan hoe problemen logisch kunnen benaderd worden.
pagina 146
Hoofdstuk 11. Future Work
Hoofdstuk 11
Future Work Met een uitgebreid onderwerp als dit is het uiteraard niet mogelijk om alles te beschrijven. Vaak kon een onderwerp slechts oppervlakkig of zelfs helemaal niet worden behandeld. Een aantal interessante nog te onderzoeken onderwerpen worden hier nog eens vermeld. Ten einde verschillende programmeerstijlen in de talen te onderzoeken kan het interessant zijn om onder andere contract programming en generiek programmeren eens nader te onderzoeken. Generiek programmeren werd niet echt uitgewerkt in onze applicaties. Dit is echter een krachtig paradigma dat zeker nader onderzoek waard is. De talen zouden ook eens vergeleken kunnen worden door bestaande programming patterns te implementeren en zo na te gaan wat de verschillende talen kunnen. Verder zijn er ook enkele praktische zaken die verder onderzocht kunnen worden. Hieronder vallen ondermeer databanktoegang, webservices, functors, closures, linq, xaml, lamba expressies en expressiebomen, ...
pagina 147
LIJST VAN FIGUREN
LIJST VAN FIGUREN
Lijst van figuren 1.1
Imperatieve versus declaratieve paradigma’s . . . . . . . . . . . . . . . . . . . . .
2.1 2.2 2.3 2.4
Compilatie proces CLI . . . . . . . . Typesysteem . . . .NET 3.0 . . . . .
6.1
Sokoban . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
pagina 148
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
6 19 28 29 34
LIJST VAN TABELLEN
LIJST VAN TABELLEN
Lijst van tabellen 2.1 2.2 2.3 2.4
C++ Versies .NET Versies C# Versies . Java Versies .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
18 32 37 43
3.1
D Array Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
56
4.1
Typering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
98
pagina 149
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
BIBLIOGRAFIE
BIBLIOGRAFIE
Bibliografie [1] B. Albahari. A comparative overview of c#. http://genamics.com/developer/csharp_ comparative_part3.htm, augustus 2000. [2] Declarations. http://www.digitalmars.com/d/declaration.html#alias, april 2007. [3] Alignattribute. http://www.digitalmars.com/d/attribute.html#align, april 2007. [4] O. Anderson. A, b, c, ... d! the programming language. OSNews, april 2004. [5] Arrays tutorial. .aspx, mei 2007.
http://msdn2.microsoft.com/en-us/library/aa288453(VS.71)
[6] Asynccallback. http://msdn2.microsoft.com/en-us/library/system. asynccallback(VS.71).aspx, mei 2007. [7] R. G. Baldwin. Learning c# and oop, properties, part 1. , oktober 2002. [8] C# nested classes are like c++ nested classes, not java inner classes. http://blogs. msdn.com/oldnewthing/archive/2006/08/01/685248.aspx, augustus 2006. [9] Devc++ 5. http://www.bloodshed.net/devcpp.html, mei 2007. [10] Devc++ 5. http://www.bloodshed.net/dev/devcpp.html, mei 2007. [11] W. Bright. The d programming language. Doctor Dobb’s Journal, februari 2002. [12] W. Bright. The d programming language. http://www.digitalmars.com/d/sdwest/ paper.html, 2004. [13] W. Bright. Walter bright. http://www.walterbright.com/, april 2006. [14] W. Bright. D Programming Language, 2007. [15] W. Bright. Templates revisited. templates-revisited.html, mei 2007.
http://www.digitalmars.com/d/
[16] W. Bryant. The difference between structs and classes. http://carcino.gen.nz/tech/ cpp/struct_vs_class.php, mei 2007.
pagina 150
BIBLIOGRAFIE
BIBLIOGRAFIE
[17] A. Buckley. Language proposals at sdn. http://blogs.sun.com/abuckley/, februari 2007. [18] Closure. http://en.wikipedia.org/wiki/Closure_(computer_science), mei 2007. [19] Funpm. http://sneezy.cs.nott.ac.uk/fun/nov-06/FunPm.ppt, mei 2007. [20] A. Cooke. Safe as houses. www.acooke.org/andrew/writing/oop.pdf, mei 2007. [21] S. Cornett. Code coverage analysis. http://www.bullseye.com/coverage.html, mei 2007. [22] Namedgzipstream, covariance and contravariance. msdnmag/issues/05/10/NETMatters/, oktober 2005. [23] Covariance and contravariance in delegates. library/ms173174(VS.80).aspx, mei 2007.
http://msdn.microsoft.com/
http://msdn2.microsoft.com/en-us/
[24] Cpptest. http://cpptest.sourceforge.net/, mei 2007. [25] Cppunit. http://cppunit.sourceforge.net/, mei 2007. [26] C preprocessing. http://en.wikipedia.org/wiki/C_preprocessor, mei 2007. [27] The c# language. http://msdn2.microsoft.com/en-us/vcsharp/aa336809.aspx, april 2007. [28] C# 3.0 specification. http://msdn.microsoft.com/library/default.asp?url= /library/en-us/dnvs05/html/cs3spec.asp, april 2007. [29] C# 3.0. http://blogger.xs4all.nl/detrio00/archive/2005/09/15/59685.aspx, september 2005. [30] Walkthrough: Reading xml data into a dataset. http://msdn2.microsoft.com/en-us/ library/ekw4dh3f(VS.80).aspx, jan 2007. [31] D programming language news groups. http://www.digitalmars.com/NewsGroup.html. [32] Doxygen - source code documentation generator tool. http://www.doxygen.org, mei 2007. [33] The c preprocessor versus d. http://www.digitalmars.com/d/pretod.html, april 2007. [34] P. dr. ir. Bart Dhoedt. Cursus software-ontwikkeling ii, 2006. [35] P. dr. ir. Filip De Turck. Cursus software-ontwikkeling i, 2006. [36] P. dr. ir. Herman Tromp. Cursus software-ontwikkeling en objectgeorinteerde talen i, 2005. [37] P. dr. V. Fack and P. dr. K. Coolsaet. Programmeren 1, 2002.
pagina 151
BIBLIOGRAFIE
BIBLIOGRAFIE
[38] Open source development for the d programming language. http://www.dsource.org/. [39] J. Dunlap. Lambda expressions. lambdaexpressions.asp, maart 2007.
http://www.codeproject.com/csharp/
[40] Enums. http://www.digitalmars.com/d/enum.html, april 2007. [41] Enums. http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html, augustus 2005. [42] Enum. 2007.
http://java.sun.com/docs/books/tutorial/java/javaOO/enum.html, mei
[43] Friend. http://www.codersource.net/cpp_tutorial_friend.html, januari 2004. [44] Function overloading. http://www.codersource.net/cpp_tutorial_function_ overloading.html, januari 2004. [45] Function pointer. http://en.wikipedia.org/wiki/Function_pointer, mei 2007. [46] Functions. http://www.digitalmars.com/d/function.html, april 2007. [47] Functor. http://groups.google.com/group/comp.lang.c++/browse_thread/thread/ 144915c2ab47dcc5?hl=en&lr=&ie=UTF-8&rnum=, mei 2007. [48] Functor. http://www.newty.de/fpt/functor.html, mei 2007. [49] Function object. http://en.wikipedia.org/wiki/Function_object, mei 2007. [50] A. Gittleman. A programming languages course at web speed. pages 177–181, 2006.
[51] S. Guthrie. New c# ”orcas” language features: Automatic properties, object initializers, and collection initializers. http://weblogs.asp.net/scottgu/archive/2007/03/08/ new-c-orcas-language-features-automatic-properties-object-initializers-and-collec aspx, maart 2007. [52] B. Hayes. The semicolon wars. American Scientist, 94:299–304, juli 2006. [53] Using new/shadows causes ”hideous hiding”. http://safari.oreilly.com/0596009097/ netgotchas-CHP-6-SECT-2, mei 2007. [54] D. Hogan. D ... c ’done right’ ? Bitwise, mei 2007. [55] D. Hogan and H. Collingbourne. Interview with walter bright. Bitwise, mei 2007. [56] E. International. Ecma 334 c# language specification, juni 2006. [57] International standard for the c++ programming language published. research.att.com/~bs/iso_pressrelease2.html, februari 2007.
pagina 152
http://www.
BIBLIOGRAFIE
BIBLIOGRAFIE
[58] D. Janssens. Programmeerparadigmas. www.fots.ua.ac.be/~pvgorp/professional/ teaching/paradigms2BACH/les1.ppt, 2006. [59] Java platform, standard edition 6 api specification. http://java.sun.com/javase/6/ docs/api/. [60] Free and open source java. http://www.sun.com/software/opensource/java/. [61] Destroying objects and preprocessing and the java language. http://java.sun.com/ developer/JDCTechTips/2003/tt0408.html, april 2003. [62] Junit. http://www.junit.org/, april 2007. [63] C. S. Lindsey, J. S. Tolliver, and T. Lindblad. JavaTech: Introduction to Scientific and Technical Computing with Java. Cambridge University Press, oktober 2005. [64] The linq project. http://msdn2.microsoft.com/en-us/netframework/aa904594.aspx, april 2007. [65] A. Matthews. C# automatic properties: Semantic changes to help cope with poor syntax? http://aabs.wordpress.com/2007/01/29/ c-automatic-properties-semantic-changes-to-help-cope-with-poor-syntax/, januari 2007. [66] D. Muller. Nominative and structural typing. NominativeAndStructuralTyping, april 2006.
http://c2.com/cgi/wiki?
[67] C. Nagel, B. Evjen, J. Glynn, M. Skinner, K. Watson, and A. Jones. Professional C# 2005. Wiley Publishing, Inc., 2006. [68] Ncover. http://ncover.org/site/, mei 2007. [69] Ncoverexplorer. http://www.kiwidude.com/blog/, mei 2007. [70] Nested function. http://en.wikipedia.org/wiki/Nested_function, april 2007. [71] Lutz roeder’s programming.net c# vb clr. http://www.aisto.com/roeder/dotnet/, mei 2007. [72] .net reflector add-ins. http://www.codeplex.com/reflectoraddins, mei 2007. [73] K. Nrmark. Programming paradigms. http://www.cs.aau.dk/~normark/prog3-03/ html/notes/paradigms_themes-paradigms.html. [74] Nunit. http://nunit.com/, mei 2007. [75] Operator overloading. 2007.
http://en.wikipedia.org/wiki/Operator_overloading, mei
pagina 153
BIBLIOGRAFIE
BIBLIOGRAFIE
[76] History of c++: Origins and examples. http://www.planetpapers.com/Assets/1847. php, november 2000. [77] Using params keyword. http://www.csharphelp.com/archives/archive18.html, mei 2007. [78] A. Patrizio. When is d better than c? when it’s a language. internetnews.com, januari 2007. [79] Plain old data. http://www.digitalmars.com/d/glossary.html#pod, april 2007. [80] T. Prasad. Programming paradigms. www.cs.wright.edu/~tkprasad/courses/cs784/ L5Pdm.ppt. [81] Properties. http://www.digitalmars.com/d/property.html, april 2007. [82] finally. http://www.research.att.com/~bs/bs_faq2.html#finally, mei 2007. [83] B. Rector. Comparing .net generics and c++ templates. http://www.codeguru.com/ columns/experts/article.php/c7423/, juni 2004. [84] Reflection (computer science). (computer_science)#C.23, mei 2007.
http://en.wikipedia.org/wiki/Reflection_
[85] Robust definition. http://www.bellevuelinux.org/robust.html, juni 2005. [86] Sarath. What’s c++ 0x. c-0x-what-is-it/, januari 2007.
http://sarathc.wordpress.com/2007/01/31/
[87] B. D. Smet. C# 3.0 automatic properties explained. http://community.bartdesmet. net/blogs/bart/archive/2007/03/03/c-3-0-automatic-properties-explained. aspx, maart 2007. [88] Z. Smith. The top 5 changes in c# 3.0 for developers looking for more flexible solutions. november 2006. [89] What does the stathread attribute. http://www.sellsbrothers.com/askthewonk/ Secure/WhatdoestheSTAThreadattri.htm, mei 2007. [90] Stathread atrribute. http://msdn2.microsoft.com/en-us/library/system. stathreadattribute(vs.71).aspx, mei 2007. [91] D strings vs c++ strings. 2007.
http://www.digitalmars.com/d/cppstrings.html, april
[92] B. Stroustrup. De programmeertaal C++. Pearson Education, 2002. [93] B. Stroustrup. A brief look at c++0x. http://www.artima.com/cppsource/cpp0x.html, januari 2006.
pagina 154
BIBLIOGRAFIE
BIBLIOGRAFIE
[94] B. Stroustrup. Faq: When next standard? faq.html#bootstrapping, april 2007.
http://www.research.att.com/~bs/bs_
[95] B. Stroustrup. Faq: When next standard? faq.html#When-next-standard, april 2007.
http://www.research.att.com/~bs/bs_
[96] B. Stroustrup. Homepage bjarne stroustrup. http://www.research.att.com/~bs/, mei 2007. [97] Structs & unions. http://www.digitalmars.com/d/struct.html, april 2007. [98] Annotations. http://java.sun.com/docs/books/tutorial/java/javaOO/ annotations.html, 20007. [99] Using java reflection. http://java.sun.com/developer/technicalArticles/ALT/ Reflection/index.html, mei 2007. [100] Testdriven.net. http://www.testdriven.net/, mei 2007. [101] T. Thai and H. Q. Lam. .NET Framework Gids. Academic Service, 2002. [102] Template unit testing. http://tut-framework.sourceforge.net/, mei 2007. [103] http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4983159, januari 2004. [104] Variadic function. http://en.wikipedia.org/wiki/Variadic_function, april 2007. [105] B. Venners and B. Eckel. Generics in c#, java, and c++ : A conversation with anders hejlsberg part vii. http://www.artima.com/intv/genericsP.html, january 2004. [106] Visual studio. 2007.
http://msdn2.microsoft.com/en-us/vstudio/aa700831.aspx, mei
[107] R. VS. Understanding properties in c#. http://www.c-sharpcorner.com/UploadFile/ rajeshvs/PropertiesInCS11122005001040AM/PropertiesInCS.aspx, oktober 2001. [108] Wikipedia, the free encyclopedia. http://en.wikipedia.org/. [109] Application binary interface. http://nl.wikipedia.org/wiki/Application_binary_ interface, mei 2007. [110] Abstraction (computer science). (computer_science), mei 2007.
http://en.wikipedia.org/wiki/Abstraction_
[111] Ado.net. http://nl.wikipedia.org/wiki/ADO.NET, april 2007. [112] Anders hejlsberg. http://en.wikipedia.org/wiki/Anders_Hejlsberg, mei 2007. [113] Application programming interface. http://nl.wikipedia.org/wiki/Application_ Programming_Interface, maart 2007.
pagina 155
BIBLIOGRAFIE
BIBLIOGRAFIE
[114] C++. http://en.wikipedia.org/wiki/C++, mei 2007. [115] Component object model. http://nl.wikipedia.org/wiki/Component_Object_Model, februari 2007. [116] Component based software engineering. http://en.wikipedia.org/wiki/ Component-based_software_engineering, mei 2007. [117] Design by contract. http://en.wikipedia.org/wiki/Design_by_contract, mei 2007. [118] Covariance and contravariance. http://en.wikipedia.org/wiki/Covariance_and_ contravariance_(computer_science), mei 2007. [119] C#. http://en.wikipedia.org/wiki/C_Sharp, mei 2007. [120] D programming language. http://en.wikipedia.org/wiki/D_programming_language, mei 2007. [121] Dynamic link library. 2007.
http://en.wikipedia.org/wiki/Dynamic-link_library, mei
[122] Programming paradigm. april 2007. [123] Generic programming. 2007.
http://en.wikipedia.org/wiki/Programming_paradigm,
http://en.wikipedia.org/wiki/Generic_programming, mei
[124] Generiek programmeren. april 2007.
http://nl.wikipedia.org/wiki/Generiek_programmeren,
[125] Interpreter (computing). http://en.wikipedia.org/wiki/Interpreter_(computing), mei 2007. [126] Interpreted language. 2007.
http://en.wikipedia.org/wiki/Interpreted_language, mei
[127] Java annotation. http://en.wikipedia.org/wiki/Java_annotation, mei 2007. [128] Java (programming language). http://en.wikipedia.org/wiki/Java_(programming_ language), mei 2007. [129] Java (programmeertaal). http://nl.wikipedia.org/wiki/Java_(programmeertaal), mei 2007. [130] Java package. http://nl.wikipedia.org/wiki/Java_package, januari 2007. [131] Java version history. 2007.
http://en.wikipedia.org/wiki/Java_version_history, mei
pagina 156
BIBLIOGRAFIE
BIBLIOGRAFIE
[132] Just-in-time compilation. compilation, mei 2007.
http://en.wikipedia.org/wiki/Just-in-time_
[133] Java virtual machine. http://nl.wikipedia.org/wiki/Java_Virtual_Machine, april 2007. [134] .net framework. http://en.wikipedia.org/wiki/.NET_Framework, mei 2007. [135] Wikipedia, de vrije encyclopedie. http://nl.wikipedia.org/. [136] Aspectgeorinteerd programmeren. Aspectgeorinteerd_programmeren, mei 2007.
http://nl.wikipedia.org/wiki/
[137] Obfuscation. http://en.wikipedia.org/wiki/Obfuscation, april 2007. [138] Preprocessing. http://en.wikipedia.org/wiki/Preprocessing, januari 2007. [139] Resource acquisition is initialization. http://en.wikipedia.org/wiki/RAII, may 2007. [140] Sokoban. http://nl.wikipedia.org/wiki/Sokoban, mei 2007. [141] Spoofing attack. http://en.wikipedia.org/wiki/Spoofing_attack, mei 2007. [142] Unit testing. http://en.wikipedia.org/wiki/Unit_test, mei 2007. [143] Visual j++. http://en.wikipedia.org/wiki/J_plus_plus, april 2007. [144] wxdev-cpp. http://wxdsgn.sourceforge.net/, mei 2007. [145] wxwidgets. http://www.wxwidgets.org/, mei 2007. [146] Events. http://www.wxwidgets.org/wiki/index.php/Events, mei 2007. [147] What is. 2007.
http://www.wxwidgets.org/manuals/2.6.3/wxwhatis.html#whatis, mei
[148] wxwindows. http://www.bzzt.net/~wxwidgets/icpp_wx1.html, mei 2007.
pagina 157