Value Mapping

CORBA defines a type set much larger than Lua. Therefore, many different IDL types are mapped to a single Lua type, for example all compound IDL types are mapped to Lua tables with different structures. This provides a higher degree of flexibility because a single Lua value may be a valid struct and exception value at the same time. But, this also brings problems since sometimes OiL cannot infer the correct IDL type a Lua value must be encoded when it is sent through the network. In this section, we describe the mapping OiL uses to convert IDL types to Lua types and vice-versa.

Index

  1. Basic Types
  2. Enumerations
  3. Structures
  4. Sequences
  5. Arrays
  6. Unions
  7. Interfaces
  8. TypeCodes
  9. Anys

Basic Types

All CORBA numeric types are mapped into the number type of Lua. Usually, the number type of Lua has the same precision of the double of C, but this can be changed when Lua is compiled.

All CORBA string types are mapped to the string type of Lua. Therefore, there are no constrains about the size of string values inside Lua. The char type is also mapped into the string type of Lua, but char values are strings with a single character.

The CORBA boolean type is mapped into the boolean type of Lua. However, when a value of type other than boolean is marshaled into a CORBA boolean it is mapped into true, except for nil that is mapped into false.

Enumerations

Enumeration values are mapped into a string containing the name of the enum value. For example, consider the following IDL definition:

enum WeekDay{ sun, mon, tue, wed, thu, fri, sat };
interface Calendar {
  void setday(in WeekDay day);
  void nextday();
  WeekDay today();
};

Then the following code tests whether the result of operation today returns the enumeration value sun

if calendar:today() == "sun" then
  print "Go home and rest!"
end

On the other hand, both strings and numbers can be marshaled into enum values if they match the name or the numeric value of an enumeration. For example, the following lines produce the same results.

calendar:setday("sun")
calendar:setday(0)

Structures

Structure values are mapped to a Lua table that maps all the name of the fields defined in the IDL definition into their proper values. For example, consider the following IDL definition.

struct Book {
  long   isbn;
  string title;
  string author;
  string publisher;
  long   year;
};
interface Library {
  Book get_book(in long isbn);
  void add_book(in Book book);
};

Then, the following code prints the fields of the Book structure.

book = library:get_book(book_id)
print("ISBN:"     , book.isbn     )
print("Title:"    , book.title    )
print("Author:"   , book.author   )
print("Publisher:", book.publisher)
print("Year:"     , book.year     )

In order to marshal a struct value, you must provide a value (e.g. a table) that index all the struct fields to its proper values. For instance, to marshal a structure of type Book you could use one of the following possibilities.

-- table containing the actual values
library:add_book({
  isbn      = 8590379817,
  title     = "Programming in Lua",
  author    = "Roberto Ierusalimschy",
  publisher = "Lua.org",
  year      = 2003,
})

-- userdata that index field values into its proper values
local books = database:query([[
  SELECT isbn, title, author, publisher, year
  FROM Books
  WHERE year > 2000
]])
for _, book in ipairs(books) do
  -- book is a userdata used to get
  -- values from a database query
  library:add_book(book)
end

Sequences

Sequence values are mapped to Lua arrays containing the values of the sequence. Actually, Lua arrays are tables mapping values at integer indices ranging from 1 to the number of elements in the array. In order to marshal sequences, you must create a Lua array that contains the values of the sequence. For example, suppose the following IDL definition.

interface NumberCompare {
  double max(in sequence<long> numbers);
};

Then, the following code invokes max with a sequence of long values.

print("Maximum is:", compare:max({64,33,52,15,29}))

If a sequence contains nil values then it must define field n that contains the number of elements of the sequence. All sequences creates by OiL provides this field n.

For better performance, sequences of octets are mapped to a string containing the character value corresponding to each octet of the sequence. This is possible because Lua strings can contain any character value, including the NULL character.

Arrays

