- •Contents
- •1.1 Fundamental Types
- •1.2 T Classes
- •1.3 C Classes
- •1.4 R Classes
- •1.5 M Classes
- •1.6 Static Classes
- •1.7 Buyer Beware
- •1.8 Summary
- •2 Leaves: Symbian OS Exceptions
- •2.1 Leaving Functions
- •2.3 Constructors and Destructors
- •2.4 Working with Leaving Functions
- •2.5 Trapping a Leave Using TRAP and TRAPD
- •2.6 LeaveScan
- •2.7 Summary
- •3 The Cleanup Stack
- •3.1 Using the Cleanup Stack
- •3.2 How Does the Cleanup Stack Work?
- •3.4 Using TCleanupItem for Customized Cleanup
- •3.5 Portability
- •3.6 An Incidental Note on the Use of Casts
- •3.7 Summary
- •5 Descriptors: Symbian OS Strings
- •5.3 Pointer Descriptors
- •5.4 Stack-Based Buffer Descriptors
- •5.5 Heap-Based Buffer Descriptors
- •5.6 Literal Descriptors
- •5.7 Summary
- •6 Good Descriptor Style
- •6.1 Descriptors as Parameters and Return Types
- •6.2 Common Descriptor Methods
- •6.3 The Use of HBufC Heap Descriptors
- •6.4 Externalizing and Internalizing Descriptors
- •6.5 The Overuse of TFileName
- •6.6 Useful Classes for Descriptor Manipulation
- •6.7 Summary
- •7 Dynamic Arrays and Buffers
- •7.1 CArrayX Classes
- •7.3 Why Use RArray Instead of CArrayX?
- •7.4 Dynamic Descriptor Arrays
- •7.5 Fixed-Length Arrays
- •7.6 Dynamic Buffers
- •7.7 Summary
- •8.1 Multitasking Basics
- •8.2 Event-Driven Multitasking
- •8.3 Working with Active Objects
- •8.4 Example Code
- •8.5 Threads Without an Active Scheduler
- •8.6 Application Code and Active Objects
- •8.7 Summary
- •9 Active Objects under the Hood
- •9.1 Active Object Basics
- •9.2 Responsibilities of an Active Object
- •9.3 Responsibilities of an Asynchronous Service Provider
- •9.4 Responsibilities of the Active Scheduler
- •9.5 Starting the Active Scheduler
- •9.6 Nesting the Active Scheduler
- •9.7 Extending the Active Scheduler
- •9.8 Cancellation
- •9.9 Request Completion
- •9.10 State Machines
- •9.11 Long-Running Tasks
- •9.14 Common Mistakes
- •9.15 Summary
- •10 Symbian OS Threads and Processes
- •10.2 Thread Priorities
- •10.3 Stopping a Running Thread
- •10.5 Exception Handling
- •10.6 Processes
- •10.7 Summary
- •11.2 How Do the Client and Server Fit Together?
- •11.3 How Do the Client and Server Communicate?
- •11.5 How Do Synchronous and Asynchronous Requests Differ?
- •11.6 How Is a Server Started?
- •11.7 How Many Connections Can a Client Have?
- •11.8 What Happens When a Client Disconnects?
- •11.9 What Happens If a Client Dies?
- •11.10 What Happens If a Server Dies?
- •11.15 How Many Outstanding Requests Can a Client Make to a Server?
- •11.16 Can Server Functionality Be Extended?
- •11.17 Example Code
- •11.18 Summary
- •12.2 Client Boilerplate Code
- •12.3 Starting the Server and Connecting to It from the Client
- •12.4 Server Startup Code
- •12.5 Server Classes
- •12.6 Server Shutdown
- •12.7 Accessing the Server
- •12.8 Summary
- •13 Binary Types
- •13.1 Symbian OS EXEs
- •13.2 Symbian OS DLLs
- •13.3 Writable Static Data
- •13.4 Thread-Local Storage
- •13.5 The DLL Loader
- •13.6 UIDs
- •13.8 Summary
- •14 ECOM
- •14.1 ECOM Architecture
- •14.2 Features of an ECOM Interface
- •14.3 Factory Methods
- •14.4 Implementing an ECOM Interface
- •14.5 Resource Files
- •14.6 Example Client Code
- •14.7 Summary
- •15 Panics
- •15.2 Good Panic Style
- •15.3 Symbian OS Panic Categories
- •15.4 Panicking Another Thread
- •15.5 Faults, Leaves and Panics
- •15.6 Summary
- •16 Bug Detection Using Assertions
- •16.3 Summary
- •17 Debug Macros and Test Classes
- •17.1 Heap-Checking Macros
- •17.2 Object Invariance Macros
- •17.3 Console Tests Using RTest
- •17.4 Summary
- •18 Compatibility
- •18.1 Forward and Backward Compatibility
- •18.2 Source Compatibility
- •18.3 Binary Compatibility
- •18.4 Preventing Compatibility Breaks
- •18.5 What Can I Change Without Breaking Binary Compatibility?
- •18.6 Best Practice: Planning for Future Changes
- •18.7 Compatibility and the Symbian OS Class Types
- •18.8 Summary
- •19 Thin Templates
- •20.1 Class Layout
- •20.3 Parameters and Return Values
- •20.4 Member Data and Functional Abstraction
- •20.5 Choosing Class, Method and Parameter Names
- •20.7 Summary
- •21 Good Code Style
- •21.1 Reduce the Size of Program Code
- •21.2 Use Heap Memory Carefully
- •21.3 Use Stack Memory Carefully
- •21.5 Optimize Late
- •21.6 Summary
- •Glossary
- •Bibliography and Online Resources
- •Index
60 |
DESCRIPTORS: SYMBIAN OS STRINGS |
5.3 Pointer Descriptors
The string data of a pointer descriptor is separate from the descriptor object itself and can be stored in ROM, on the heap or on the stack. The memory that holds the data is not ”owned” by the descriptor and is not managed through it. Thus, if it is on the heap, the memory is created, reallocated if necessary, and destroyed using a heap descriptor pointer (HBufC, described below). If a pointer descriptor is referencing a stackbased string, the memory in question will already have been allocated on the stack. The pointer descriptors themselves are usually stack-based, but they can be used on the heap, for example as a member variable of a CBase-derived class. Pointer descriptors are agnostic about where the memory they point to is actually stored.
In a non-modifiable pointer descriptor (TPtrC), the pointer to the data follows the length word, thus the total size of the descriptor object is two words. In a modifiable pointer descriptor (TPtr), it follows the maximum length word and the descriptor object is three words in length. Figure 5.1 compares the memory layouts of TPtr and TPtrC.
TPtrC |
|
|
|
|
|
|
|
|
|
|
|
iLength |
iPtr |
|
|
Hello World! |
|
12 |
|
|
|
|
|
|
|
|
ROM, heap or stack |
||
|
|
|
|
||
TDesC |
TPtrC |
|
|
||
TPtr |
|
|
|
|
|
|
|
|
|
|
|
iLength |
iMaxLength |
iPtr |
|
|
|
12 |
12 |
|
|
|
|
|
|
|
|
|
|
TDesC |
TDes |
TPtr |
Figure 5.1 Memory layouts of pointer descriptors
TPtrC
TPtrC is the equivalent of using const char* when handling strings in C. The data can be accessed but not modified: that is, the data in the descriptor is constant. All the non-modifiable operations defined in the TDesC base class are accessible to objects of type TPtrC. The class also defines a range of constructors to allow TPtrC to be constructed from another descriptor, a pointer into memory or a zero-terminated C string.
POINTER DESCRIPTORS |
61 |
// Literal descriptors are described later in this chapter _LIT(KLiteralDes, "Sixty zippers were quickly picked from the woven
jute bag");
TPtrC pangramPtr(KLiteralDes); |
// Constructed from a literal descriptor |
TPtrC copyPtr(pangramPtr); |
// Copy constructed from another TPtrC |
TBufC<100> constBuffer(KLiteralDes); // Constant buffer descriptor |
|
TPtrC ptr(constBuffer); |
// Constructed from a TBufC |
//TText8 is a single (8-bit) character, equivalent to unsigned char const TText8* cString = (TText8*)"Waltz, bad nymph, for quick jigs
vex";
//Constructed from a zero-terminated C string
TPtrC8 anotherPtr(cString);
TUint8* memoryLocation; // Pointer into memory initialized elsewhere TInt length; // Length of memory to be represented
...
TPtrC8 memPtr(memoryLocation,length); // Constructed from a pointer
The pointer itself may be changed to point at different string data – the Set() methods in TPtrC are defined for that purpose. If you want to indicate that the data your TPtrC points at should not be changed, you can declare the TPtrC to be const, which typically generates a compiler warning if an attempt is made to call Set() upon it. It will not fail, however, since the rules of const-ness in C++ are such that both const and non-const functions may be called on a const object.
// Literal descriptors are described later in this chapter _LIT(KLiteralDes1, "Sixty zippers were quickly picked from the woven jute
bag");
_LIT(KLiteralDes2, "Waltz, bad nymph, for quick jigs vex");
TPtrC alpha(KLiteralDes1);
TPtrC beta(KLiteralDes2);
alpha.Set(KLiteralDes2); // alpha points to the data in KLiteralDes2 beta.Set(KLiteralDes1); // beta points to the data in KLiteralDes1
const TPtrC gamma(beta); // |
Points to |
the data in beta, KLiteralDes1 |
|
gamma.Set(alpha); |
// |
Generates |
a warning, but points to alpha |
TPtr
TPtr is the modifiable pointer descriptor class for access to and modification of a character string or binary data. All the modifiable and non-modifiable base class operations of TDes and TDesC respectively may be performed on a TPtr.
62 |
DESCRIPTORS: SYMBIAN OS STRINGS |
The class defines constructors to allow objects of type TPtr to be constructed from a pointer into an address in memory, setting the length and maximum length as appropriate.
The compiler also generates implicit default and copy constructors, since they are not explicitly declared protected or private in the class. A TPtr object may be copy constructed from another modifiable pointer descriptor, for example, by calling the Des() method on a non-modifiable buffer, which returns a TPtr as shown below:
_LIT(KLiteralDes1, "Jackdaws love my big sphinx of quartz"); TBufC<60> buf(KLiteralDes1); // TBufC are described later
TPtr ptr(buf.Des()); // Copy construction; can modify the data in buf TInt length = ptr.Length(); // Length = 12
TInt maxLength = ptr.MaxLength(); // Maximum length = 60, as for buf
TUint8* memoryLocation; |
// Valid pointer into memory |
|
... |
|
|
TInt len = 12; |
// Length of data to |
be represented |
TInt maxLen = 32; |
// Maximum length to |
be represented |
// Construct a pointer descriptor from a pointer into memory
TPtr8 memPtr(memoryLocation, maxLen); // length = 0, max length = 32 TPtr8 memPtr2(memoryLocation, len, maxLen); // length = 12, max = 32
In addition, the class provides an assignment operator, operator =(), to copy data into the memory referenced by the pointer (from another modifiable pointer descriptor, a non-modifiable pointer or a zero-termin- ated string). If the length of the data to be copied exceeds the maximum length of the descriptor, a panic will be raised. Like TPtrC, this class also defines a Set() method to change the descriptor to point at different data.
_LIT(KLiteralDes1, "Jackdaws love my big sphinx of quartz");
TBufC<60> buf(KLiteralDes1); |
// TBufC are described later |
TPtr ptr(buf.Des()); |
// Points to the contents of buf |
TUint16* memoryLocation; |
// Valid pointer into memory |
...
TInt maxLen = 40; // Maximum length to be represented
TPtr memPtr(memoryLocation, maxLen); // length = 12, max length = 40
// Copy and replace
memPtr = ptr; // memPtr data is KLiteralDes1 (37 bytes), maxLength = 40
_LIT(KLiteralDes2, "The quick brown fox jumps over the lazy dog");
TBufC<100> buf2(KLiteralDes2); |
// |
TBufC are |
described later |
TPtr ptr2(buf2.Des()); |
// |
Points to |
the data in buf |
STACK-BASED BUFFER DESCRIPTORS |
63 |
// Replace what ptr points to
ptr.Set(ptr2); // ptr points to contents of buf2, max length = 100 memPtr = ptr2; // Attempt to update memPtr which panics because the
// contents of ptr2 (43 bytes) exceeds max length of memPtr (40 bytes)
You should be careful not to confuse Set(), which resets your descriptor to point at a new data area (with corresponding modification to the length and maximum length members) with operator =() which merely copies data into the existing descriptor (and may modify the descriptor length but not its maximum length).
5.4 Stack-Based Buffer Descriptors
The stack-based buffer descriptors may be modifiable or non-modifiable. The string data forms part of the descriptor object, located after the length word in a non-modifiable descriptor and after the maximum length word in a modifiable buffer descriptor. Figure 5.2 compares the memory layouts of TBuf and TBufC.
TBufC <12> |
|
|
|
|
|
|
|
iLength |
iBuf |
|
|
12 |
Hello World! |
|
|
|
|
|
|
TDesC |
TBufC |
|
|
TBufC <15> |
|
|
|
|
|
|
|
iLength |
iMaxLength |
iBuf |
|
12 |
15 |
Hello World! |
|
|
|
|
|
TDesC |
TDes |
TBuf |
Figure 5.2 Buffer descriptors
These descriptors are useful for fixed-size or relatively small strings, say up to the length of a 256-character filename. Being stack-based, they should be used when they have a lifetime that coincides with that of their creator. They may be considered equivalent to char[] in C, but with the benefit of overflow checking.
64 |
DESCRIPTORS: SYMBIAN OS STRINGS |
TBufC<n>
This is the non-modifiable buffer class, used to hold constant string or binary data. The class derives from TBufCBase (which derives from TDesC, and exists as an inheritance convenience rather than to be used directly). TBufC<n> is a thin template class which uses an integer value to determine the size of the data area allocated for the buffer descriptor object. Chapter 19 describes the thin template pattern and its role in Symbian OS code.
The class defines several constructors that allow non-modifiable buffers to be constructed from a copy of any other descriptor or from a zeroterminated string. They can also be created empty and filled later since, although the data is non-modifiable, the entire contents of the buffer may be replaced by calling the assignment operator defined by the class. The replacement data may be another non-modifiable descriptor or a zero-terminated string, but in each case the new data length must not exceed the length specified in the template parameter when the buffer was created.
_LIT(KPalindrome, "Satan, oscillate my metallic sonatas");
TBufC<50> |
buf1(KPalindrome); // |
Constructed |
from |
literal descriptor |
|
TBufC<50> |
buf2(buf1); |
// |
Constructed |
from |
buf1 |
// Constructed from a NULL-terminated C string TBufC<30> buf3((TText*)"Never odd or even");
TBufC<50> buf4; // Constructed empty, length = 0
// Copy and replace
buf4 = buf1; // buf4 contains data copied from buf1, length modified buf1 = buf3; // buf1 contains data copied from buf3, length modified buf3 = buf2; // Panic! Max length of buf3 is insufficient for buf2 data
The class also defines a Des() method which returns a modifiable pointer descriptor for the data represented by the buffer. So, while the content of a non-modifiable buffer descriptor cannot normally be altered directly, other than by complete replacement of the data, it is possible to change the data indirectly by creating a modifiable pointer descriptor into the buffer. When the data is modified through the pointer descriptor, the lengths of both the pointer descriptor and the constant buffer descriptor are changed although, of course, the length is not automatically extended because the descriptor classes do not provide memory management.
_LIT8(KPalindrome, "Satan, oscillate my metallic sonatas"); TBufC8<40> buf(KPalindrome); // Constructed from literal descriptor TPtr8 ptr(buf.Des()); // data is the string in buf, max length = 40
// Illustrates the use of ptr to copy and replace contents of buf ptr = (TText8*)"Do Geese see God?";