forked from DlangRen/Programming-in-D
-
Notifications
You must be signed in to change notification settings - Fork 1
/
modules.d
622 lines (461 loc) · 21.3 KB
/
modules.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
Ddoc
$(DERS_BOLUMU $(IX module) Modules and Libraries)
$(P
The building blocks of D programs (and libraries) are modules.
)
$(P
D modules are based on a simple concept: Every source file is a module. Accordingly, the single files that we have been writing our programs in have all been individual modules.
)
$(P
By default, the name of a module is the same as its filename without the $(C .d) extension. When explicitly specified, the name of the module is defined by the $(C module) keyword, which must appear as the first non-comment line in the source file.
)
$(P
For example, assuming that the name of a source file is "cat.d", the name of the module would be specified by the $(C module) keyword:
)
---
module cat;
class Cat {
// ...
}
---
$(P
The $(C module) line is optional if the module is not part of any package (see below). When not specified, it is the same as the file name without the $(C .d) extension.
)
$(H6 $(IX static this, module) $(IX static ~this, module) $(IX this, static, module) $(IX ~this, static, module) $(IX module constructor, thread-local) $(C static this()) and $(C static ~this()))
$(P
$(C static this()) and $(C static ~this()) at module scope are similar to their $(C struct) and $(C class) counterparts:
)
---
module cat;
static this() {
// ... the initial operations of the module ...
}
static ~this() {
// ... the final operations of the module ...
}
---
$(P
Code that are in these scopes are executed once for each thread. (Note that most programs consist of a single thread that starts executing the $(C main()) function.) Code that should be executed only once for the entire program (e.g. initializing $(C shared) and $(C immutable) variables) must be defined in $(C shared static this()) and $(C shared static ~this()) blocks, which will be covered in $(LINK2 /ders/d.en/concurrency_shared.html, the Data Sharing Concurrency chapter).
)
$(H6 File and module names)
$(P
D supports Unicode in source code and module names. However, the Unicode support of file systems vary. For example, although most Linux file systems support Unicode, the file names in Windows file systems may not distinguish between lower and upper case letters. Additionally, most file systems limit the characters that can be used in file and directory names.
)
$(P
For portability reasons, I recommend that you use only lower case ASCII letters in file names. For example, "resume.d" would be a suitable file name for a class named $(C Résumé).
)
$(P
Accordingly, the name of the module would consist of ASCII letters as well:
)
---
module resume; // Module name consisting of ASCII letters
class Résumé { // Program code consisting of Unicode characters
// ...
}
---
$(H5 $(IX package, definition) Packages)
$(P
A combination of related modules are called a $(I package). D packages are a simple concept as well: The source files that are inside the same directory are considered to belong to the same package. The name of the directory becomes the name of the package, which must also be specified as the first parts of module names.
)
$(P
For example, if "cat.d" and "dog.d" are inside the directory "animal", then specifying the directory name along with the module name makes them be a part of the same package:
)
---
module $(HILITE animal.)cat;
class Cat {
// ...
}
---
$(P
Similarly, for the $(C dog) module:
)
---
module $(HILITE animal.)dog;
class Dog {
// ...
}
---
$(P
For modules that are parts of packages, the $(C module) line is not optional and the whole module name including the package name must be specified.
)
$(P
Since package names correspond to directory names, the package names of modules that are deeper than one directory level must reflect that hierarchy. For example, if the "animal" directory included a "vertebrate" directory, the name of a module inside that directory would include $(C vertebrate) as well:
)
---
module animal.vertebrate.cat;
---
$(P
The directory hierarchies can be arbitrarily complex depending on the needs of the program. Relatively short programs usually have all of their source files in a single directory.
)
$(H5 Importing modules)
$(P
$(IX import) The $(C import) keyword, which we have been using in almost every program so far, is for introducing a module to the current module:
)
---
import std.stdio;
---
$(P
The module name may contain the package name as well. For example, the $(C std.) part above indicates that $(C stdio) is a module that is a part of the $(C std) package.
)
$(P
The $(C animal.cat) and $(C animal.dog) modules would be imported similarly. Let's assume that the following code is inside a file named "deneme.d":
)
---
module deneme; // the name of this module
import animal.cat; // a module that it uses
import animal.dog; // another module that it uses
void main() {
auto cat = new Cat();
auto dog = new Dog();
}
---
$(P
$(I $(B Note:) As described below, for the program to be built correctly, those module files must also be provided to the linker.)
)
$(P
More than one module can be imported at the same time:
)
---
import animal.cat, animal.dog;
---
$(H6 $(IX selective import) $(IX import, selective) Selective imports)
$(P
$(IX :, import) Instead of importing a module as a whole with all of its names, it is possible to import just specific names from it.
)
---
import std.stdio $(HILITE : writeln;)
// ...
write$(HILITE f)ln("Hello %s.", name); $(DERLEME_HATASI)
---
$(P
The code above cannot be compiled because only $(C writeln) is imported, not $(C writefln).
)
$(P
Selective imports are considered to be better than importing an entire module because it reduces the chance of $(I name collisions). As we will see in an example below, a name collision can occur when the same name appears in more than one imported module.
)
$(P
Selective imports may reduce compilation times as well because the compiler needs to compile only the parts of a module that are actually imported. On the other hand, selective imports require more work as every imported name must be specified separately on the $(C import) line.
)
$(P
This book does not take advantage of selective imports mostly for brevity.
)
$(H6 $(IX local import) $(IX import, local) Local imports)
$(P
So far we have always imported all of the required modules at the tops of programs:
)
---
import std.stdio; $(CODE_NOTE at the top)
import std.string; $(CODE_NOTE at the top)
// ... the rest of the module ...
---
$(P
Instead, modules can be imported at any other line of the source code. For example, the two functions of the following program import the modules that they need in their own scopes:
)
---
string makeGreeting(string name) {
$(HILITE import std.string;)
string greeting = format("Hello %s", name);
return greeting;
}
void interactWithUser() {
$(HILITE import std.stdio;)
write("Please enter your name: ");
string name = readln();
writeln(makeGreeting(name));
}
void main() {
interactWithUser();
}
---
$(P
Local imports are recommended over global imports because instead of importing every module unconditionally at the top, the compiler can import only the ones that are in the scopes that are actually used. If the compiler knows that the program never calls a function, it can ignore the import directives inside that function.
)
$(P
Additionally, a locally imported module is accessible only inside that local scope, further reducing the risk of name collisions.
)
$(P
We will later see in $(LINK2 /ders/d.en/mixin.html, the Mixins chapter) that local imports are in fact required for $(I template mixins.)
)
$(P
The examples throughout this book do not take advantage of local imports mostly because local imports were added to D after the start of writing this book.
)
$(H6 Locations of modules)
$(P
The compiler finds the module files by converting the package and module names directly to directory and file names.
)
$(P
For example, the previous two modules would be located as "animal/cat.d" and "animal/dog.d", respectively (or "animal\cat.d" and "animal\dog.d", depending on the file system). Considering the main source file as well, the program above consists of three files.
)
$(H6 Long and short module names)
$(P
The names that are used in the program may be spelled out with the module and package names:
)
---
auto cat0 = Cat();
auto cat1 = animal.cat.Cat(); // same as above
---
$(P
The long names are normally not needed but sometimes there are name conflicts. For example, when referring to a name that appears in more than one module, the compiler cannot decide which one is meant.
)
$(P
The following program is spelling out the long names to distinguish between two separate $(C Jaguar) structs that are defined in two separate modules: $(C animal) and $(C car):
)
---
import animal.jaguar;
import car.jaguar;
// ...
auto conflicted = Jaguar(); $(DERLEME_HATASI)
auto myAnimal = animal.jaguar.Jaguar(); $(CODE_NOTE compiles)
auto myCar = car.jaguar.Jaguar(); $(CODE_NOTE compiles)
---
$(H6 Renamed imports)
$(P
$(IX renamed import) It is possible to rename imported modules either for convenience or to resolve name conflicts:
)
---
import $(HILITE carnivore =) animal.jaguar;
import $(HILITE vehicle =) car.jaguar;
// ...
auto myAnimal = $(HILITE carnivore.)Jaguar(); $(CODE_NOTE compiles)
auto myCar = $(HILITE vehicle.)Jaguar(); $(CODE_NOTE compiles)
---
$(P
Instead of renaming the entire import, it is possible to rename individual imported symbols.
)
$(P
For example, when the following code is compiled with the $(C -w) compiler switch, the compiler would warn that $(C sort()) $(I function) should be preferred instead of $(C .sort) $(I property):
)
---
import std.stdio;
import std.algorithm;
// ...
auto arr = [ 2, 10, 1, 5 ];
arr$(HILITE .sort); $(CODE_NOTE_WRONG compilation WARNING)
writeln(arr);
---
$(SHELL
Warning: use std.algorithm.sort instead of .sort property
)
$(P
$(I $(B Note:) The $(C arr.sort) expression above is the equivalent of $(C sort(arr)) but it is written in the UFCS syntax, which we will see in $(LINK2 /ders/d.en/ufcs.html, a later chapter).)
)
$(P
One solution in this case is to import $(C std.algorithm.sort) by renaming it. The new name $(C algSort) below means the $(C sort()) $(I function) and the compiler warning is eliminated:
)
---
import std.stdio;
import std.algorithm : $(HILITE algSort =) sort;
void main() {
auto arr = [ 2, 10, 1, 5 ];
arr$(HILITE .algSort);
writeln(arr);
}
---
$(H6 $(IX package import) Importing a package as a module)
$(P
Sometimes multiple modules of a package may need to be imported together. For example, whenever one module from the $(C animal) package is imported, all of the other modules may need to be imported as well: $(C animal.cat), $(C animal.dog), $(C animal.horse), etc.
)
$(P
In such cases it is possible to import some or all of the modules of a package by importing the package as if it were a module:
)
---
import animal; // ← entire package imported as a module
---
$(P
$(IX package.d) It is achieved by a special configuration file in the package directory, which must always be named as $(C package.d). That special file includes the $(C module) directive for the package and imports the modules of the package $(I publicly):
)
---
// The contents of the file animal/package.d:
module animal;
$(HILITE public) import animal.cat;
$(HILITE public) import animal.dog;
$(HILITE public) import animal.horse;
// ... same for the other modules ...
---
$(P
Importing a module publicly makes that module available to the users of the importing module as well. As a result, when the users import just the $(C animal) module (which actually is a package), they get access to $(C animal.cat) and all the other modules as well.
)
$(H6 $(IX deprecated) Deprecating features)
$(P
Modules evolve over time and get released under new version numbers. Going forward from a particular version, the authors of the module may decide to $(I deprecate) some of its features. Deprecating a feature means that newly written programs should not rely on that feature anymore; using a deprecated feature is disapproved. Deprecated features may even be removed from the module in the future.
)
$(P
There can be many reasons why a feature is deprecated. For example, the new version of the module may include a better alternative, the feature may have been moved to another module, the name of the feature may have changed to be consistent with the rest of the module, etc.
)
$(P
The deprecation of a feature is made official by defining it with the $(C deprecated) attribute, optionally with a custom message. For example, the following deprecation message communicates to its user that the name of the function has been changed:
)
---
deprecated("Please use doSomething() instead.")
void do_something() {
// ...
}
---
$(P
By specifying one of the following compiler switches, the user of the module can determine how the compiler should react when a deprecated feature is used:
)
$(UL
$(LI $(IX -d, compiler switch) $(C -d): Using deprecated features should be allowed)
$(LI $(IX -dw, compiler switch) $(C -dw): Using deprecated features should produce compilation warnings)
$(LI $(IX -de, compiler switch) $(C -de): Using deprecated features should produce compilation errors)
)
$(P
For example, calling the deprecated feature in a program and compiling it with $(C -de) would fail compilation:
)
---
do_something();
---
$(SHELL_SMALL
$ dmd deneme.d $(HILITE -de)
$(SHELL_OBSERVED deneme.d: $(HILITE Deprecation): function deneme.do_something is
deprecated - Please use doSomething() instead.)
)
$(P
The name of a deprecated feature is usually defined as an $(C alias) of the new name:
)
---
deprecated("Please use doSomething() instead.")
$(HILITE alias do_something) = doSomething;
void doSomething() {
// ...
}
---
$(P
We will see the $(C alias) keyword in $(LINK2 /ders/d.en/alias.html, a later chapter).
)
$(H6 Adding module definitions to the program)
$(P
The $(C import) keyword is not sufficient to make modules become parts of the program. It simply makes available the features of a module inside the current module. That much is needed only to $(I compile) the code.
)
$(P
It is not possible to build the previous program only by the main source file, "deneme.d":
)
$(SHELL_SMALL
$ dmd deneme.d -w -de
$(SHELL_OBSERVED deneme.o: In function `_Dmain':
deneme.d: $(HILITE undefined reference) to `_D6animal3cat3Cat7__ClassZ'
deneme.d: $(HILITE undefined reference) to `_D6animal3dog3Dog7__ClassZ'
collect2: ld returned 1 exit status)
)
$(P
Those error messages are generated by the $(I linker). Although they are not user-friendly messages, they indicate that some definitions that are needed by the program are missing.
)
$(P
$(IX linker) $(IX building the program) The actual build of the program is the responsibility of the linker, which gets called automatically by the compiler behind the scenes. The compiler passes the modules that it has just compiled to the linker, and the linker combines those modules (and libraries) to produce the executable program.
)
$(P
For that reason, all of the modules that make up the program must be provided to the linker. For the program above to be built, "animal/cat.d" and "animal/dog.d" must also be specified on the compilation line:
)
$(SHELL_SMALL
$ dmd deneme.d animal/cat.d animal/dog.d -w -de
)
$(P
Instead of having to mention the modules individually every time on the command line, they can be combined as libraries.
)
$(H5 $(IX library) Libraries)
$(P
A collection of compiled modules is called a library. Libraries are not programs themselves; they do not have the $(C main()) function. Libraries contain compiled definitions of functions, structs, classes, and other features of modules, which are to be linked later by the linker to produce the program.
)
$(P
dmd's $(C -lib) command line option is for making libraries. The following command makes a library that contains the "cat.d" and the "dog.d" modules. The name of the library is specified with the $(C -of) switch:
)
$(SHELL_SMALL
$ dmd animal/cat.d animal/dog.d -lib -ofanimal -w -de
)
$(P
The actual name of the library file depends on the platform. For example, the extension of library files is $(C .a) under Linux systems: $(C animal.a).
)
$(P
Once that library is built, It is not necessary to specify the "animal/cat.d" and "animal/dog.d" modules individually anymore. The library file is sufficient:
)
$(SHELL_SMALL
$ dmd deneme.d animal.a -w -de
)
$(P
The command above replaces the following one:
)
$(SHELL_SMALL
$ dmd deneme.d animal/cat.d animal/dog.d -w -de
)
$(P
$(IX Phobos, library) As an exception, the D standard library Phobos need not be specified on the command line. That library is automatically included behind the scenes. Otherwise, it could be specified similar to the following line:
)
$(SHELL_SMALL
$ dmd deneme.d animal.a /usr/lib64/libphobos2.a -w -de
)
$(P
$(I $(B Note:) The name and location of the Phobos library may be different on different systems.)
)
$(H6 Using libraries of other languages)
$(P
D can use libraries that are written in some other compiled languages like C and C++. However, because different languages use different $(I linkages), such libraries are available to D code only through their $(I D bindings).
)
$(P
$(IX linkage) $(IX name mangling) $(IX mangling, name) $(IX symbol) Linkage is the set of rules that determines the accessibility of entities in a library as well as how the names (symbols) of those entities are represented in compiled code. The names in compiled code are different from the names that the programmer writes in source code: The compiled names are $(I name-mangled) according to the rules of a particular language or compiler.
)
$(P
$(IX mangle, core.demangle) $(IX demangle) For example, according to C linkage, the C function name $(C foo) would be $(I mangled) with a leading underscore as $(C _foo) in compiled code. Name-mangling is more complex in languages like C++ and D because these languages allow using the same name for different entities in different modules, structs, classes, etc. as well as for overloads of functions. A D function named $(C foo) in source code has to be mangled in a way that would differentiate it from all other $(C foo) names that can exist in a program. Although the exact mangled names are usually not important to the programmer, the $(C core.demangle) module can be used to mangle and demangle symbols:
)
---
module deneme;
import std.stdio;
import core.demangle;
void foo() {
}
void main() {
writeln($(HILITE mangle)!(typeof(foo))("deneme.foo"));
}
---
$(P
$(I $(B Note:) $(C mangle()) is a function template, the syntax of which is unfamiliar at this point in the book. We will see templates later in $(LINK2 /ders/d.en/templates.html, the Templates chapter).)
)
$(P
A function that has the same type as $(C foo) above and is named as $(C deneme.foo), has the following mangled name in compiled code:
)
$(SHELL_SMALL
_D6deneme3fooFZv
)
$(P
Name mangling is the reason why linker error messages cannot include user-friendly names. For example, a symbol in an error message above was $(C _D6animal3cat3Cat7__ClassZ) instead of $(C animal.cat.Cat).
)
$(P
$(IX extern()) $(IX C) $(IX C++) $(IX D) $(IX Objective-C) $(IX Pascal) $(IX System) $(IX Windows) The $(C extern()) attribute specifies the linkage of entities. The valid linkage types that can be used with $(C extern()) are $(C C), $(C C++), $(C D), $(C Objective-C), $(C Pascal), $(C System), and $(C Windows). For example, when a D code needs to make a call to a function that is defined in a C library, that function must be declared as having C linkage:
)
---
// Declaring that 'foo' has C linkage (e.g. it may be defined
// in a C library):
$(HILITE extern(C)) void foo();
void main() {
foo(); // this call would be compiled as a call to '_foo'
}
---
$(P
$(IX namespace, C++) In the case of C++ linkage, the namespace that a name is defined in is specified as the second argument to the $(C extern()) attribute. For example, according to the following declaration, $(C bar()) is the declaration of the function $(C a::b::c::bar()) defined in a C++ library (note that D code uses dots instead of colons):
)
---
// Declaring that 'bar' is defined inside namespace a::b::c
// and that it has C++ linkage:
extern(C++, $(HILITE a.b.c)) void bar();
void main() {
bar(); // a call to a::b::c::bar()
a.b.c.bar(); // same as above
}
---
$(P
$(IX binding) A file that contains such D declarations of the features of an external library is called a $(I D binding) of that library. Fortunately, in most cases programmers do not need to write them from scratch as D bindings for many popular non-D libraries are available through $(LINK2 https://github.com/D-Programming-Deimos/, the Deimos project).
)
$(P
$(IX extern) When used without a linkage type, the $(C extern) attribute has a different meaning: It specifies that the storage for a variable is the responsibility of an external library; the D compiler should not reserve space for it in this module. Having different meanings, $(C extern) and $(C extern()) can be used together:
)
---
// Declaring that the storage for 'g_variable' is already
// defined in a C library:
extern(C) $(HILITE extern) int g_variable;
---
$(P
If the $(C extern) attribute were not specified above, while having C linkage, $(C g_variable) would be a variable of this D module.
)
Macros:
SUBTITLE=Modules and Libraries
DESCRIPTION=D modules and libraries
KEYWORDS=d programming lesson book tutorial module library