Programming mobile devices

Download Report

Transcript Programming mobile devices

Programming mobile devices
Part II
Programming Symbian devices
with Symbian C++
Basic Symbian programming
Content of this part
•
•
•
•
•
•
Basic Data Types
Classes
Error handling
Cleanup
Panics
Libraries
Basic Data Types
• TInt, TUint integer, natural word length
• TInt8, TInt16, TInt32 integers of 8b,16b...
• TUint8, TUint16, TUint32 unsigned
integers of 8b,16b and 32b
• TInt64 64 bit integer with operators(+,-,...)
• TText8, TText16, TText characters
• TChar A character class with several
methods for manipulating
Basic Data Types
• TBool ETrue / EFalse
• TReal32, TReal64, TReal floating point
numbers (TReal64 and TReal map to
double)
• TAny like void, used in situations other
than return values of functions
Classes
• 4 main gategories with agreed prefixes
• T for data type classes (e.g. TInt)
• C for Heap allocated classes (these inherit
from CBase class)
• R for Resource classes (which reserve
resources)
• M for Interface classes
Data Type Classes
• Encapsulate basic data types, such as
TInt and int
• Usually have methods for manipulating the
data
• e.g. TChar.loweCase()
• Comparison usually implemented
• Used instead of basic data types
Heap Classes
• All inherit from the CBase class
• Instantiated on the heap (using the new
operation)
• CBase has a virtual destructor (A heap
class hence has to implement it)
• zero initialization to all members
Resource classes
•
•
•
•
•
Control resources owned by someone else
e.g. client in a client/server communication
usually allocated on the stack
for example: RFile, RSocket, RThread
Pointers to a resource, deleting the class
does not usually delete the resource
• A resource has a reference count (How
many pointers point to it)
Interface classes
•
•
•
•
•
M for Mixin
Abstract classes that define interfaces
No member variables
Methods usually pure virtual
Note: Even though multiple inheritance is
possible in C++, in Symbian programminig
it is usually used only through interfaces.
• Basically: use interfaces as in Java
Errors and Exceptions
• Handling errors and exceptions is utterly
important in mobile phone apps.
• Sources of exceptions are vast; out of
memory, incoming call, empty battery...
• Users have high expectations of
robustness of the phones and the software
on them.
Return Codes
• Function's return value tells the result of
excecution
• KErrNone -success
• Others include an error code that tells
what went wrong, e.g. KErrNoMemory
• Used extensively in the APIs
• Still, not the best possible error handling
mechanism. why ?
Leave/Trap
• Corresponding to Javas try-catch and C++
throw-catch, Symbian has a mechanism
called leave-trap
• leave is like throw in java
• trap is similar to catch
• The mechanism is also familiar, a leave
"bubbles" up the call stack like in java until
it is handled by a trap (if ever)
Leave
• Programmer can call
User::Leave(KErrorCode); which
corresponds to throw(Exception) in java
• The execution of that function is stopped
immediately and the control returns to the
calling code.
• Also an API function can (and most do)
leave.
Trap
• Trapping is done with a marco TRAP (or
TRAPD).
• The code is executed onward from the
next line after a TRAP (stops "bubbling")
• The macro takes 2 parameters: a TInt to
store the leave code and the name of the
function to be executed
TInt LeaveResult;
TRAP(LeaveResult, func1());
Example code
void func1()
{
TInt result;
result = somefunc();
if(result)
{
User::Leave(KSomeError);
}
}
void TestFunc()
{
TInt LeaveResult;
TRAP(LeaveResult, func1());
if(LeaveResult)
{
// Leave happened in func1() that has to be handled
}
}
Example code
void func1()
{
TInt result;
result = somefunc();
if(result)
{
User::Leave(KSomeError);
}
}
void TestFunc()
{
TRAPD(LeaveResult, func1()); // Defines LeaveResult
if(LeaveResult)
{
// Leave happened in func1() that has to be handled
}
}
Trap return values
• If leave does not occur KErrNone, integer
0 is returned.
• In trap, the return value of the function can
also be saved:
TRAPD(LeaveResult, resval = func1());
Leave functions
• User:: namespace has a set of static API
leave functions:
• User::Leave(leavecode);
• User::LeaveNoMemory(); //KErrNoMemory
• User::LeaveIfError(leavecode); // If
leavecode is negatice, leave with error as
the reason.
• User::LeaveIfNull(TAny *pointer);
//KErrNoMemory as the reason
The L suffix
• Symbian uses a convention to use a suffix
L in functions that may leave
• This is to signal the programmer that it
may be necessary to trap such a function
• Unless trapped, the code to follow might
not be executed
A couple words about memory
• In contrast to Java, in C++ you must
actively free any memory you have
allocated.
• Not doing so will result in leaking of
memory.
• Usually you use new to generate new
objects and delete to destroy them
• Also, it is a good practice to set pointers to
null after they no longer point to an object
Leaving and memory
What if?
void func1()
{
CSomeObject *testObj = new CSomeObject;
TInt result = testObj.somefunc(); // which may leave
delete testObj;
testObj = null;
}
void TestFunc()
{
TRAPD(LeaveResult, func1()); // Defines LeaveResult
if(LeaveResult)
{
// Leave happened in func1() that has to be handled
}
}
Cleanup
• Obviously, we have a problem
• Since leave can result in skipping some
code, we must be careful with allocated
memory not to have code that leaks
• In previous example testObj is an
automatic variable that goes out of scope
when func1 exits
• Result: the created object is left in the
memory and cannot be deleted
Cleanup stack
• The solution to our problem is something
called a cleanup stack
• It can be thought of a storage of pointers
to created objects (which might otherwise
be lost)
• Think of it as a memory accounting device
• push
• pop
Cleanup stack
• Items that must be cleaned are pushed
into the stack
• If leave occurs, these items are
automatically popped from the stack and
deleted
• If no leave occurs, you must pop the items
yourself
• Watch out for double deletion
An example
void myTestfunc()
{
CTestObj *obj = new CTestObj();
CleanupStack::PushL(obj);
MyOtherFuncL(); // may leave
CleanupStack::Pop();
delete obj;
}
An example
void myTestfunc()
{
CTestObj *obj = new CTestObj();
CleanupStack::PushL(obj);
MyOtherFuncL(); // may leave
CleanupStack::PopAndDestroy();
delete obj; // WRONG! obj already deleted
}
Creating a cleanup stack
• Cleanup stack is automatically created for
GUI applications
• For your own threads you have to create
the cleanup stac yourself
• CTrapCleanup *clstack =
CTrapCleanup::New()
• remember to delete the stac when finished
• delete clstack;
Object types
• PushL(CBase *)
– delete is called when object is popped
– calls the destructor of the derived class
• PushL(TAny *)
– User::Free() is called when object is popped
– destructor is not called
What if PushL leaves?
• What if there is no space in the stack?
• Symbian always keeps at least one free
spot on the cleanup stack.
• If the PushL leaves, the pushed item will
still be pushed to the stack
• Allocating the next slot may fail resulting
PushL leaving
Complex cleanup
• Sometimes we have objects that require
more complex cleanup than just delete
• PushL(TCleanupItem userCleanup)
• TCleanupItem is a wrapper class for a
function that handles cleanup
Other cleanup functions
• CleanupClosePushL <class T>(T& obj)
• Pop calls obj.Close()
• used mainly for R classes, which require
Close() to be called
• CleanupReleasePushL<class T>(T& obj)
• calls Release()
Other cleanup functions
• CleanupDeletePushL <class T>(T& obj)
• Using templates, the pop function calls the
actual destructor of the object regardless
of the type of the object
• Hence, the object doesn't have to be
derived from CBase
• you should use this for all non CBase
derived classes that have a destructor.
Templates
• Were created to make function code reuse possible (instead of just overloading
functions)
• Templates basically achieve the same as a
superclass in Java
LC functions
• LC suffix denotes functions that
automatically push a reference of a
created object to the cleanup stack
• For example: User::AllocLC(100) allocates
memory and pushes a pointer to the
cleanupstack
• No need for User::PushL(...)
Leave in object creation
• The new operation may fail due to
inadequate memory.
• You can use a ELeave when calling the
constructor to assure that leave occurs if
object creation (new operation) fails
• CMyObject *obj =
new (ELeave) CMyObject;
• Constructor itself should never leave
(why?)
Two phase constructors
• After creating the object with new we call a
constructor function which may leave
• This call can be trapped
• Often we name this function as ConstructL
ConstructL
void testL()
{
CTestObj *o = new (ELeave) CTestObj;
CleanupStack::PushL(o);
o->ConstructL();
...
}
Panics
• Don' t panic
• The operating system may do that.
• An unrecoverable error that causes the
thread to finish
• User::panic(const TDes &Category, TInt
reason)
• On the device the execution ends and the
device shows a dialog box
Libraries
• static (linked at build)
• DLLs (Dynamic Link Library)
– loaded and linked at runtime
– one copy is shared by multiple programs