Array values are mapped similarly to the way sequences are. However, Lua arrays marshaled as CORBA arrays must have the exact number of elements as defined by the CORBA array type.

Again, for better performance, arrays of octets are mapped to a string containing the character values corresponding to each octet of the array.

Unions

Union values are mapped to tables containing information about the discriminator and the actual union value. Union tables contain the following fields:

_switch = <value of the discriminator>
_value = <value of the selected union field>
_field = <name of the selected union field>

Additionally, union values offer shortcuts to its value: you can access the value of the union by the selected union field name, for example, suppose the following IDL union definition:

union MyUnion switch(short) {
      case 1: string onechoice;
      case 2: long otherchoice;
      case 3: double anotherchoice;
}

Then you can access the union value like the following:

local union = myobject:get_unionvalue()
if union._switch == 1 then
      print(union.onechoice)
elseif union._switch == 2 then
      print(union.otherchoice)
elseif union._switch == 3 then
      print(union.anotherchoice)
end

In order to marshal some union value you must create a table that matches one of the following options:

_switch = <value of the discriminator>
_value = <value of the selected union field>

_field = <name of the selected union field>
_value = <value of the selected union field>

You can also use the union shortcuts to create union values, so it is possible to place the union value at the union field name, so OiL will assume the corresponding discriminator. For example, suppose the IDL union described above, than you could create a matching union value using one of the following options (the formats are presented in order of precedence):

union = { _switch = 0 }
union = { _switch = 2, _value = 1024 }
union = { _switch = 3, anotherchoice = 0.625 }
union = { _field = "otherchoice", _value = 1024 }
union = { _field = "anotherchoice", anotherchoice = 0.625 }
union = { onechoice = "my string value" }

Interfaces

Unlike other IDL types, when an object is transfered through the network it is not copied. Instead, only a reference for the object is sent through the network. Therefore, interface types actually denote object references, which are mapped to Lua as tables that have an opaque structure. Both servants and proxies are object references, but the former is a reference for a local object and the later is typically a reference for a remote one. Additionally, both servants and proxies provide methods matching the attributes and methods of the interface they implement, which can be called to perform invocation on the referenced object. The following sections, describe the mappings of operations, attributes and exceptions defined in IDL interfaces.

Operations

Operations are mapped to methods that receive as parameters the values of all in and inout parameters of the operation. The values of these parameters are in the same relative order as in the IDL definition. Furthermore, these methods return the value returned by the IDL operation followed by the output values of each out and inout parameters. Again, the values of these parameters are returned in the same relative order as defined in the IDL definition. Operations, which return type is void, does not return an additional value before the output value of the out and inout parameters.

For example, suppose the following IDL interface, which defines an operation to connect a channel to a given host defined by parameter host.

broker:loadidl[[
  interface Channel {
    boolean connect(in string host, inout long attempts, out long port);
  };
]]

The code below invokes operation connect on a proxy that implements the Channel interface. The value of parameter host is the string "localhost", the value of parameter attempts is number 3. The returned value is stored in variable success. The resulting values of inout parameter attempts and out parameter port are stored in the local variables attempts and port, respectively.

local proxy = broker:newproxy(oil.readIOR("channel.ior"), "Channel")

local success, attempts, port = proxy:connect("localhost", 3)

if success then
  io.write("successfully connect to host ", host,
           " at port ", port,
           " after ", attempts, " attempts.")
else
  io.write("unable to connect to a port of host ", host,
           " after 3 attempts.")
end

The same mappings is valid for implementation of servants. Therefore, a possible implementation for the interface above would be

local impl = {}
function impl:connect(host, max)
  for actual = 1, max do
    local port = self:getnextport()
    conn = socket.connect(host, port)
    if conn then
      self.conn = conn
      return true,   -- returned value
             actual, -- value of attempts parameter
             port    -- value of port parameter
    end
  end
  return false, -- returned value
         max,   -- value of parameter 'attempts'
         -1     -- value of parameter 'port'
