Modules

4 min read

4 min read

4 min read

5 min read

4 min read

4 min read

4 min read

4 min read

5 min read

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:

Slice
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.)

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:

Slice
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).

You can define a nested module directly. For example:

Slice
module ClearSky::Ephemerides
{
// ...
}

is a concise notation for the following modules:

Slice
module ClearSky
{
module Ephemerides
{
// ...
}
}

Modules can be reopened:

Slice
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).

A Slice module maps to a C++ namespace with the same name. The mapping preserves the nesting of the Slice definitions. For example:

Slice
module M1::M2
{
// ...
}
// ...
module M1 // Reopen M1
{
// ...
}

This definition maps to the corresponding C++ definition:

C++
namespace M1::M2
{
// ...
}
// ...
namespace M1 // Reopen M1
{
// ...
}

If a Slice module is reopened, the corresponding C++ namespace is reopened as well.

The cpp:identifier metadata directive allows you to map a module to a C++ namespace or sub-namespace of your choice. For example:

Slice
// module Time becomes namespace remote::clock in C++.
["cpp:identifier:remote::clock"]
module Time
{
// ...
}

You can only use cpp:identifier on a module with a simple name - this metadata directive is not compatible with the nested module syntax.

A Slice module maps to a C# namespace with the same name. The mapping preserves the nesting of the Slice definitions. For example:

Slice
module M1::M2
{
// ...
}
// ...
module M1 // Reopen M1
{
// ...
}

This definition maps to the corresponding C# definition:

C#
namespace M1.M2
{
// ...
}
// ...
namespace M1 // Reopen M1
{
// ...
}

If a Slice module is reopened, the corresponding C# namespace is reopened as well.

The cs:identifier metadata directive allows you to map a module to a C# namespace or sub-namespace of your choice. For example:

Slice
// module Time becomes namespace Remote.Clock in C#.
["cs:identifier:Remote.Clock"]
module Time
{
// ...
}

You can only use cs:identifier on a module with a simple name - this metadata directive is not compatible with the nested module syntax.

A Slice module maps to a Java package with the same name. The mapping preserves the nesting of the Slice definitions. For example:

Slice
module M1::M2
{
// ...
}
// ...
module M1 // Reopen M1
{
// ...
}

This definition maps to the corresponding Java definition:

Java
package M1.M2;
// Definitions for M2 here...
package M1;
// Definitions for M1 here...

Note that these definitions appear in the appropriate source files; source files for definitions in module M1 are generated in directory M1 underneath the top-level directory, and source files for definitions for module M2 are generated in directory M1/M2 underneath the top-level directory. You can set the top-level output directory using the --output-dir option with slice2java.

The java:identifier metadata directive allows you to map a module to a Java package of your choice. For example:

Slice
// module Time becomes package com.example.clock in Java
["java:identifier:com.example.clock"]
module Time
{
// ...
}

You can only use java:identifier on a module with a simple name - this metadata directive is not compatible with the nested module syntax.

Slice modules map to a JavaScript object with the same name and to a TypeScript namespace with the same name as the Slice module. The mapping preserves the nesting of Slice definitions.

For example:

Slice
// Slice definitions in M.ice
module M1::M2 {
// ...
}
module M1 { // Reopen M1
// ...
}

The mapping for these definitions is equivalent to the following code:

JavaScript
// Generated JavaScript code in M.js
export const M1 = {};
M1.M2 = {};
// definitions in M1 and M1.M2
TypeScript
// Generated TypeScript definitions in M.d.ts
export namespace M1 {
namespace M2 {
// Definitions in M1.M2
}
}
export namespace M1 { // Reopen M1
// ...
}

The generated code always exports the top-level modules as named exports. You can import them with standard ES module syntax, for example:

JavaScript
import { M1 } from "./M";

The js:identifier metadata directive allows you to map a module to a JavaScript name or TypeScript namespace or sub-namespace of your choice. For example:

Slice
// module Time becomes object Remote.Clock in JavaScript and namespace
// Remote.Clock in TypeScript.
["js:identifier:Remote.Clock"]
module Time {
// ...
}

You can only use js:identifier on a module with a simple name - this metadata directive is not compatible with the nested module syntax.

A Slice module maps to a MATLAB namespace with the same name. The mapping preserves the nesting of the Slice definitions. For example:

Slice
module M1::M2
{
// ...
}
// ...
module M1 // Reopen M1
{
// ...
}

The Slice compiler generates the corresponding MATLAB definition in the namespace folders +M1 and +M1/+M2.

The matlab:identifier metadata directive allows you to map a module to a MATLAB namespace or sub-namespace of your choice. For example:

Slice
// module Time becomes namespace remote.clock in MATLAB.
["matlab:identifier:remote.clock"]
module Time
{
// ...
}

You can only use matlab:identifier on a module with a simple name - this metadata directive is not compatible with the nested module syntax.

A Slice module maps to a PHP namespace with the same name. The mapping preserves the nesting of the Slice definitions. For example:

Slice
module M1::M2
{
// ...
}
// ...
module M1 // Reopen M1
{
// ...
}

This definition maps to the corresponding PHP definitions:

PHP
namespace M1\M2
{
// ...
}
// ...
namespace M1 // Reopen M1
{
// ...
}

If a Slice module is reopened, the corresponding PHP namespace is reopened as well.

The php:identifier metadata directive allows you to map a module to a PHP namespace or sub-namespace of your choice. For example:

Slice
// module Time becomes namespace Remote\Clock in PHP.
["php:identifier:Remote\Clock"]
module Time
{
// ...
}

You can only use php:identifier on a module with a simple name - this metadata directive is not compatible with the nested module syntax.

A Slice module maps to a Python package with the same name. The mapping preserves the nesting of the Slice definitions. For example:

Slice
module M1::M2
{
// ...
}
// ...
module M1 // Reopen M1
{
// ...
}

This definition maps to the corresponding Python definitions:

M1/__init__.py
M2/M2/__init__.py

If a Slice module is reopened, the corresponding PHP namespace is reopened as well.

The python:identifier metadata directive allows you to map a module to a Python package or sub-package of your choice. For example:

Slice
// module Time becomes package Remote.Clock in Python.
["python:identifier:Remote\Clock"]
module Time
{
// ...
}

You can only use python:identifier on a module with a simple name - this metadata directive is not compatible with the nested module syntax.

A Slice module maps to a Ruby module with the same name. The mapping preserves the nesting of the Slice definitions. For example:

Slice
module M1::M2
{
// ...
}
// ...
module M1 // Reopen M1
{
// ...
}

This definition maps to the corresponding Ruby definitions:

Ruby
module M1::M2
# ...
end
module M1
# ...
end

If a Slice module is reopened, the corresponding Ruby module is reopened as well.

The ruby:identifier metadata directive allows you to map a module to a Ruby module or nested module of your choice. For example:

Slice
// module Time becomes module Remote::Clock in Ruby.
["ruby:identifier:Remote::Clock"]
module Time
{
// ...
}

You can only use ruby:identifier on a module with a simple name - this metadata directive is not compatible with the nested module syntax.

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:

Slice
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:

Slice
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:

Slice
module M1::M2
{
interface A { ... }
}
// ...
module M1 // Reopen M1
{
// More definitions for M1 here...
interface B { ... }
}

This definition maps to the corresponding Swift definitions:

Swift
public protocol M2APrx {
...
}
public protocol BPrx {
...
}

There is no mapped Swift module in this case.

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:

Slice
// 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.