Slice Metadata Directives

12 min read

9 min read

10 min read

8 min read

7 min read

6 min read

8 min read

6 min read

8 min read

Slice has the concept of a metadata directive. For example:

Slice
["java:type:java.util.LinkedList<Integer>"] sequence<int> IntSeq;

A metadata directive can appear as a prefix to any Slice definition. Metadata directives appear in a pair of square brackets and contain one or more string literals separated by commas. For example, the following is a syntactically valid metadata directive containing two strings:

Slice
["a", "b"] interface Example {}

Metadata directives are not part of the Slice language per se: the presence of a metadata directive has no effect on the client-server contract, that is, metadata directives do not change the Slice type system in any way. Instead, metadata directives are targeted at specific back-ends, such as the code generator for a particular language mapping. In the preceding example, the java: prefix indicates that the directive is targeted at the Slice to Java compiler.

Metadata directives permit you to provide supplementary information that does not change the Slice types being defined, but somehow influences how the compiler will generate code for these definitions. For example, a metadata directive ["java:type:java.util.LinkedList<T>"] instructs the Slice to Java compiler to map a sequence to a linked list instead of an array (which is the default).

Apart from metadata directives that are attached to a specific definition, there are also file metadata directives. For example:

Slice
[["cpp:dll-export:WIDGET_API"]]

