
See An Introduction to L Classes for further documentation about the LString class.
The EUserHL package is available for all phones running Symbian OS v9.x. For version 1.2 (June 2009), LString16 & LString8 directly support C++ string literals.
The new EUserHL Core Idioms library delivers:
The necessary header files for exploiting the new code idioms library are supplied in a header EUserHL.h, supported by a DLL, EUserHL.DLL, and an EUserHL.SIS installable package, available for all Symbian OS v9-based devices.
This makes Symbian OS easier for programmers by:
Using the Core Idioms library has a pervasive impact on line-of-code count and on simplicity and cleanness. That’s great when you write the code, and awesome when you come to maintain it.
The Core Idioms library delivers these improvements by exploiting the mapping of Symbian OS User::Leave() onto C++ throw, introduced in Symbian OS v9 and by relieving the application programmer of much explicit responsibility for memory management for strings and cleanup.
Idioms define the style by which programmers use an OS, and therefore have a pervasive ease-of-use impact in normal Symbian programming by programmers working at all levels of the software stack.
Use of this library is recommended for all new code. Only if you know you can do better by managing your own memory with traditional descriptor APIs, cleanup stack idioms, and two-phase construction, should you continue to use the traditional Symbian OS features instead of Core Idioms.
Here’s how the Core Idioms library makes a difference in real-life situations:
| Without Core Idioms library | With Core Idioms library |
{
TBuf<KMaxQuery> query; // fixed worst-
// case max
query.Format(KQueryFormat, param);
ExecuteQueryL(query);
}
|
{
LString query; // can grow its heap
// buffer on demand
query.FormatL(KQueryFormat, param);
ExecuteQueryL(query);
} // query buffer released on
// normal scope exit or leave |
{
HBufC* queryc =
HBufC::NewLC(KTooBigForStackMaxQuery);
TPtr query(queryc->Des());
BuildQueryL(query);
ExecuteQueryL(query);
CleanupStack::PopAndDestroy();
}
|
{
LString query;
BuildQueryL(query);
ExecuteQueryL(query);
}
|
{
RBuf query;
query.CleanupClosePushL();
query.CreateL(TooBigForStackMaxQuery);
BuildQueryL(query);
ExecuteQueryL(query);
CleanupStack::PopAndDestroy();
} |
{
LString query;
BuildQueryL(query);
ExecuteQueryL(query);
}
|
{
CQuery* query = CQuery::NewL();
CleanupStack::PushL(query);
query->BuildQueryL();
query->ExecuteQueryL();
CleanupStack::PopAndDestroy();
} |
{
LCleanedupPtr<CQuery> query(CQuery::NewL());
query->BuildQueryL();
query->ExecuteQueryL();
} // query deleted on normal scope
// exit or leave
|
{
CQuery* query = CQuery::NewL();
CleanupStack::PushL(query);
query.BuildQueryL();
CleanupStack::Pop();
return query;
} |
{
LCleanedupPtr<CQuery> query(CQuery::NewL());
query->BuildQueryL();
return query.Unmanage();
// query was protected until Unmanage()
} // was called
|
{
RQuery query;
CleanupClosePushL(query);
query.BuildQueryL();
query.ExecuteQueryL();
CleanupStack::PopAndDestroy();
} |
{
LCleanedupHandle<RQuery>;
query->BuildQueryL();
query->ExecuteQueryL();
} // query is closed on normal scope
// exit or leave
|
As this shows, the code with Core Idioms is usually one or two lines shorter than pre-Core Idioms code, and is never longer.
Cleanup stack operations don’t appear explicitly in the Core Idioms code, even though the cleanup stack is in fact being used, and all of the code shown here – both with and without Core Idioms – is totally leave-safe.
The code without Core Idioms often uses more memory than is really necessary, either on the stack or the heap, and the only way to avoid this is to code awkward extra lines of code. The code with Core Idioms uses only as much space as necessary and is brief.
Core Idioms v1.2 removes the need to specify string literals with _LIT, when using LString.
| Core Idioms prior to v1.2 | Core Idioms v1.2 |
{
// bunch of literal descriptors
_LIT(KStringOne, "One ");
_LIT(KStringTwo, "Two ");
_LIT(KStringThree, "Three");
// somewhat later, use those literals
LString s;
s = KStringOne;
s.AppendL(KStringTwo);
s += KStringThree;
}
|
{
// just use wide literals from pure C++
LString s;
s = L"One ";
s.AppendL(L"Two ");
s += L"Three";
}
|
This feature exploits the fact that expressions such as L"Three" produce a wchar_t[], and that in all ABIs of interest to Symbian OS, wchar_t is a 16-bit wide character, compatible with LString16.
The EUserHL Core Idioms library provides new APIs for simpler, exception-safe programming, a new string class and single-phase object construction.
The LString class provides a self managing, resizable descriptor that bridges the gap between the behavior of standard C++ strings, such as std::string, and Symbian descriptors. LString is recommended instead of traditional TBuf, HBufC and RBuf descriptors. LStrings can be passed into Symbian OS APIs which take TDes& and const TDesC& parameters.
The Symbian C++ descriptors have been a defining attribute of Symbian OS. The descriptor family was designed with reliability and space efficiency as primary goals. This was achieved by spreading common string behavior across a number of descriptor classes. Unfortunately, this decision has a usability cost due to the number of classes that must be directly understood by developers to achieve common objectives with strings. LString is intended to provide a self-managing alternative to several of the standard descriptor types.
An LString may be used much like a simple T class. LString-typed variables will automatically clean up after themselves when they go out of scope, and LString-typed fields will similarly automatically release all owned resources when their containing object is destroyed.
In addition to the value-type semantics described above, LString also supports automatic in-place resizing. All standard descriptor methods are available but for any non-leaving descriptor method that may panic due to buffer overflow, LString adds a corresponding leaving method that automatically expands the underlying buffer on-demand. For example, Append() will panic if the new data overflows available buffer space, while AppendL() will attempt to realloc() the buffer as necessary. The new leaving variants may therefore leave with KErrNoMemory, may invalidate any existing raw pointers into the data buffer (e.g., those previously returned by Ptr()), and may change the value returned by MaxLength(). To protect developers from inadvertently calling the non-leaving methods on LString, these have been privatized.
LString is compatible with existing APIs that accept const TDesC& and TDes& arguments. Both 8 and 16 bit versions are provided.
Finally, to allow LString to be allocated on the heap and deleted, the class defines implementations for various overloads of the operator new() and operator delete() methods.
For more information about the new string class, see An Introduction to L Classes.
The LCleanedupX and LManagedX class templates from Core Idioms are Symbian-specific analogues to C++ smart pointers:
The LCleanedupX and LManagedX class templates are:
| LCleanedupX template | LManagedX template |
What it manages |
How it cleans up by default |
LCleanedupPtr |
LManagedPtr |
a pointer |
deletes the pointer |
LCleanedupHandle |
LManagedHandle |
a handle, by value |
calls Close() on the handle |
LCleanedupRef</p> |
LManagedRef |
a handle, by reference |
calls Close() on the handle |
LCleanedupArray |
LManagedArray |
a C array |
deletes the array |
LCleanedupGuard |
LManagedGuard |
anything protected with a TCleanupItem |
calls the TCleanupItem’s cleanup operation |
The cleanup strategy can be changed to one of the predefined cleanup strategies provided, or to a user-defined custom cleanup strategy. For more information about the cleanup management helper templates, see An Introduction to L Classes.
Use LCleanedUpX templates instead of the classic CleanupStack::PushL() and CleanupStack::PopAndDestroy() approach to write leave-safe code more succinctly and elegantly than was previously possible, as shown in the before-and-after examples above.
Using:{
LCleanedupPtr<CQuery> query(CQuery::NewL());
// ...
if (condition)
return;
// ...
}{
CQuery* query = CQuery::NewL();
CleanupStack::PushL(query);
//...
if (condition)
{
// Pop and destroy the object (also deletes the pointer)
CleanupStack::PopAndDestroy(query);
return;
}
// ...
// Pop and destroy the object (also deletes the pointer)
CleanupStack::PopAndDestroy(query);
}Use LManagedX templates for member variables in classes which implement single-phase construction. See the example below.
Do not use LManagedX for any other purpose, without being aware of the differences between C++ exception-handling semantics and Symbian OS leave semantics: [R2]
The Core Idioms library supports single-phase construction of CBase-derived classes. To implement single-phase construction,
#include <e32std.h>
#include <f32file.h>
#include <euserhl.h> // Core Idioms
class CFinder : public CBase
{
public:
// We have opted to use single-phase construction here, and some of
// our constructor's initialization actions may leave. In order to
// guarantee full cleanup in all cases, we have to declare this fact.
CONSTRUCTORS_MAY_LEAVE
static CFinder* NewL(const TDesC& aPattern);
~CFinder();
void GetNextMatchL(TDes& aMatch);
protected:
CFinder(const TDesC& aPattern);
protected:
LString iPattern; // looks after its own cleanup
LManagedHandle<RFs> iFs; // will be closed as required
// ...
};
CFinder* CFinder::NewL(const TDesC& aPattern)
{
return new(ELeave) CFinder(aPattern);
}
CFinder::CFinder(const TDesC& aPattern)
// This initializer may leave, since the LString will allocate a
// heap buffer large enough to contain a copy of aPattern's data
: iPattern(aPattern)
{
// If connection fails and we leave here, iPattern's destructor
// will be called automatically, and the string's resources will
// be released
iFs->Connect() OR_LEAVE;
}
CFinder::~CFinder()
{
// Automatic destruction of each of the data members does all
// of the work for us: iPattern's heap buffer if freed, while
// Close() is called on the managed RFs in iFs.
// Even though this destructor is textually empty, it should
// still be exported; the compiler is generating destruction
// logic for us in this case
}
Compared to two-phase construction, NewL() is not implemented in terms of NewLC() followed by CleanupStack::Pop(); and there’s no ConstructL().
In fact, the NewL() function really isn’t necessary at all. Its sole benefit is the possibility to change the implementation to old-fashioned two-phase construction without breaking compatibility. Without a NewL(), the implementation would be even briefer and clients would just call new(ELeave) CFinder(pattern).
Note the use of the pointer operator rather than the member-selection operator. If iFs had been an unmanaged RFs, we’d have used iFs.Connect(). But since iFs is an LManagedHandle<RFs>, we use iFs->Connect().
In the example above, the line
iFs->Connect() OR_LEAVE;
shows the Core Idioms OR_LEAVE macro, which uses C++ operator overloading to generate code which is exactly equivalent to
User::LeaveIfError(iFs->Connect());With OR_LEAVE, it’s just as efficient, and much more readable.
The EUserHL Core Idioms library allows:
[R1] Resource Acquisition Is Initialization: a design pattern popular in many object oriented languages, which combines the acquisition and release of resources with initialization and uninitialization of objects. See en.wikipedia.org/wiki/RAII.