Modules
Modules Reduce Clutter
A common problem in large systems is pollution of the global namespace: over time, as isolated systems are integrated, name clashes become quite likely. Slice provides the module construct to alleviate this problem:
module ZeroC
{
module Client
{
// Definitions here...
}
module Server
{
// Definitions here...
}
}
A module can contain any legal Slice construct, including other module definitions. Using modules to group related definitions together avoids polluting the global namespace and makes accidental name clashes quite unlikely. (You can use a well-known name, such as a company or product name, as the name of the outermost module.)
Modules are Mandatory
Slice requires all definitions to be nested inside a module, that is, you cannot define anything other than a module at global scope. For example, the following is illegal:
interface I // Error: only modules can appear at global scope
{
// ...
}
Definitions at global scope are prohibited because they cause problems with some implementation languages (such as Python, which does not have a true global scope).
Throughout the Ice manual, you will occasionally see Slice definitions that are not nested inside a module. This is to keep the examples short and free of clutter. Whenever you see such a definition, assume that it is nested in module M.
Nested Module Syntax
You can define a nested module directly. For example:
module ClearSky::Ephemerides
{
// ...
}
is a concise notation for the following modules:
module ClearSky
{
module Ephemerides
{
// ...
}
}
Reopening Modules
Modules can be reopened:
module ZeroC
{
// Definitions here...
}
// Possibly in a different source file:
module ZeroC // OK, reopened module
{
// More definitions here...
}
Reopened modules are useful for larger projects: they allow you to split the contents of a module over several different source files. The advantage of doing this is that, when a developer makes a change to one part of the module, only files dependent on the changed part need be recompiled (instead of having to recompile all files that use the module).
Language Mapping
A top-level Slice module maps to a Swift module with the same name as the Slice module.
Keep in mind that a Swift module is a unit of code distribution that you define when your build and organize your code. It’s not a namespace construct like in C++ or C#.
Take the Greeter.ice Slice file:
module VisitorCenter
{
interface Greeter { ... }
}
When the Slice to Swift compiler (slice2swift) compiles this file, it does not generate anything for VisitorCenter.
The mapped Swift module is used only when you make cross-module references, as in:
module VisitorCenter
{
interface Greeter { ... }
}
module TourOperator
{
struct PointOfInterest
{
// A cross-module reference.
VisitorCenter::Greeter* greeter;
}
}
With this example, the mapped Swift greeter property is a VisitorCenter.GreeterPrx?.
A nested Slice module is used as prefix for the mapped Swift types in that module. For example:
module M1::M2
{
interface A { ... }
}
// ...
module M1 // Reopen M1
{
// More definitions for M1 here...
interface B { ... }
}
This definition maps to the corresponding Swift definitions:
public protocol M2APrx {
...
}
public protocol BPrx {
...
}
There is no mapped Swift module in this case.
Custom Mapping
The swift:identifier metadata directive allows you to map a top-level module to a Swift module of your choice. For a nested module, swift:identifier remaps the prefix. For example:
// module Time becomes Swift module Clock in cross-module references.
["swift:identifier:Clock"]
module Time
{
// ...
}
You can only use ”swift:identifier” on a module with a simple name - this metadata directive is not compatible with the nested module syntax.