Converting .ice into .slice

Learn how to convert .ice definitions into .slice definitions.

You need to convert existing Slice definitions into new Slice definitions when you want to use IceRPC to build a client or server application that interoperates with your existing Ice applications.

This page shows how to convert each construct in your .ice file into the equivalent construct in your new .slice file. This equivalent construct is naturally encoded the same way—otherwise, there would be no interop.

All the .slice files used for interop with Ice must use the Slice1 compilation mode:

slice
mode = Slice1

Ice will soon provide an ice2slice tool to automate this conversion.

A few definitions in .ice files have no equivalent in the .slice syntax and as a result cannot be converted:

  • local Slice
    The .slice syntax does not support local definitions.
  • constants
    You cannot define constants in .slice files.
  • optional fields and parameters whose type is a class or references a class.
  • default values for numeric and string fields in classes, exceptions, and structs.
  • operations on classes (deprecated in Ice 3.7)
  • interface parameters and fields passed "by value" (deprecated in Ice 3.7)
.ice syntax
class Vehicle
{
string color;
}
class Bicycle extends Vehicle
{
int speedCount;
optional(1) bool rented;
}
Same classes with the .slice syntax
class Vehicle {
color: string
}
class Bicycle : Vehicle {
speedCount: int32
tag(1) rented: bool?
}
.ice syntax
dictionary<string, int> StringIntDict;
Same dictionary with the .slice syntax
typealias StringIntDict = Dictionary<string, int32>
.ice syntax
enum Fruit
{
Apple,
Orange,
Strawberry = 3,
Pineapple
}
Same enum with the .slice syntax
enum Fruit {
Apple
Orange
Strawberry = 3
Pineapple
}

A .slice enum can be checked (the default) or unchecked (with the unchecked keyword), while the "check-ness" of a .ice enum is language-mapping dependent. When the generated code decodes a numeric value into an enumerator, and the enum has no enumerator with this numeric value, the decoding fails with a checked enum and succeeds with an unchecked enum.

.ice syntax
exception SyntaxException
{
string message;
}
exception InvalidIdentifierException
extends SyntaxException
{
string badIdentifier;
}
Same exceptions with the .slice syntax
exception SyntaxException {
message: string
}
exception InvalidIdentifierException
: SyntaxException {
badIdentifier: string
}

With the .ice syntax, an operation lists the Slice-defined exceptions it's allowed to throw in its exception specification. For example:

.ice syntax
void op(string s) throws ArgumentException, InvalidStateException, NotAvailableException;

The allowable exceptions include any exception derived from ArgumentException, InvalidStateException and NotAvailableException. If this list is empty (no throws), the operation is not allowed to throw any Slice-defined exception.

With the .slice syntax, an operation can also throw one or more exceptions. The only difference is a list of two or more exceptions must be in parenthesis:

Same operation with the .slice syntax
op(s: string) throws (ArgumentException, InvalidStateException, NotAvailableException)

Another difference between Ice and IceRPC is where the exception specifications are checked:

  • with Ice, the generated code enforces exception specifications only when decoding responses
  • with the IceRPC + Slice integration, the generated code enforces exception specifications during encoding and also during decoding.
.ice syntax
interface ChessPiece
{
void move(Position newPosition)
throws ChessException;
}
interface Pawn extends ChessPiece
{
// The returned proxy can't be null; however,
// the .ice syntax does not provide a way to
// express this constraint.
ChessPiece* promote(Kind newKind)
throws ChessException;
}
Same interfaces with the .slice syntax
interface ChessPiece {
move(newPosition: Position) throws ChessException
}
interface Pawn : ChessPiece {
// The returned chess piece proxy is non-optional.
promote(newKind: Kind) -> ChessPieceProxy
throws ChessException
}
// A Slice type encoded/decoded as a service address.
[cs::type("BoardGame.Chess.ChessPieceProxy")]
custom ChessPieceProxy

When you define an interface with the .ice syntax, you also implicitly define a Slice type with the same name. This Slice type represents a proxy to a service that implements this interface, and is encoded as the underlying service address.

With the .slice syntax, an interface is a Slice construct but not a Slice type.

As shown in the example above, when converting .ice definitions into .slice definitions, you may want to define a custom type named NameProxy that wraps a service address. Please refer to Using proxies as Slice types for details.

.ice syntax
module BoardGame
{
module Checkers {}
module Chess {}
}
Same modules with the .slice syntax
// Each .slice file must have exactly one module.
module BoardGame::Checkers
// Must be in a different file.
module BoardGame::Chess

The optional keyword in .ice files is replaced by the tag keyword in .slice files and the type of a tagged parameter or field must be marked optional (with a ? suffix) in .slice files.

.ice syntax
interface Widget
{
void spin(optional(1) int speed);
}
class Person
{
string name;
optional(1) string email;
}
Same definitions with the .slice syntax
interface Widget {
spin(tag(1) speed: int32?)
}
class Person {
name: string
tag(1) email: string?
}

With the .ice syntax, the return type of an operation can be split between a return type and out parameters, whereas with the .slice syntax, an operation has only "in" parameters but can return a tuple.

When converting .ice definitions into .slice definitions, keep in mind that Ice encodes out parameters before the return type.

.ice syntax
interface Sample
{
bool op(
string input,
out string output1,
out int output2)
}
Same interface with the .slice syntax
interface Sample {
// The .ice return type is at the end of the
// return tuple.
op(input: string) ->
(output1: string,
output2: int32,
return: bool)
}

The names of the parameters and return type elements are not encoded; as a result, you can change them freely without breaking on-the-wire compatibility.

.ice syntax.slice syntax
bool
bool
byte
uint8
double
float64
float
float32
int
int32
long
int64
short
int16
string
string
Object
AnyClass?
Object*
Ice::Object? or IceRpc::ServiceAddress?
Value
AnyClass?

With the .ice syntax, Object, Object* and Value always represent nullable parameters or fields. With the .slice syntax, the corresponding AnyClass, Ice::Object or IceRpc::ServiceAddress can be optional (with a ? suffix) or non-optional (without a ? suffix).

.ice syntax
sequence<string> StringSeq;
Same sequence with the .slice syntax
typealias StringSeq = Sequence<string>
.ice syntax
struct Position
{
int x;
int y;
}
Same struct with the .slice syntax
compact struct Position {
x: int32
y: int32
}

With the .slice syntax and the Slice1 compilation mode, all structs must be compact.

Was this page helpful?