Slice Metadata Directives

12 min read

9 min read

9 min read

8 min read

7 min read

6 min read

7 min read

6 min read

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

deprecate[:message]

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

This directive defines the encoding format used for any classes or exceptions marshaled as the arguments or results of an operation. The tag can be applied to an interface, which affects all of its operations, or to individual operations. Legal values for the tag are format:sliced, format:compact, and format:default. A tag specified for an operation overrides any setting applied to its enclosing interface. The Ice.Default.SlicedFormat property defines the behavior when no tag is present.

This directive applies to operations and changes the return type of mapped skeleton methods in C++, C#, and Java. It has no effect on the client-side mapping.

With this directive, the mapped skeleton method returns a “marshaled result” struct or class that marshals the return value and out parameters immediately in its constructor. This allows you to perform the marshaling in a thread-safe manner, typically while holding a mutex lock.

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(ICE_STATIC_LIBS)
# define WIDGET_API /**/
# ifdef 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 __attribute__((visibility ("default")))
# define ICE_DECLSPEC_IMPORT __attribute__((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 primitive 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-to-Java compiler computes a default value for the serialVersionUID member of Slice classes, exceptions and structures. This directive overrides the this default generated value.

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 apply to forward declarations. The Slice compiler needs to know where the forward declared type is defined in order to generate the correct JavaScript import statements, file-name must be the relative path to the file that defines the forward declared 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 integral built-in types. 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. 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 integral built-in types. 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
{
...
}
}