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
Overview
Slice has the concept of a metadata directive. For example:
["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:
["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:
[["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.
General Metadata Directives
amd
amdThis 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.
deprecated[:message]
deprecated[:message]deprecate[:message]
This directive allows you to emit a deprecation warning for Slice constructs.
format
formatThis 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.
marshaled-result
marshaled-resultThis 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:
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:
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:
// Generated server-side codeclass 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:
GridServant::GetGridMarshaledResultGridServant::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:
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:
// Generated server-side codepublic 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:
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:
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:
// Generated server-side codepublic 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:
@Overridepublic GridIntf.GetGridMarshaledResult getGrid(com.zeroc.Ice.Current current) { synchronized (_mutex) { // marshal _grid field within synchronization return new GridIntf.GetGridMarshaledResult(_grid, current); }}suppress-warning
suppress-warningThis 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 Category | Description |
|---|---|
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. |
oneway
onewayThe 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.
Language-Specific Metadata Directives
The metadata directives for C++ uses the cpp prefix.
cpp:array
cpp:arrayThis directive applies to sequence parameters in operations. It directs the Slice compiler to map these parameters to pairs of pointers.
cpp:const
cpp:constThis directive applies to operations. It directs the Slice compiler to create a const pure virtual member function for the skeleton class.
cpp:custom-print
cpp:custom-printThis 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.
cpp:dll-export:SYMBOL
cpp:dll-export:SYMBOLThis 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:
[["cpp:dll-export:WIDGET_API"]]results in the following additional code being generated into Widget.h:
#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#endifThe 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:
#if defined(_MSC_VER)# define ICE_DECLSPEC_EXPORT __declspec(dllexport)# define ICE_DECLSPEC_IMPORT __declspec(dllimport)With GCC and clang, they are defined as:
#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.
cpp:doxygen:include:c++-header
cpp:doxygen:include:c++-headerThis file directive instructs the Slice compiler to generate a doc-comment with @headerfile and the specified C++ header for all generated C++ classes.
cpp:header-ext:c++-ext
cpp:header-ext:c++-extThis file directive allows you to use a file extension for C++ header files other than the default .h extension.
cpp:identifier:c++-identifier
cpp:identifier:c++-identifierThis directive applies to all Slice constructs, and instructs the Slice compiler to use the specified c++-identifier.
For example:
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.
cpp:ice_print
cpp:ice_printThis directive applies to exceptions. It is a deprecated alias for cpp:custom-print.
cpp:include:c++-header
cpp:include:c++-headerThis file directive allows you to inject additional #include directives into the generated C++ header file. This is useful when using the cpp:type metadata.
cpp:source-ext:c++-ext
cpp:source-ext:c++-extThis file directive allows you to use a file extension for C++ source files other than the default .cpp extension.
cpp:source-include:c++-header
cpp:source-include:c++-headerThis 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.
cpp:type:c++-type
cpp:type:c++-typeThis directive applies to sequences and dictionaries. It directs the Slice compiler to map the Slice type or parameter to the provided C++ type.
cpp:type:string and cpp:type:wstring
cpp:type:string and cpp:type:wstringThese 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.
module A{ ["cpp:type:wstring"] struct Struct1 { string s1; // Maps to std::wstring ["cpp:type:string"] string s2; // Maps to std::string }}cpp:view-type:c++-view-type
cpp:view-type:c++-view-typeThis 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.
cs:attribute
cs:attributeThis directive applies to enums, enumerators, constants and fields. It injects a C# attribute definition into the generated code.
cs:class
cs:classThis directive applies to Slice structures. It directs the Slice compiler to emit a C# class instead of a structure.
cs:generic:List, cs:generic:LinkedList, cs:generic:Queue and cs:generic:Stack
cs:generic:List, cs:generic:LinkedList, cs:generic:Queue and cs:generic:StackThese directives apply to sequences and map them to the specified sequence type.
cs:generic:SortedDictionary and cs:generic:SortedList
cs:generic:SortedDictionary and cs:generic:SortedListThis directive applies to dictionaries and maps them to the specified type.
cs:generic:csharp-custom-type
cs:generic:csharp-custom-typeThis directive applies to sequences and allows you map them to custom types.
cs:identifier:csharp-identifier
cs:identifier:csharp-identifierThis directive applies to all Slice constructs, and instructs the Slice compiler to use the specified csharp-identifier.
For example:
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).
cs:internal
cs:internalThis 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.
cs:namespace:enclosing-csharp-namespace
cs:namespace:enclosing-csharp-namespaceThis 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.
cs:property
cs:propertyThis directive applies to Slice structures, classes, and exceptions. It directs the Slice compiler to map Slice fields to C# properties instead of C# fields.
cs:readonly
cs:readonlyThis 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.
java:buffer
java:bufferThis directive applies to sequences of certain primitive types. It directs the Slice compiler to map the sequence to a subclass of java.nio.Buffer.
java:getset
java:getsetThis directive applies to fields, structures, classes, and exceptions. It adds accessor and modifier methods (JavaBean methods) for fields.
java:identifier:java-identifier
java:identifier:java-identifierThis directive applies to all Slice constructs, and instructs the Slice compiler to use the specified java-identifier.
For example:
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.
java:package:enclosing-java-package
java:package:enclosing-java-packageThis 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.
java:serializable
java:serializableThis 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.
java:serialVersionUID
java:serialVersionUIDThe 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.
java:type:<instance-type[:formal-type]>
java:type:<instance-type[:formal-type]>This directive allows you to use custom types for sequences and dictionaries.
java:UserException
java:UserExceptionThis 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.
js:defined-in:file-name
js:defined-in:file-nameThis 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.
js:identifier:javascript-identifier
js:identifier:javascript-identifierThis directive applies to all Slice constructs, and instructs the Slice compiler to use the specified javascript-identifier.
For example:
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.
js:module:module-name
js:module:module-nameThis 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:
// 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.
matlab:identifier:matlab-identifier
matlab:identifier:matlab-identifierThis directive applies to all Slice constructs, and instructs the Slice compiler to use the specified matlab-identifier.
For example:
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.
python:array.array
python:array.arrayInstructs 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..
python:identifier:<identifier>
python:identifier:<identifier>This directive applies to all Slice constructs, and instructs the Slice compiler to use the specified python-identifier.
For example:
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).
python:list
python:listInstruct the Ice for Python runtime to unmarshal a sequence as a list.
python:memoryview:<factory>(:type-hint)
python:memoryview:<factory>(:type-hint)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..
python:numpy.ndarray
python:numpy.ndarrayInstructs 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..
python:tuple
python:tupleInstructs the Ice for Python runtime to unmarshal a sequence as a Python tuple.
The metadata directives for Swift uses the swift prefix.
swift:attribute:attribute
swift:attribute:attributeThis directive adds the specified Swift attribute to the generated code. It can be used with classes, structs, enums, and exceptions.
swift:identifier:swift-identifier
swift:identifier:swift-identifierThis directive applies to all Slice constructs, and instructs the Slice compiler to use the specified swift-identifier.
For example:
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).
swift:module:module:prefix
swift:module:module:prefixThis 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:
["swift:module:Mod:Pre"] module Test{ ...}is equivalent to:
module Mod{ module Pre { ... }}