end

broker:newobject(impl, nil, "Channel")

Attributes

Generally, attributes in an IDL are mapped as a pair of _get_ and _set_ methods of the Lua object. For example, suppose the following IDL definition.

broker:loadidl[[
  interface Hello {
    attribute long count;
    void say_hello_to(in string name);
  };
]]

Then, all references to objects (e.g. proxies) of interface Hello provides operations _get_count() and _set_count(value), which are used to get and change the value of the attribute count, respectively. This is illustrated in the example below.

local proxy = broker:newproxy(oil.readIOR("hello.ior"), "Hello")

print("Value of count is:", proxy:_get_count())
proxy:_set_count(0)

Similarly, an object that is registered as a servant with interface Hello could provide the following implementation.

local impl = { names = {} }
function impl:_get_count()
  return #self.names
end
function impl:_set_count(value)
  if value < #self.names then
    for i = value+1, #self.names do
      self.names[i] = nil
    end
  end
end
function impl:say_hello_to(name)
  self.names[#self.names+1] = name
  print("Hello "..name.."!")
end

broker:newobject(impl, nil, "Hello")

Alternatively, an attribute is also mapped as a field of the Lua object. However, this is only valid for the implementation object of a servant. This means that attributes cannot be accessed as fields of proxies. The example below, shows an alternative implementation for a servant with interface Hello.

local impl = { count = 0 }
function impl:say_hello_to(name)
  self.count = self.count + 1
  print("Hello "..name.."!")
end

broker:newobject(impl, nil, "Hello")

Exceptions

Exceptions are mapped to Lua errors, but instead of a textual message, exceptions are errors that carry a table as the error value. Like structures, this table maps the name of fields defined in the IDL to their values. However, it contains an additional field at index 1 that contains the repository ID of the exception. For example, suppose the following IDL exception definition.

broker:loadidl[[
  exception CompilationError {
    string script;
    string message;
  };
  exception ExecutionError {
    string script;
    string message;
  };

  interface RemoteLua {
    void run_script(in string path)
      raises (CompilationError, ExecutionError);
  };
]]

A valid exception value for this definition would be

local impl = {}
function impl:run_script(path)
  local chunk, errmsg = loadfile(path)
  if not chunk then
    -- raise CompilationError exception
    error{ "IDL:CompilationError:1.0",
      script = path,
      message = errmsg,
    }
  end
  local success, errmsg = pcall(chunk)
  if not success then
    -- raise ExecutionError exception
    error{ "IDL:ExecutionError:1.0",
      script = path,
      message = errmsg,
    }
  end
end

broker:newobject(impl, nil, "RemoteLua")

Therefore, to catch an exception you shall use the same mechanisms used to catch Lua errors, as illustrated in the example below.

local remoteLua = broker:newproxy(oil.readfrom("ref.ior"), "RemoteLua")

local success, errmsg = oil.pcall(remoteLua.run_script, remoteLua, "/usr/lua/scripts/error.lua")
if not success then
  print("Caught an exception with repID:", errmsg[1])
  print("  Failed scrpt:", errmsg.script)
  print("  Error message:", errmsg.message)
end

The use of oil.pcall instead of the ordinary pcall is due to current limitations of the OiL implementation, as discussed in section Cooperative Multithreading.

TypeCodes

TypeCode values are mapped to a table containing the fields that describes the type accordingly to its representation in CORBA's CDR. Basic types are represented by pre-defined tables exported by module oil.corba.idl, as listed below.

DescriptorCORBA type
oil.corba.idl.void void
oil.corba.idl.short short
oil.corba.idl.long long
oil.corba.idl.ushort unsigned short
oil.corba.idl.ulong unsigned long
oil.corba.idl.float float
oil.corba.idl.double double
oil.corba.idl.boolean boolean
oil.corba.idl.char char
oil.corba.idl.octet octet
oil.corba.idl.any any
oil.corba.idl.TypeCodeTypeCode
oil.corba.idl.string string
oil.corba.idl.object CORBA::Object

Constructed types follow a pre-defined structure. Module oil.corba.idl provides auxiliary functions to construct TypeCodes for constructed types, as illustrated below:

Enumeration

local MyEnum = oil.corba.idl.enum{
  -- typedef information
  repID = "IDL:MyEnum:1.0",
  name = "MyEnum",
  -- list of enumeration values
  "text", "number", "logic",
}

Structure

local MyStruct = oil.corba.idl.struct{
  -- typedef information
  repID = "IDL:MyStruct:1.0",
  name = "MyStruct",
  -- list of struct fields
  { type = oil.corba.idl.string , name = "text"   },
  { type = oil.corba.idl.double , name = "number" },
  { type = oil.corba.idl.boolean, name = "logic"  },
}

Sequence

local MyStringSeq  = oil.corba.idl.sequence{ oil.corba.idl.string }
local MyBoundedSeq = oil.corba.idl.sequence{ oil.corba.idl.string, maxlength = 100 }

Array

local MyStringArray = oil.corba.idl.array{ oil.corba.idl.string, length = 100 }

Union

local MyUnion = oil.corba.idl.union{
  -- typedef information
  repID = "IDL:MyUnion:1.0",
  name = "MyUnion",
  -- union discriminant definition
  switch = oil.corba.idl.short,
  default = 0, -- first option is the default
  -- list of union options
  { label = 1, type = oil.corba.idl.string , name = "text"   },
  { label = 2, type = oil.corba.idl.double , name = "number" },
  { label = 3, type = oil.corba.idl.boolean, name = "logic"  },
}

Exception

local MyExcept = oil.corba.idl.union{
  -- typedef information
  repID = "IDL:MyExcept:1.0",
  name = "MyExcept",
  -- list of exception members
  { type = oil.corba.idl.string, name = "message" },
  { type = oil.corba.idl.string, name = "file"    },
  { type = oil.corba.idl.long  , name = "line"    },
}

Object

local HelloObjectRef = oil.corba.idl.Object{
  repID = "IDL:Hello:1.0",
  name = "Hello",
}

Anys

Some Lua values can be automatically mapped to anys because OiL defines a default mapping for them. This default mapping is illustrated in the table below.

Type of Lua Value
Default Type of Any
nil null
number double
string string
booleanboolean

To marshal other values as anys or to encode any of the values of the table above with a different IDL type than the default one, you must create a table to represent the any. This table must contain a field _anyval with the actual value of the any and a field _anytype with the type of the any. Alternatively, the application may create values with custom metatables. In that case, the any type is defined by the field __type of the custom metatable. Finally, the type of the any can be defined as the metatable of the table containing the actual value. For illustrative purpose, the code below creates different anys using the different possibilities allowed by OiL.

-- any value containing a long
local longany = setmetatable({ _anyval = 4 }, oil.corba.idl.long)

-- any value containing a struct
local MyStruct = oil.corba.idl.struct{
  repID = "IDL:MyStruct:1.0",
  name = "MyStruct",
  { type = oil.corba.idl.string , name = "text"   },
  { type = oil.corba.idl.double , name = "number" },
  { type = oil.corba.idl.boolean, name = "logic"  },
}
local structany1 = setmetatable({
  text = "some text",
  number = 123,
  logic = true,
}, MyStruct)

-- any value containing a struct with a custom metatable
local structany2 = setmetatable({}, {
  __index = structany1,
  __type  = MyStruct,
})

-- any value containing a sequence of anys
local myanyseq = { longany, structany1, structany2 }
local seqany = setmetatable(myanyseq, oil.corba.idl.sequence{oil.corba.idl.any})

Copyright (C) 2004-2008 Tecgraf, PUC-Rio

This project is currently being maintained by Tecgraf at PUC-Rio.