Note that a file metadata directive is enclosed by double square brackets, whereas a local metadata directive (one that is attached to a specific definition) is enclosed by single square brackets. File metadata directives are used to pass instructions that affect the entire Slice file. File metadata directives must precede any definitions in a file (but can appear following any #include directives).

We describe below the metadata directives you can use.

This directive applies to interfaces and operations.

In C++, C#, and Java, this directive instructs the Slice compiler to generate an asynchronous method in the skeleton class or interface instead of the default synchronous method. You could alternatively make your servant class derive from an async skeleton, and not rely on this metadata directive. See Operations for details.

This directive is ignored by other Slice compilers.

This directive allows you to emit a deprecation warning for Slice constructs.

This directive defines the encoding format used for class instances marshaled as the arguments or result of an operation. Ice always marshals user exceptions in the sliced format. You can apply this directive to an interface, which affects all of its operations, or to an operation. Its argument is compact or sliced: format:compact selects the compact format, and format:sliced selects the sliced format. On an operation, the directive overrides the format directive of its enclosing interface.

When neither an operation nor its enclosing interface selects a format, the Ice.Default.SlicedFormat property selects it.

This directive lets a servant marshal the results of an operation as soon as it computes them, typically while it holds the lock that protects the data it returns. The mapped skeleton method returns a “marshaled result” struct or class whose constructor marshals the return value and out parameters immediately.

You can apply this directive to an operation, or to an interface, where it covers every operation that the interface defines. The Slice compiler changes the mapping of an operation only when its return type or one of its out parameters is a class, structure, sequence, or dictionary. This directive affects the server-side mapping in C++, C#, and Java, and has no effect on the client-side mapping.

For example:

Slice
sequence<int> IntSeq;
sequence<IntSeq> IntIntSeq;
sequence<string> StringSeq;
class Grid
{
StringSeq xLabels;
StringSeq yLabels;
IntIntSeq values;
}
interface GridIntf
{
// We want to marshal the returned Grid object within a lock,
// and return a consistent object not affected by concurrent calls to
// clearValues.
["marshaled-result"]
["cs:identifier:GetGrid"]
Grid getGrid();
void clearValues();
}

The mapped skeleton member function for getGrid is:

C++
GetGridMarshaledResult getGrid(const Ice::Current& current) = 0;

where GetGridMarshaledResult is a generated class with a constructor that accepts a parameter for the return value, followed by Current:

C++
// Generated server-side code
class GetGridMarshaledResult : public Ice::MarshaledResult
{
public:
// Marshals returnValue immediately.
GetGridMarshaledResult(const GridPtr& returnValue, const Ice::Current& current);
};

A typical implementation of the getGrid operation in your servant would be:

C++
GridServant::GetGridMarshaledResult
GridServant::getGrid(const Ice::Current& current)
{
lock_guard lock(_mutex);
// marshal _grid data member within synchronization
return GetGridMarshaledResult{_grid, current};
}

The mapped skeleton method for getGrid is:

C#
GridIntf_GetGridMarshaledResult GetGrid(Ice.Current current);

where GridIntf_GetGridMarshaledResult is a generated record struct with a constructor that accepts a parameter for the return value, followed by Current:

C#
// Generated server-side code
public readonly record struct GridIntf_GetGridMarshaledResult : Ice.MarshaledResult
{
// Marshals returnValue immediately.
public GridIntf_GetGridMarshaledResult(Grid? ret, Ice.Current current)
{
...
}
...
}

A typical implementation of the getGrid operation in your servant would be:

C#
public override GridIntf_GetGridMarshaledResult GetGrid(Ice.Current current)
{
lock (_mutex)
{
// marshal _grid field within synchronization
return new GridIntf_GetGridMarshaledResult(_grid, current);
}
}

The mapped skeleton method for getGrid is:

Java
GridIntf.GetGridMarshaledResult getGrid(com.zeroc.Ice.Current current);

where GetGridMarshaledResult is a nested static class with a constructor that accepts a parameter for the return value, followed by Current:

Java
// Generated server-side code
public interface GridIntf extends com.zeroc.Ice.Object {
public static class GetGridMarshaledResult implements
com.zeroc.Ice.MarshaledResult {
public GetGridMarshaledResult(
Grid returnValue,
com.zeroc.Ice.Current current) {
...
}
}
...
}

A typical implementation of the getGrid operation in your servant would be:

Java
@Override
public GridIntf.GetGridMarshaledResult getGrid(com.zeroc.Ice.Current current) {
synchronized (_mutex) {
// marshal _grid field within synchronization
return new GridIntf.GetGridMarshaledResult(_grid, current);
}
}

This file directive allows to suppress Slice compiler warnings. It applies to all definitions in the Slice file that includes this directive. If one or more categories are specified (for example "suppress-warning:invalid-comment" or "suppress-warning:deprecated, invalid-comment") only warnings matching these categories will be suppressed, otherwise all warnings are suppressed. The categories are described in the following table:

Suppress Warning CategoryDescription
all
Suppress all Slice compiler warnings. Equivalent to [["suppress-warning"]].
deprecated
Suppress warnings related to deprecated features.
invalid-comment
Suppress warnings related to invalid doc-comments.

The oneway metadata is used to ensure that a proxy operation is only called by a one-way proxy.

A one-way request is a "fire and forget" request: the request is considered successful as soon as it's sent successfully.

This directive can only be applied to operations that do not return data (no return type, out parameters, or exception specification). OnewayOnlyException is thrown if an operation with this metadata is invoked using a two-way proxy.

It has no effect on the server-side generated code.

The metadata directives for C++ uses the cpp prefix.

This directive applies to sequence parameters in operations. It directs the Slice compiler to map these parameters to pairs of pointers.

This directive applies to operations. It directs the Slice compiler to create a const pure virtual member function for the skeleton class.

This directive applies to enumerations, structs, classes and exceptions. It tells the Slice compiler that you want to implement your own “custom print” for this type, and not rely on the compiler-generated print implementation.

For an enum E, the Slice compiler generates a declaration for std::ostream& operator<<(std::ostream&, E) in the enclosing namespace, but does not implement this operator.

For a struct S, the Slice compiler generates a declaration for std::ostream& operator<<(std::ostream&, const S&) in the enclosing namespace, but does not implement this operator.

For a class or exception C, the Slice compiler generates a declaration for the member function void ice_print(std::ostream& os) const override in the mapped C++ class, but does not implement this member function.

This file directive applies to all definitions in a Slice file.

Use SYMBOL to control the export and import of symbols from DLLs on Windows and shared libraries on other platforms. This option allows you to export symbols from the generated code, and place such generated code in a DLL (on Windows) or shared library (on other platforms). As an example, compiling a Slice file Widget.ice with:

Slice
[["cpp:dll-export:WIDGET_API"]]

results in the following additional code being generated into Widget.h:

C++
#ifndef WIDGET_API
# if defined(WIDGET_API_EXPORTS)
# define WIDGET_API ICE_DECLSPEC_EXPORT
# else
# define WIDGET_API ICE_DECLSPEC_IMPORT
# endif
#endif

The generated code also includes the provided SYMBOL name (WIDGET_API in our example) in the declaration of classes and functions that need to be exported (when building a DLL or shared library) or imported (when using such library).

ICE_DECLSPEC_EXPORT and ICE_DECLSPEC_IMPORT are macros that expand to compiler-specific attributes. For example, for Visual Studio, they are defined as:

C++
#if defined(_MSC_VER)
# define ICE_DECLSPEC_EXPORT __declspec(dllexport)
# define ICE_DECLSPEC_IMPORT __declspec(dllimport)

With GCC and clang, they are defined as:

C++
#elif defined(__GNUC__) || defined(__clang__)
# define ICE_DECLSPEC_EXPORT [[gnu::visibility("default")]]
# define ICE_DECLSPEC_IMPORT [[gnu::visibility("default")]]

The generated .cpp file (Widget.cpp in our example) defines SYMBOL_EXPORTS; this way, you don't need to do anything special when compiling generated files.

This file directive instructs the Slice compiler to generate a doc-comment with @headerfile and the specified C++ header for all generated C++ classes.

This file directive allows you to use a file extension for C++ header files other than the default .h extension.

This directive applies to all Slice constructs, and instructs the Slice compiler to use the specified c++-identifier.

For example:

Slice
struct Descriptor
{
["cpp:identifier:blueprint"]
string template;
}

The cpp:identifier directive ensures the field template is mapped to blueprint in C++. We can’t use the default mapping (template) since it’s a C++ keyword.

This directive applies to exceptions. It is a deprecated alias for cpp:custom-print.

This file directive allows you to inject additional #include directives into the generated C++ header file. This is useful when using the cpp:type metadata.

This file directive allows you to use a file extension for C++ source files other than the default .cpp extension.

This file directive allows you to inject additional #include directives into the generated C++ source file. This is required to make forward declared types visible to the source files.

This directive applies to sequences and dictionaries. It directs the Slice compiler to map the Slice type or parameter to the provided C++ type.

These directives apply to fields of type string as well as to containers, such as structures, classes and exceptions. String fields map by default to std::string. You can use the cpp:type:wstring metadata to cause a string field (or all string fields in a structure, class or exception) to map to std::wstring instead. Use the cpp:type:string metadata to force string fields to use the default mapping regardless of any enclosing metadata.

Slice
module A
{
["cpp:type:wstring"] struct Struct1
{
string s1; // Maps to std::wstring
["cpp:type:string"] string s2; // Maps to std::string
}
}

This directive applies to sequence parameters. It directs the Slice compiler to map this parameter to the provided C++ type when this parameter does not need to hold any memory, for example when mapping an in-parameter to a proxy function.

The metadata directives for C# uses the cs prefix.

This directive applies to enums, enumerators, constants and fields. It injects a C# attribute definition into the generated code.

This directive applies to Slice structures. It directs the Slice compiler to emit a C# class instead of a structure.

These directives apply to sequences and map them to the specified sequence type.

This directive applies to dictionaries and maps them to the specified type.

This directive applies to sequences and allows you map them to custom types.

This directive applies to all Slice constructs, and instructs the Slice compiler to use the specified csharp-identifier.

For example:

Slice
interface Greeter
{
["cs:identifier:Greet"]
string greet(string name);
}

The cs:identifier directive in this example ensures operation greet is mapped to methods Greet and GreetAsync in C#, instead of the default (greet and greetAsync).

This directives applies to Slice interfaces, classes, exceptions, structures, sequences, dictionaries, enumerations and constants. This directive instructs the Slice compiler to generate an internal C# construct, instead of the default, public.

This deprecated directive applies to top-level modules. It instructs the Slice compiler to place the generated C# namespace in the specified namespace. You should use cs:identifier instead.

This directive applies to Slice structures, classes, and exceptions. It directs the Slice compiler to map Slice fields to C# properties instead of C# fields.

This directive applies to Slice structures.

When the Slice structure maps to a C# record struct, the mapped record struct is marked readonly.

When the Slice structure maps to a C# record class, the fields of this class are mapped to readonly C# fields or get-only properties (see cs:property), except for fields with a Slice class type that are mapped as usual (read-write fields or get-set properties).

The metadata directives for Java uses the java prefix.

This directive applies to sequences of certain basic types. It directs the Slice compiler to map the sequence to a subclass of java.nio.Buffer.

This directive applies to fields, structures, classes, and exceptions. It adds accessor and modifier methods (JavaBean methods) for fields.

This directive applies to all Slice constructs, and instructs the Slice compiler to use the specified java-identifier.

For example:

Slice
struct Descriptor
{
["java:identifier:ephemeral"]
bool transient;
}

The java:identifier directive in this example remaps the Slice field transient (a Java keyword) to ephemeral in Java.

This deprecated directive applies to top-level modules and can also be used as file metadata. It instructs the Slice compiler to place the generated Java package in the specified Java package. You should use java:identifier instead on your modules.

This directive applies to sequence<byte>. It allows you to use Ice to transmit serializable Java classes as native objects, without having to define corresponding Slice definitions for these classes. The Slice compiler maps the sequence to java-class, the fully qualified name of a Java class that implements java.io.Serializable. See Filling a Sequence of Bytes with a Serializable Object for an example.

The Slice-to-Java compiler computes a default value for the serialVersionUID member of Slice classes, exceptions and structures. This directive overrides this default generated value with uid, an integer literal. For example:

Slice
["java:serialVersionUID:1"]
struct Point
{
int x;
int y;
}

By using this metadata, the application assumes responsibility for updating the UID whenever changes to the Slice definition affect the serializable state of the type.

This directive allows you to use custom types for sequences and dictionaries.

This directive applies to operations, and indicates that the generated Java methods on the mapped servant interface and class can throw any user exception, regardless the exception specification of the Slice operation. The exception specification for these methods is simply throws com.zeroc.Ice.UserException. This metadata has no effect on the methods of generated proxies.

The metadata directives for JavaScript uses the js prefix.

This directive applies to forward declarations of interfaces and classes. It tells the Slice-to-JavaScript compiler which file to import the forward-declared type from in the TypeScript declarations; file-name is the relative path to the Slice file that defines the type.

This directive applies to all Slice constructs, and instructs the Slice compiler to use the specified javascript-identifier.

For example:

Slice
struct CompilerInfo
{
["js:identifier:debuggerName"]
string debugger;
}

The js:identifier directive in this example ensures that debugger filed, a reserved JavaScript keyword, is mapped debuggerName.

This file directive allows you to tell the Slice-to-JavaScript compiler how the generated modules should be imported by other generated code. It is mainly useful when you want to publish your generated code as an npm package, so that other projects can import the generated code for your Slice definitions by package name rather than by relative path.

For example, the Slice definitions for the Ice built-in files use:

For example the Slice definitions for Ice builtin files use:

Slice
// Ice/Locator.ice
[["js:module:@zeroc/ice"]]
#include "Identity.ice"
module Ice
{
...
}

When you include Ice/Locator.ice in your own Slice files, the generated code will import Ice from @zeroc/ice. Without this directive, the compiler would instead fall back on its default heuristic and generate a relative import, such as from Ice/Locator.js.

Other files that use the same js:module directive will still import each other with relative paths, since they are assumed to be part of the same npm package.

The metadata directives for MATLAB uses the matlab prefix.

This directive applies to all Slice constructs, and instructs the Slice compiler to use the specified matlab-identifier.

For example:

Slice
class AtmosphericConditions
{
["matlab:identifier:Temperature"]
double temperature;
["matlab:identifier:Humidity"]
double humidity;
}

The matlab:identifier directives in this example instructs the Slice compiler to map Slice fields temperature and humidity to Temperature and Humidity properties in MATLAB.

The metadata directives for Python uses the python prefix.

Instructs the Ice for Python runtime to unmarshal a sequence as a Python array.array type. This directive applies to sequences of bool, byte, short, int, long, float, and double. See Python mapping for sequences..

This directive applies to all Slice constructs, and instructs the Slice compiler to use the specified python-identifier.

For example:

Slice
enum Color
{
["python:identifier:RED"]
Red,
["python:identifier:GREEN"]
Green,
["python:identifier:BLUE"]
Blue
}

The python:identifier directives in this example ensure the enumerators Red, Green, and Blue are mapped to RED, GREEN, and BLUE, per Python’s usual conventions, instead of the default mapping (Red, Green, and Blue).

Instruct the Ice for Python runtime to unmarshal a sequence as a list.

Instructs the Ice for Python runtime to unmarshal a sequence as a custom Python type created from a Python memoryview object. This directive applies to sequences of bool, byte, short, int, long, float, and double. See Python mapping for sequences..

Instructs the Ice for Python runtime to unmarshal a sequence as a Python numpy.ndarray type. This directive applies to sequences of bool, byte, short, int, long, float, and double. See Python mapping for sequences..

Instructs the Ice for Python runtime to unmarshal a sequence as a Python tuple.

The metadata directives for Swift uses the swift prefix.

This directive adds the specified Swift attribute to the generated code. It can be used with classes, structs, enums, and exceptions.

This directive applies to all Slice constructs, and instructs the Slice compiler to use the specified swift-identifier.

For example:

Slice
enum ButtonPressed
{
["swift:identifier:snooze"]
Snooze,
["swift:identifier:stop"]
Stop
}

The swift:identifier directives in this example ensure the enumerators Snooze and Stop are mapped to snooze and stop, per Swift’s usual conventions, instead of the default mapping (Snooze and Stop).

This deprecated directive applies to Slice modules. Use swift:identifier instead.

swift:module instructs the Slice compiler to map this Slice module to the specified Swift module. All Swift identifiers in that module also receive the specified prefix, as if they were defined in a nested module prefix.

With respect to the Swift mapping:

Slice
["swift:module:Mod:Pre"] module Test
{
...
}

is equivalent to:

Slice
module Mod
{
module Pre
{
...
}
}