Prev: m Runtime System
m Data Types and Values
Scalars
m is not a strongly typed language. As such, it allows variables and array elements to assume any valid
m data type (number, string, array, etc.) and to change that type at any time (positively put,
m is "fully polymorphic"). Consequently, all variables and array elements in
m are of type
Runtime::Value. Internally, this is a union of eight bytes aligned on an eight byte boundary.
The following table summarizes the methods to set and get a Runtime::Value:
| m type | C++ type | Getter | Setter/Creator |
| Number | TReal | GetNumberL() | SetNumber() |
| Number | TInt | GetIntL() | SetNumber() |
| Number | TTime | GetTimeL() | SetTime() |
| Number | TTimeIntervalMicroSeconds32 | GetTimeoutL() | SetTimeout() |
| Number | TUid | GetUidL() | |
| Boolean | TBool | GetBooleanL() | SetBoolean() |
| String | Runtime::String | GetStringL() | NewStringL() |
| String | TPtrC | GetPtrCL() | |
| Array | Runtime::Array | GetArrayL() | NewArrayL() |
| null | | IsNull() | SetNull() |
| |
For most datatypes, there are also constructors or equivalent static methods, e.g. Runtime::BooleanValue.
The getters all leave if the type of the value is not the requested one. You can use IsNumber(), IsString() etc. to check for the type of a value.
GetTimeoutL() and SetTimeout() convert a millisecond number to a microsecond timeout value and vice versa. GetTimeoutL() also checks for the number not exceeding 231-1 microseconds (2147483 milliseconds).
GetUidL() correctly handles the signed/unsigned problems caused by Symbian's inconsistent definition of the TUid type: numbers in the ranges 0x80000000 to 0xffffffff and -0x7fffffff to -1 are both mapped to the correct TUid instance.
World Server Example, Continued
Returning to our world server example, we can already count the function calls and implement
world.count with the above table:
// count this call
Runtime::Value &calls = runtime->GetVariableL(WORLD, CALLS);
calls.SetNumber(calls.GetNumberL() + 1);
switch (index) {
case CountFunction:
// directly set the result
result.SetNumber(server.NumberCities());
break;
|
Furthermore, even though we don't know yet how to create the m array containing a city's data, we can declare the following function which we will need to create the m result of our functions:
/*
Return the current city (defined by this->id) as an associative m
array.
@param error the outcome of the world server call: KErrNone if the
city exists, KErrNotFound if the city does not exist; any other code
indicates an error.
@return the m array with the city's data.
*/
Runtime::Value GetCurrentCityL(TInt error) {
// @todo
}
|
Strings
Strings in m are immutable arrays of 16-bit characters, just as in Symbian. They are created via one of the runtime->NewStringL() overloads. For instance, the following fragment creates four times the string "NMI":
Runtime::Value a = runtime->NewStringL("NMI");
Runtime::Value b = runtime->NewStringL(_L("NMI"));
Runtime::Value c = runtime->NewStringL(_L8("NMI"));
Runtime::Value d = runtime->NewStringL(3);
TText* s = d.GetStringL().GetBuf();
s++ = 'N'; s++ = 'M'; s++ = 'I';
|
To read a string, use Runtime::Value::GetPtrCL(), which returns a Symbian TPtrC, and leaves if the value is not a string:
Runtime::Value a = runtime->NewStringL("NMI");
TBuf<32> buf;
buf.Format(_L("a=%S"), &a.GetPtrCL()); // buf = "a=NMI"
|
See the struct Runtime::String definition in Runtime.h for additional functions on m string values.
World Server Example, Continued
We can now implement all functions of our world server module:
case FindFunction:
{
// get the first argument, leaving if it is not a string
TPtrC city(params[0].GetPtrCL());
result = GetCurrentCityL
(paramCount == 2 && !params[1].IsNull()
// with country (2nd parameter which is not null)
? server.Find(id, city, params[1].GetPtrCL())
// without country (no 2nd parameter, or it is null)
: server.FindCity(id, city));
}
break;
case FirstFunction:
result = GetCurrentCityL(server.FirstCity(id));
break;
case HomeFunction:
result = GetCurrentCityL(server.Home(id));
break;
case NextFunction:
result = GetCurrentCityL(server.NextCity(id));
break;
|
In the implementation of world.find, params[0].GetPtrCL() obtains the first parameter passed to the function as a string, leaving if it is not a string. We then check whether to call WorldServer::Find or WorldServer::FindCity, based on the presence and the value of the second parameter params[1].
Note that, in conjunction with the parameter count range set via runtime->AddNativeFunctionL(), all invalid parameters are catched, causing the function to leave. The m runtime system will convert the Symbian leave into an m exception.
Arrays
Arrays are a fundamental data structure in m: since m values are polymorphic, arrays also serve to represent flat or arbitrarily complex and even recursive data structures.
Arrays are created via one of the runtime->NewArrayL() overloads. For instance, the following fragment creates the m array ["The", "answer", "is", 42]:
Runtime::Value a = runtime->NewArrayL();
Runtime::Array &array = a.GetArrayL();
array.AppendL(runtime->NewStringL("The"));
array.AppendL(runtime->NewStringL("answer"));
array.AppendL(runtime->NewStringL("is"));
array.AppendL(Runtime::Value(42));
|
Since we know there are four elements in the array, it is slightly more efficient to pre-allocate space for exactly four elements. Also, we can directly obtain a pointer to the new array, omitting the GetArrayL() call and the unnecessary check it performs:
Runtime::Array *array;
Runtime::Value a = runtime->NewArrayL(array, 4);
array->AppendL(runtime->NewStringL("The"));
...
|
To access array elements, there are a number of functions. All functions perform array boundary checking, leaving if the index is out of bounds. The following fragment changes the above array into ["The", "question", "is", "unknown"]:
array->SetL(1, runtime->NewStringL("question"));
array->SetL(3, runtime->NewStringL("unknown"));
|
To get or set values with string keys, use the corresponding overloads:
array->SetL(_L("extra"), runtime->NewStringL("so far"));
runtime->WriteL(array->GetL(_L("extra")));
|
Runtime::Array::SetL with a string key appends an element if the corresponding key does not exist yet, so the array is now equivalent to ["The", "question", "is", "unknown", "extra":"so far"]. The second line simply writes "so far" to the m process standard output.
A Runtime::Array internally consists of two data structures: a lazily created hash table maintaining the relationships between string keys and array indices, and the actual flat array of values. The flat array is of type Runtime::ValueArray and can be accessed directly via Runtime::Array::GetValues(). If you perform a lot of operations only on the flat array, it may be more efficient to operate directly on it.
For instance, the following function computes the scalar product of two arrays:
Runtime::Value DotProductL(const Runtime::Value &a,
const Runtime::Value &b)
{
Runtime::ValueArray &aa = a.GetArrayL().GetValues(),
&ab = b.GetArrayL().GetValues();
TInt len = aa.Length();
// check the two arrays have the same length
if (ab.Length() != len) User::Leave(KErrArgument);
TReal s = 0;
for (TInt i = 0; i < len; i++)
s += aa.NumberL(i) * ab.NumberL(i);
return Runtime::Value(s);
}
|
The calls to NumberL() perform array boundary checking. Since we know the boundaries are not violated, we can directly access the flat array as an array of Runtime::Value:
Runtime::Value *va = aa.GetPtr(), *vb = ab.GetPtr();
TReal s = 0;
for (TInt i = 0; i < len; i++)
s += va[i].GetNumberL() * vb[i].GetNumberL();
|
See the struct Runtime::Array and struct Runtime::ValueArray definitions for additional functions on m arrays.
World Server Example, Continued
We can now finish our module by implementing
GetCurrentCityL. Remember that it, depending on the outcome of a call, should either return an associative array with the elements we specified, or
null, or leave with an error.
Runtime::Value GetCurrentCityL(TInt error) {
Runtime::Value result;
if (error == KErrNone) { // city exists
TCityData city;
User::LeaveIfError(server.CityData(city, id));
TCountryData country;
User::LeaveIfError(server.CountryData(country, id));
// create an array, pre-allocating for eight elements
Runtime::Array *array;
result = runtime->NewArrayL(array, 8);
// append a string with the city name
array->SetL(_L("name"), runtime->NewStringL(city.iCity));
// append a string with the nation code
array->SetL(_L("nat"), runtime->NewStringL(country.iNatCode));
// append a string with the area code
array->SetL(_L("area"), runtime->NewStringL(city.iAreaCode));
// append a number with the UTC offset
array->SetL(_L("utcofs"),
Runtime::Value(city.iUniversalTimeOffset));
// append a number with the latitude in degrees
array->SetL(_L("lat"),
Runtime::Value((TReal)city.iLatLong.MinutesLat() / 60));
// append a number with the longitude in degrees
array->SetL(_L("long"),
Runtime::Value((TReal)city.iLatLong.MinutesLong()/ 60));
// append a string with the country name
array->SetL(_L("country"), runtime->NewStringL(country.iCountry));
// append a string with the capital
array->SetL(_L("capital"), runtime->NewStringL(country.iCapital));
}
else if (error == KErrNotFound) // city does not exist
result.SetNull();
else // real error
User::Leave(error);
return result;
}
|
Next: Creating, Installing and Testing the module DLL© 2004-2010 airbit AG, CH-8008 Zürich
Document AB-M-NMI-869