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
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.
Descriptor | CORBA 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.TypeCode | TypeCode |
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 any
s because OiL defines a default mapping for them.
This default mapping is illustrated in the table below.
nil | null |
number | double |
string | string |
boolean | boolean |
To marshal other values as any
s 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 any
s 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})