--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lowlevellibsandfws/genericusabilitylib/example/src/euserhl_walkthrough.cpp Tue Feb 02 02:01:42 2010 +0200
@@ -0,0 +1,1309 @@
+// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include <e32std.h>
+#include <f32file.h>
+#include <e32test.h>
+#include <euserhl.h>
+
+
+
+// Note: Methods are defined inline within classes here simply to make
+// the code shorter, keep related code closer together, and hopefully
+// make things easier to follow.
+
+RTest test(_L("EuserHl Walkthrough"));
+
+// Some dummy methods and data used in the walkthroughs below
+_LIT(KFill, "XXX");
+_LIT(KPath, "c:\\a\\b\\c");
+_LIT(KOne, "One ");
+_LIT(KTwo, "Two ");
+_LIT(KTesting, "Testing ");
+
+void MaybeLeaveL()
+ {
+ // Some code that may leave
+ }
+
+HBufC* AllocateNameL(const TDesC& aDes)
+ {
+ return aDes.AllocL();
+ }
+
+void ReadToMax(TDes& aDes)
+ {
+ aDes.SetMax();
+ aDes.Repeat(KFill);
+ }
+
+void GetCurrentPath(TDes& aDes)
+ {
+ aDes = KPath;
+ }
+
+void GetCurrentPathStringL(LString& aString)
+ {
+ aString = L"c:\\a\\b\\c"; // Will auto-grow if necessary, may leave
+ }
+
+LString AppendCurrentPathStringL(LString aString)
+ {
+ return aString+= L"c:\\a\\b\\c";
+ }
+
+class CTicker : public CBase
+ {
+public:
+ void Tick() { ++iTicks; }
+ void Tock() { ++iTocks; }
+
+ void Zap() { delete this; }
+
+public:
+ TInt iTicks;
+ TInt iTocks;
+ };
+
+// Defines a custom pointer cleanup policy that calls the Zap member
+class TTickerZapStrategy
+ {
+public:
+ static void Cleanup(CTicker* aPtr)
+ {
+ // The general template/class scaffolding remains the same
+ // for all custom cleanups, just this cleanup body varies
+ aPtr->Zap();
+ test.Printf(_L("Zapped CTicker\n"));
+ }
+ };
+
+void RegisterTicker(CTicker& aTicker)
+ {
+ (void)aTicker;
+ }
+
+void RegisterTickerPtr(CTicker* aTicker)
+ {
+ (void)aTicker;
+ }
+
+void TakeTickerOwnership(CTicker* aTicker)
+ {
+ delete aTicker;
+ }
+
+void RegisterTimer(RTimer& aTimer)
+ {
+ (void)aTimer;
+ }
+
+// Defines a custom handle cleanup policy that calls Cancel then Close
+class TCancelClose
+ {
+public:
+ template <class T>
+ static void Cleanup(T* aHandle)
+ {
+ // The general template/class scaffolding remains the same
+ // for all custom cleanups, just this cleanup body varies
+ aHandle->Cancel();
+ aHandle->Close();
+ test.Printf(_L("Cancel Closed RTimer\n"));
+ }
+ };
+
+void BespokeCleanupFunction(TAny* aData)
+ {
+ (void)aData;
+ test.Printf(_L("BespokeCleanupFunction\n"));
+ }
+
+// The walkthroughs themselves
+
+// This class demonstrates the use of an embedded LString in the
+// conventional Symbian two-phase construction pattern. We've chosen
+// to implement the temporary leave protection in NewL in terms of
+// LCleanedupPtr instead of the the CleanupStack API in this example.
+class CStringUserTwoPhase : public CBase
+ {
+public:
+ static CStringUserTwoPhase* NewL(const TDesC& aName)
+ {
+ // We can use the resource management utility classes in
+ // two-phase if we want to
+ LCleanedupPtr<CStringUserTwoPhase> self(new(ELeave) CStringUserTwoPhase);
+ self->ConstructL(aName);
+ // Calling Unmanage() disables cleanup and yields the
+ // previously managed pointer so that it can be safely
+ // returned
+ return self.Unmanage();
+ }
+
+ virtual void ConstructL(const TDesC& aName)
+ {
+ // This assignment may leave if LString fails to allocate a
+ // heap buffer large enough to hold the data in aName
+ iName = aName;
+ }
+
+ ~CStringUserTwoPhase()
+ {
+ // The iName LString cleans up after itself automatically
+ }
+
+ const TDesC& Name()
+ {
+ // We can just return an LString directly as a const TDesC
+ return iName;
+ }
+
+protected:
+ CStringUserTwoPhase()
+ {
+ // Everything interesting happens in ConstructL in this
+ // version.
+
+ // Default initialization of the iName LString does not
+ // allocate a heap buffer, and so cannot leave. As long as
+ // initialization is deferred to ConstructL, LStrings can be
+ // used safely with two-phase construction.
+ }
+
+protected:
+ LString iName;
+ };
+
+// This class demonstrates the use of an embedded LString in the
+// single-phase construction pattern, where a leave-safe constructor
+// fully initializes the object.
+//
+// Note that where a class's constructor forms part of its exported
+// public or protected contract, switching from a non-leaving to a
+// potentially leaving constructor would be a BC break. On the other
+// hand, if instantiation is entirely encapsulated within factory
+// functions like NewL, there is no such BC restriction.
+class CStringUserSinglePhase : public CBase
+ {
+public:
+ // This macro is necessary to ensure cleanup is correctly handled
+ // in the event that a constructor may leave beneath a call to
+ // new(ELeave)
+ CONSTRUCTORS_MAY_LEAVE
+
+ static CStringUserSinglePhase* NewL(const TDesC& aName)
+ {
+ return new(ELeave) CStringUserSinglePhase(aName);
+ }
+
+ ~CStringUserSinglePhase()
+ {
+ // The iName LString cleans up after itself automatically
+ }
+
+ const TDesC& Name()
+ {
+ // We can just return an LString directly as a const TDesC
+ return iName;
+ }
+
+protected:
+ CStringUserSinglePhase(const TDesC& aName)
+ // This initialization of iName may leave because LString
+ // needs to allocate a heap buffer to copy the aName string
+ // data into
+ : iName(aName)
+ {
+ // If iName initialization is successful but the constructor
+ // then goes on to leave later, iName (like all fields fully
+ // constructed at the point of a leave in a constructor) will
+ // be destructed, and so clean up after itself
+ MaybeLeaveL();
+ }
+
+protected:
+ LString iName;
+ };
+
+
+void WalkthroughStringsL()
+ {
+
+ {
+ // Trivially exercise the LString using classes defined above
+
+ LCleanedupPtr<CStringUserTwoPhase> one(CStringUserTwoPhase::NewL(KOne));
+ test.Printf(_L("Single phase name: %S\n"), &one->Name());
+
+ LCleanedupPtr<CStringUserSinglePhase> two(CStringUserSinglePhase::NewL(KTwo));
+ test.Printf(_L("Two phase name: %S\n"), &two->Name());
+
+ // Both instances are automatically deleted as we go out of scope
+ }
+
+ {
+ // A default constructed LString starts empty, doesn't
+ // allocate any memory on the heap, and therefore the
+ // following cannot leave
+ LString s;
+
+ // But it will grow on demand if you assign to it, so it has
+ // enough space to hold the copied string data, and so
+ // assignment may leave
+ s = L"One ";
+
+ // Similarly if you append to it with the leaving variant of
+ // Append, AppendL, if may grow on demand
+ s.AppendL(L"Two ");
+
+ // The += operator for LString also maps to AppendL
+ s += L"Three ";
+
+ // You can also use new leaving format methods that also grow
+ // on demand
+ s.AppendFormatL(KTesting);
+
+ // This general style of use of LString may be preferable to
+ // typical descriptor use for a number of reasons e.g. it
+ // avoids the common temptation to set an artificial maximum
+ // buffer size; it avoids massive conservative over-allocation
+ // when the average case length of a string is far less than
+ // the worst-case maximum; it will not surprise you (compared
+ // to the alternative of a large stack-allocated TBuf) by
+ // triggering stack overflow.
+
+ // An LString can be printed the same way as any descriptor
+ test.Printf(_L("Value: %S\n"), &s);
+
+ // An LString supports all TDesC and TDes methods
+ // LString findToken(L"Two ");
+ test(s.Find(L"Two ") == 4);
+
+ // LString matchPattern(L"*Two* ");
+ test(s.Match(L"*Two*") == 4);
+ test(s.Match(L"*T?o*") == 4);
+
+ // LString compare(L"some string");
+ test(s.Compare(L"One Two Three Testing ") == 0);
+ test(s.Compare(L"One Two Three Testing! ") < 0);
+ test(s.Compare(L"One Two Testing ") > 0);
+
+ // also LString ==,!=,>,<,<=,>=(L"some string");
+ test(s == L"One Two Three Testing ");
+ test(s < L"One Two Three Testing! ");
+ test(s > L"One Two Testing ");
+ test(s != L"not equal");
+
+ // An LString supports all TDesC and TDes operators
+ test(s[4] == TChar('T'));
+
+ TInt untrimmed = s.Length();
+ s.Trim();
+ test(s.Length() == untrimmed - 1);
+
+ s.UpperCase();
+ test.Printf(_L("UpperCase: %S\n"), &s);
+ s.LowerCase();
+ test.Printf(_L("LowerCase: %S\n"), &s);
+
+ // The underlying heap allocated buffer is released
+ // automatically when the LString goes out of scope, either
+ // normally or through a leave
+ }
+ {
+ // Copy, Append,Insert,Replace,Justify the same way as TDesC and TDes
+
+ LString s;
+ // Copies data into this 8-bit string descriptor, replacing any existing
+ // data, and expanding its heap buffer to accommodate if necessary.
+ // leaves on not being able to accomodate the new content
+ // both AssignL and += use CopyL internally
+ s.CopyL(L"new way of dealing with strings");
+ s.CopyUCL(L"new way of dealing with strings");
+ test(s == L"NEW WAY OF DEALING WITH STRINGS");
+
+ // Insert data into this descriptor.
+ // The length of this descriptor is changed to reflect the extra data.
+ // This leaving variant of the standard, non-leaving descriptor method
+ // differs in that this operation may cause the string descriptor's heap
+ // buffer to be reallocated in order to accommodate the new data. As a
+ // result, MaxLength() and Ptr() may return different values afterwards,
+ // and any existing raw pointers to into the descriptor data may be
+ // invalidated.
+ s.CopyL(L"Some Content Can Be Into This String");
+ s.InsertL(20,L"Inserted ");
+ test(s == L"Some Content Can Be Inserted Into This String");
+
+ // Replace data in this descriptor.
+ // The specified length can be different to the length of the replacement data.
+ // The length of this descriptor changes to reflect the change of data.
+ // This leaving variant of the standard, non-leaving descriptor method
+ // differs in that this operation may cause the string descriptor's heap
+ // buffer to be reallocated in order to accommodate the new data. As a
+ // result, MaxLength() and Ptr() may return different values afterwards,
+ // and any existing raw pointers to into the descriptor data may be
+ // invalidated.
+ s.CopyL(L"Some Content Can Be Decalper");
+ s.ReplaceL(20,8,L"Replaced");
+ test(s == L"Some Content Can Be Replaced");
+
+ // Append data onto the end of this descriptor's data.
+ // The length of this descriptor is incremented to reflect the new content.
+ // This leaving variant of the standard, non-leaving descriptor method
+ // differs in that this operation may cause the string descriptor's heap
+ // buffer to be reallocated in order to accommodate the new data. As a
+ // result, MaxLength() and Ptr() may return different values afterwards,
+ // and any existing raw pointers to into the descriptor data may be
+ // invalidated.
+ s.CopyL(L"Try appending ");
+ s.AppendL(L"Try appending some more",3);
+ test(s == L"Try appending Try");
+
+ // Copy data into this descriptor and justifies it, replacing any existing data.
+ // The length of this descriptor is set to reflect the new data.
+ // The target area is considered to be an area of specified width positioned at
+ // the beginning of this descriptor's data area. Source data is copied into, and
+ // aligned within this target area according to the specified alignment
+ // instruction.
+ // If the length of the target area is larger than the length of the source, then
+ // spare space within the target area is padded with the fill character.
+ // This leaving variant of the standard, non-leaving descriptor method
+ // differs in that this operation may cause the string descriptor's heap
+ // buffer to be reallocated in order to accommodate the new data. As a
+ // result, MaxLength() and Ptr() may return different values afterwards,
+ // and any existing raw pointers to into the descriptor data may be
+ // invalidated.
+ s.CopyL(L"Justified");
+ s.JustifyL(L"Just",9,ERight,'x');
+ test(s == L"xxxxxJust");
+
+ // Append data onto the end of this descriptor's data and justifies it.
+ // The source of the appended data is a memory location.
+ // The target area is considered to be an area of specified width, immediately
+ // following this descriptor's existing data. Source data is copied into, and
+ // aligned within, this target area according to the specified alignment instruction.
+ // If the length of the target area is larger than the length of the source,
+ // then spare space within the target area is padded with the fill character.
+ // This leaving variant of the standard, non-leaving descriptor method
+ // differs in that this operation may cause the string descriptor's heap
+ // buffer to be reallocated in order to accommodate the new data. As a
+ // result, MaxLength() and Ptr() may return different values afterwards,
+ // and any existing raw pointers to into the descriptor data may be
+ // invalidated.
+ s.CopyL(L"One ");
+ s.AppendJustifyL(L"Two Three",3,7,ERight,'x');
+ test(s == L"One xxxxTwo" );
+
+ }
+ {
+ // You can initialize with a MaxLength value
+ LString s(KMaxFileName); // This operation may leave
+ test(s.MaxLength() == KMaxFileName);
+
+ // And you can dynamically adjust MaxLength later using
+ // SetMaxLengthL if you want an exact allocated size
+ // Setting MaxLength on construction or via SetMaxLengthL is
+ // exact; calling MaxLength() immediately afterwards is
+ // guaranteed to return exactly the value you specified
+ s.SetMaxLengthL(2 * KMaxFileName);
+ test(s.MaxLength() == 2 * KMaxFileName);
+
+ // Pre-setting MaxLength is important when passing an LString
+ // as a TDes to a library function, because the LString can't
+ // be auto-grown via the TDes API
+
+ }
+
+ {
+ // You can initialize from any descriptor/literal/[wide]character string and the
+ // string data is copied into the LString
+ LString s(L"One "); // From a character string
+ s += L"Two ";
+ LString half(s.Left(s.Length() / 2)); // Left returns a TPtrC
+ test.Printf(_L("All: %S, Half: %S\n"), &s, &half);
+
+ // On the other hand, you can initialize from a returned
+ // HBufC* and the LString automatically takes ownership
+ LString own(AllocateNameL(KTesting));
+ test.Printf(_L("What I own: %S\n"), &own);
+
+ // Following that you can re-assign an HBufC to an existing
+ // string using the assignment operator
+ // taking ownership of the new content.
+ own = AllocateNameL(KTesting);
+
+ // Following that you can re-assign an HBufC to an existing
+ // string. The string destroys its original content before
+ // taking ownership of the new content.
+ own.Assign(AllocateNameL(KTesting));
+
+ // The content of one string can similarly be assigned
+ // to another to avoid copying. In this example, the content
+ // is detached from 's' and transfered to 'own'.
+ own.Assign(s);
+
+ // The same content transfer can be achieved from an RBuf to a
+ // string. You may need to do this if a legacy method returns
+ // you an RBuf. The RBuf is emptied of its content.
+ RBuf16 buf;
+ buf.CreateL(KOne);
+ own.Assign(buf);
+
+ // You can also assign a simple text array to a string as its
+ // new buffer. This method initialises the length to zero.
+ own.Assign((TText*)User::Alloc(24*(TInt)sizeof(TText)), 24);
+
+ // If the buffer has already been filled with some characters
+ // then you supply the length in this alternative Assign method.
+ own.Assign((TText*)User::Alloc(24*(TInt)sizeof(TText)), 12,24);
+
+ // Each Assign destroys the old content before assuming ownership
+ // of the new.
+ // As usual the last content of the string is destroyed when the
+ // LString goes out of scope
+ }
+
+ {
+ // You can reserve extra free space in preparation for an
+ // operation that adds characters to the string. You may
+ // need to do this when you cannot use any of the auto-buffer
+ // extending LString methods to achieve your objective.
+ LString s(L"One ");
+ s.ReserveFreeCapacityL(4);
+ test(s.Length() == 4);
+ test(s.MaxLength() >= 8);
+
+ // Almost all the methods that may extended the string buffer,
+ // including the explicit ReserveFreeCapacityL, but excluding
+ // SetMaxLengthL, attempt to grow the size exponentially.
+ // The Exponential growth pattern is expected to give better
+ // performance at an amortised complexity of O(n) when adding n characters.
+ // If the exponential growth is less than the supplied extra size
+ // then the supplied size is used instead to save time.
+ // The exponential growth is used in anticipation of further additions
+ // to a string. This trades-off speed efficiency for space efficiency.
+ // If required you may be able to swap the oversized buffer for
+ // a more compact one using:
+ s.Compress();
+ test(s.MaxLength() >= 4); //note indefinite test
+
+ // Resize attempts to re-allocate a smaller buffer to copy
+ // the content into. If the new memory cannot be allocated then the
+ // original string is left unaffected.
+
+ // When you have finished using the content of a string you can
+ // get its buffer released without destroying the string itself.
+ // You may want to do this when using member declared strings.
+ // Automatic strings are destroyed when they go out of scope.
+ s.Reset();
+ test(s.Length() == 0);
+ test(s.MaxLength() == 0);
+
+ }
+
+ {
+ // An LString can be passed directly to any function requiring
+ // a const TDesC&
+ TInt year = 2009;
+
+ LString s;
+ s.FormatL(_L("Happy New Year %d"), year);
+ // InfoPrint takes a const TDesC&
+ User::InfoPrint(s);
+
+ LString pattern;
+ pattern.FormatL(_L("*Year %d"), year);
+ // Match takes a const TDesC& as a pattern
+ TInt loc = s.Match(pattern);
+ test(loc == 10);
+ }
+
+ {
+ // An LString can be passed directly to any function requiring
+ // a TDes& but care must always be taken to pre-set MaxLength
+ // since LStrings can't be automatically grown via the TDes
+ // interface
+
+ LString s;
+ // Calling GetCurrentPath(s) now would panic because LStrings
+ // are initialized by default to MaxLength 0. Although s is
+ // an LString GetCurrentPath takes a TDes& and so inside the function
+ // s behaves as a TDes and would panic with USER 11 if the resulting
+ // new length of s is greater than its maximum length.
+ test(s.MaxLength() == 0);
+
+ // Calling SetMaxLengthL will automatically realloc the
+ // underlying buffer if required, and is guaranteed to leave
+ // MaxLength() equal to the specified value
+ s.SetMaxLengthL(KMaxFileName);
+ GetCurrentPath(s);
+ //LString pathString(L"c:\\a\\b\\c");
+ test.Printf(_L("Path: %S\n"), &s);
+ test(s == L"c:\\a\\b\\c");
+
+ // If SetMaxLengthL adjusts MaxLength lower than the current
+ // Length, the data is truncated to the new MaxLength and
+ // Length set to the new MaxLength
+ s.SetMaxLengthL(s.Length() / 2);
+ test.Printf(_L("Truncated path: %S\n"), &s);
+ test(s.Length() == s.MaxLength());
+
+ // An initial MaxLength can be specified when constructing an
+ // LString. Note that unlike the default constructor, this
+ // variant allocates and may leave.
+ LString s2(KMaxFileName);
+ GetCurrentPath(s2);
+ test.Printf(_L("Path: %S\n"), &s2);
+ test(s2 == L"c:\\a\\b\\c");
+
+ // Your code and APIs can benefit from LString's auto-growth
+ // behaviour by accepting an LString to fill in as an output
+ // parameter. Using LString rather than TDes parameters means
+ // that the function is able to safely increase the size of the
+ // string as the LString will re-allocate as necessary
+ LString s3;
+ // GetCurrentPathStringL takes an LString&
+ GetCurrentPathStringL(s3);
+ test.Printf(_L("Path: %S\n"), &s3);
+ test(s3 == L"c:\\a\\b\\c");
+
+ // As a well-defined value class, if you want to, LStrings can
+ // be passed and returned by value. This is relatively
+ // inefficient however due to the amount of copying and heap
+ // reallocation involved.
+ LString s4(AppendCurrentPathStringL(s3));
+ test.Printf(_L("Appended path: %S\n"), &s4);
+ test(s4.Length() == s3.Length() * 2);
+ }
+
+ {
+ // LStrings can be allocated on the heap if necessary.
+ // Then it can managed as part of an array of string pointers
+ TInt n = 5;
+ LCleanedupHandle<RPointerArray<LString>, TResetAndDestroy> sarray;
+
+ for (TInt i = 0; i < n; ++i)
+ {
+ LString* s = new(ELeave) LString;
+ s->FormatL(_L("String %d"), i);
+ sarray->Append(s);
+ }
+
+ for (TInt i = 0, n = sarray->Count(); i < n; ++i)
+ {
+ LString tmp;
+ tmp.FormatL(_L("String %d"), i);
+ test(tmp == *(*sarray)[i]);
+ test.Printf(_L("String %d = %S\n"), i, (*sarray)[i]);
+ }
+
+ }
+
+ {
+ // Any allocation failure in new(ELeave)LString throws
+ // KErrNoMemory and cleans up after itself fully
+
+ __UHEAP_MARK;
+ //coverity[resource_leak]
+ //As mentioned in the comment above any allocation failure is taken care of
+ TRAPD(status, new(ELeave) LString(100 * 1024 * 1024));
+ test(status == KErrNoMemory);
+ __UHEAP_MARKEND;
+ }
+
+ {
+ // Native C arrays (both heap and stack allocated) of LStrings
+ // also work, although their use is not recommended
+
+ TInt n = 5;
+ LCleanedupArray<LString> sarray(new(ELeave) LString[n]);
+
+ for (TInt i = 0; i < n; ++i)
+ {
+ sarray[i].FormatL(_L("String %d"), i);
+ }
+
+ for (TInt i = 0; i < n; ++i)
+ {
+ LString tmp;
+ tmp.FormatL(_L("String %d"), i);
+ test(tmp == sarray[i]);
+ test.Printf(_L("String %d = %S\n"), i, &sarray[i]);
+ }
+
+ }
+ {
+ // 8-bit wide null terminated character string support
+
+ // A default constructed LString8 starts empty, doesn't
+ // allocate any memory on the heap, and therefore the
+ // following cannot leave
+ LString8 s;
+
+ // But it will grow on demand if you assign to it, so it has
+ // enough space to hold the copied string data, and so
+ // assignment may leave
+ s ="One ";
+
+ // Similarly if you append to it with the leaving variant of
+ // Append, AppendL, if may grow on demand
+ s.AppendL("Two ");
+
+ // The += operator for LString8 also maps to AppendL
+ s +="Three ";
+ s +="Testing ";
+
+ // An LString8 can be printed the same way as any descriptor
+ test.Printf(_L("Value: %S \n"), &s);
+
+ // An LString8 can be compared the same way as any descriptor
+ test(s == "One Two Three Testing ");
+
+ // An LString8 supports all TDesC and TDes methods
+ // LString findToken("Two ");
+ test(s.Find("Two ") == 4);
+
+ // LString8 matchPattern("*Two* ");
+ test(s.Match("*Two*") == 4);
+ test(s.Match("*T?o*") == 4);
+
+ // LString8 compare("some string");
+ test(s.Compare("One Two Three Testing ") == 0);
+ test(s.Compare("One Two Three Testing! ") < 0);
+ test(s.Compare("One Two Testing ") > 0);
+
+ // also LString8 ==,!=,>,<,<=,>=(L"some string");
+ test(s == "One Two Three Testing ");
+ test(s < "One Two Three Testing! ");
+ test(s > "One Two Testing ");
+ test(s != "not equal");
+
+ // Copies data into this 8-bit string descriptor, replacing any existing
+ // data, and expanding its heap buffer to accommodate if necessary.
+ // leaves on not being able to accomodate the new content
+ // both AssignL and += use CopyL internally
+ s.CopyL("new way of dealing with strings");
+
+
+ // Copy, Append,Insert,Replace,Justify the same way as TDesC8 and TDes8
+
+ // Copies data into this 8-bit string descriptor, replacing any existing
+ // data, and expanding its heap buffer to accommodate if necessary.
+ // leaves on not being able to accomodate the new content
+ // both AssignL and += use CopyL internally
+ s.CopyL("new way of dealing with strings");
+ s.CopyUCL("new way of dealing with strings");
+ test(s == "NEW WAY OF DEALING WITH STRINGS");
+
+ // Insert data into this descriptor.
+ // The length of this descriptor is changed to reflect the extra data.
+ // This leaving variant of the standard, non-leaving descriptor method
+ // differs in that this operation may cause the string descriptor's heap
+ // buffer to be reallocated in order to accommodate the new data. As a
+ // result, MaxLength() and Ptr() may return different values afterwards,
+ // and any existing raw pointers to into the descriptor data may be
+ // invalidated.
+ s.CopyL("Some Content Can Be Into This String");
+ s.InsertL(20,"Inserted ");
+ test(s == "Some Content Can Be Inserted Into This String");
+
+ // Replace data in this descriptor.
+ // The specified length can be different to the length of the replacement data.
+ // The length of this descriptor changes to reflect the change of data.
+ // This leaving variant of the standard, non-leaving descriptor method
+ // differs in that this operation may cause the string descriptor's heap
+ // buffer to be reallocated in order to accommodate the new data. As a
+ // result, MaxLength() and Ptr() may return different values afterwards,
+ // and any existing raw pointers to into the descriptor data may be
+ // invalidated.
+ s.CopyL("Some Content Can Be Decalper");
+ s.ReplaceL(20,8,"Replaced");
+ test(s == "Some Content Can Be Replaced");
+
+ // Append data onto the end of this descriptor's data.
+ // The length of this descriptor is incremented to reflect the new content.
+ // This leaving variant of the standard, non-leaving descriptor method
+ // differs in that this operation may cause the string descriptor's heap
+ // buffer to be reallocated in order to accommodate the new data. As a
+ // result, MaxLength() and Ptr() may return different values afterwards,
+ // and any existing raw pointers to into the descriptor data may be
+ // invalidated.
+ s.CopyL("Try appending ");
+ s.AppendL("Try appending some more",3);
+ test(s == "Try appending Try");
+
+ // Copy data into this descriptor and justifies it, replacing any existing data.
+ // The length of this descriptor is set to reflect the new data.
+ // The target area is considered to be an area of specified width positioned at
+ // the beginning of this descriptor's data area. Source data is copied into, and
+ // aligned within this target area according to the specified alignment
+ // instruction.
+ // If the length of the target area is larger than the length of the source, then
+ // spare space within the target area is padded with the fill character.
+ // This leaving variant of the standard, non-leaving descriptor method
+ // differs in that this operation may cause the string descriptor's heap
+ // buffer to be reallocated in order to accommodate the new data. As a
+ // result, MaxLength() and Ptr() may return different values afterwards,
+ // and any existing raw pointers to into the descriptor data may be
+ // invalidated.
+ s.CopyL("Justified");
+ s.JustifyL("Just",9,ERight,'x');
+ test(s == "xxxxxJust");
+
+ // Append data onto the end of this descriptor's data and justifies it.
+ // The source of the appended data is a memory location.
+ // The target area is considered to be an area of specified width, immediately
+ // following this descriptor's existing data. Source data is copied into, and
+ // aligned within, this target area according to the specified alignment instruction.
+ // If the length of the target area is larger than the length of the source,
+ // then spare space within the target area is padded with the fill character.
+ // This leaving variant of the standard, non-leaving descriptor method
+ // differs in that this operation may cause the string descriptor's heap
+ // buffer to be reallocated in order to accommodate the new data. As a
+ // result, MaxLength() and Ptr() may return different values afterwards,
+ // and any existing raw pointers to into the descriptor data may be
+ // invalidated.
+ s.CopyL("One ");
+ s.AppendJustifyL("Two Three",3,7,ERight,'x');
+ test(s == "One xxxxTwo" );
+
+ }
+
+ }
+
+// This class demonstrates the use of the embeddable management
+// classes in a conventional Symbian two-phase construction
+// pattern.
+class CManagedUserTwoPhase : public CBase
+ {
+public:
+ static CManagedUserTwoPhase* NewL(CTicker* aTicker)
+ {
+ // We can use the resource management utility classes in
+ // two-phase if we want to
+ LCleanedupPtr<CManagedUserTwoPhase> self(new(ELeave) CManagedUserTwoPhase);
+ self->ConstructL(aTicker);
+ // Calling Unmanage() disables cleanup and yields the
+ // previously managed pointer so that it can be safely
+ // returned
+ return self.Unmanage();
+ }
+
+ ~CManagedUserTwoPhase()
+ {
+ // The iTicker manager will automatically delete the CTicker
+ // The iTimer manager will automatically Close() the RTimer
+ }
+
+ CTicker& Ticker()
+ {
+ // If we dereference the management object we get a CTicker&
+ return *iTicker;
+ }
+
+ RTimer& Timer()
+ {
+ // If we dereference the management object we get an RTimer&
+ return *iTimer;
+ }
+
+private:
+
+ virtual void ConstructL(CTicker* aTicker)
+ {
+ // Take ownership and manage aTicker
+ iTicker = aTicker;
+
+ // Note use of -> to indirect through the management wrapper
+ iTimer->CreateLocal() OR_LEAVE;
+ }
+
+ CManagedUserTwoPhase()
+ {
+ // Everything interesting happens in ConstructL in this
+ // version.
+
+ // Default initialization of the iName LString does not
+ // allocate a heap buffer, and so cannot leave. As long as
+ // initialization is deferred to ConstructL, LStrings can be
+ // used safely with two-phase construction.
+ }
+
+private:
+ // We have to use LManagedXxx for fields, not LCleanedupXxx
+ LManagedPtr<CTicker> iTicker;
+ LManagedHandle<RTimer> iTimer;
+ };
+
+// This class demonstrates the use of embedded management classes in
+// the single-phase construction pattern, where a leave-safe
+// constructor fully initializes the object.
+//
+// Note that where a class's constructor forms part of its exported
+// public or protected contract, switching from a non-leaving to a
+// potentially leaving constructor would be a BC break. On the other
+// hand, if instantiation is entirely encapsulated within factory
+// functions like NewL, there is no such BC restriction.
+
+class CManagedUserSinglePhase : public CBase
+ {
+public:
+ // This macro is necessary to ensure cleanup is correctly handled
+ // in the event that a constructor may leave beneath a call to
+ // new(ELeave)
+ CONSTRUCTORS_MAY_LEAVE
+
+ static CManagedUserSinglePhase* NewL(CTicker* aTicker)
+ {
+ return new(ELeave) CManagedUserSinglePhase(aTicker);
+ }
+
+ ~CManagedUserSinglePhase()
+ {
+ // The iTicker manager destructor will automatically Zap() the CTicker
+ // The iTimer manager destructor will automatically Close() the RTimer
+ }
+
+ CTicker& Ticker()
+ {
+ // If we dereference the management object we get a CTicker&
+ return *iTicker;
+ }
+
+ RTimer& Timer()
+ {
+ // If we dereference the management object we get an RTimer&
+ return *iTimer;
+ }
+
+private:
+ CManagedUserSinglePhase(CTicker* aTicker)
+ // Take ownership and manage aTicker. Note that initialization
+ // of the LManagedXxx classes does not actually leave, but
+ // initialization of the LCleanedupXxx classes can.
+ : iTicker(aTicker)
+ {
+ // If iTicker initialization is successful but the constructor
+ // then goes on to leave later, iTicker (like all fields fully
+ // constructed at the point of a leave in a constructor) will
+ // be destructed, and the manager will cleanup the CTicker
+
+ // Note use of -> to indirect through the management wrapper
+ iTimer->CreateLocal() OR_LEAVE;
+
+ // Likewise if we leave here, both iTicker and iTimer will
+ // undergo managed cleanup
+ MaybeLeaveL();
+ }
+
+private:
+ // We have to use LManagedXxx for fields, not LCleanedupXxx
+ LManagedPtr<CTicker, TTickerZapStrategy> iTicker;
+ LManagedHandle<RTimer> iTimer;
+ };
+
+//Class definition of trivial R-Class
+class RSimple
+ {
+public:
+
+ RSimple(){iData = NULL;}
+
+ //Open function sets value
+ void OpenL(TInt aValue)
+ {
+ iData = new(ELeave) TInt(aValue);
+ }
+
+ //Cleanup function – frees resource
+ void Close()
+ {
+ delete iData;
+ iData = NULL;
+ }
+
+ //Cleanup function – frees resource
+ void Free()
+ {
+ delete iData;
+ iData = NULL;
+ }
+
+ //Cleanup function – frees resource
+ void ReleaseData()
+ {
+ delete iData;
+ iData = NULL;
+ }
+
+ //static cleanup function – frees aRSimple resources
+ static void Cleanup(TAny* aRSimple)
+ {
+ static_cast<RSimple*>(aRSimple)->Close();
+ }
+
+
+private:
+ TInt* iData;
+
+ };
+
+
+//This sets the default cleanup behaviour for the RSimple class to
+//be RSimple::ReleaseData.
+//If this Macro is not used then the default cleanup behaviour
+//would be to call RSimple::Close().
+DEFINE_CLEANUP_FUNCTION(RSimple, ReleaseData);
+
+
+void WalkthroughManagedL()
+ {
+ {
+ // Trivially exercise the manager-using classes defined above
+ CTicker* ticker1 = new(ELeave) CTicker;
+ LCleanedupPtr<CManagedUserTwoPhase> one(CManagedUserTwoPhase::NewL(ticker1));
+ test(&one->Ticker() == ticker1);
+ one->Timer().Cancel(); // Just to check we can get at it
+
+ CTicker* ticker2 = new(ELeave) CTicker;
+ LCleanedupPtr<CManagedUserSinglePhase> two(CManagedUserSinglePhase::NewL(ticker2));
+ test(&two->Ticker() == ticker2);
+ two->Timer().Cancel(); // Just to check we can get at it
+
+ // Both instances are automatically deleted as we go out of scope
+ }
+
+ // Always use LCleanedupXxx for locals, not LManagedXxx
+
+ {
+ // Begin the scenes the LCleanedupXxx constructors push a
+ // cleanup item onto the cleanup stack and so may leave. If
+ // there is a leave during construction, the supplied pointer
+ // will still get cleaned up.
+ LCleanedupPtr<CTicker> t(new(ELeave) CTicker);
+
+ // We can access CTicker's members via the management object
+ // using ->
+ t->Tick();
+ t->Tock();
+ test(t->iTicks == t->iTocks);
+
+ // We can get at a reference to the managed object using *
+ // when we need to, e.g. if we need to pass it to a function
+ RegisterTicker(*t); // Takes a CTicker&
+
+ // If some unfriendly interface needs a pointer rather than a
+ // ref, we have a couple of options
+ RegisterTickerPtr(&*t); // Takes a CTicker*
+ RegisterTickerPtr(t.Get()); // Takes a CTicker*
+
+ // Note the use of . in t.Get() above; this distinguishes
+ // operations on the managing type from operations on the
+ // managed object
+
+ // When the management object goes out of scope, either
+ // normally or as the result of a leave, the managed object is
+ // automatically deleted
+ }
+
+ {
+ // Sometimes you need to protect something temporarily before
+ // transferring ownership e.g. by returning the pointer or
+ // passing it to a function that takes ownership.
+
+ LCleanedupPtr<CTicker> t(new(ELeave) CTicker);
+
+ // Protected while we do this
+ MaybeLeaveL();
+
+ // But now we want to hand it off, so we use Unmanage() to
+ // both return a pointer and break the management link
+ TakeTickerOwnership(t.Unmanage());
+
+ // Now when it goes out of scope, no cleanup action is
+ // performed
+ }
+
+ {
+ // If needed, it is possible to reuse a manager by using = to
+ // assign it a new managed object.
+
+ // Not managing anything to start with
+ LCleanedupPtr<CTicker> t;
+ test(t.Get() == NULL);
+ test(&*t == NULL);
+
+ for (TInt i = 0; i < 10; ++i)
+ {
+ // If an object is already being managed, it is cleaned up
+ // before taking ownership of the new object
+ t = new(ELeave) CTicker;
+ }
+ // We're left owning the final ticker instance, all prior
+ // instances having been automatically deleted
+ }
+
+ {
+ // If you have stateful code where a pointer can sometimes be
+ // NULL, as a convenience you can test the managing object
+ // itself as a shortcut test for NULL
+ LCleanedupPtr<CTicker> t(new(ELeave) CTicker);
+
+ // Does t refer to NULL?
+ if (!t)
+ {
+ test(EFalse);
+ }
+
+ t = NULL; // Also releases the currently managed CTicker
+
+ // Does t refer to a non-NULL pointer?
+ if (t)
+ {
+ test(EFalse);
+ }
+ }
+
+ {
+ // LCleanedupPtr uses delete to cleanup by default, but
+ // alternative cleanups can be specified
+
+ // We just want to free this one and not invoke the destructor
+ LCleanedupPtr<CTicker, TPointerFree> t(static_cast<CTicker*>(User::AllocL(sizeof(CTicker))));
+
+ // Now User::Free() is called when t goes out of scope
+ }
+
+ {
+ // As well as the stock options, custom cleanup policies can
+ // also be defined. See above for the definition of
+ // TTickerZap.
+ LCleanedupPtr<CTicker, TTickerZapStrategy> t(new(ELeave) CTicker);
+
+ // Now Zap() is called on the CTicker instance when t goes out of scope
+ }
+
+ {
+ // LCleanedupHandle is very similar in behaviour to
+ // LCleanedupPtr, the main difference being that it can define
+ // and contain its own instance of a handle rather than
+ // being supplied one
+ LCleanedupHandle<RTimer> t;
+
+ // Again, access to managed handle members is via ->
+ t->CreateLocal() OR_LEAVE;
+ t->Cancel();
+
+ // We can get a reference to the handle for passing to
+ // functions using *
+ RegisterTimer(*t);
+
+ // When the management object goes out of scope, either
+ // normally or as the result of a leave, the managed object is
+ // automatically cleanup by calling Close() on it
+ }
+
+ {
+ // LCleanedupHandle calls Close() by default, but alternative
+ // cleanups can be specified
+
+ // We want this RPointerArray cleanup with with
+ // ResetAndDestroy instead of Close()
+ LCleanedupHandle<RPointerArray<HBufC>, TResetAndDestroy> array;
+ for (TInt i = 0; i < 10; ++i)
+ {
+ array->AppendL(HBufC::NewL(5));
+ }
+
+ // Now when array goes out of scope, ResetAndDestroy is called
+ // to clean it up
+ }
+
+ {
+ // As well as the stock options, custom cleanup policies can
+ // also be defined. See above for the definition of
+ // TCancelClose.
+ LCleanedupHandle<RTimer, TCancelClose> t;
+ t->CreateLocal();
+
+ // Now Cancel() followed by Close() are called when t goes out
+ // of scope
+ }
+
+
+ {
+ // LCleanedupHandleRef calls Close() by default, but alternative
+ // cleanups can be specified
+
+ // We want this RPointerArray cleanup with with
+ // ResetAndDestroy instead of Close()
+ RPointerArray<HBufC> rar;
+ // calls to functions that cannot leave here
+ rar.Append(HBufC::NewL(5));
+ rar.Append(HBufC::NewL(5));
+
+
+ LCleanedupRef<RPointerArray<HBufC>, TResetAndDestroy> array(rar);
+ // calls to functions that could leave here
+ for (TInt i = 0; i < 10; ++i)
+ {
+ array->AppendL(HBufC::NewL(5));
+ }
+
+ // Now when array goes out of scope, ResetAndDestroy is called
+ // to clean it up
+ }
+
+ {
+ // Never mix direct cleanup stack API calls with management
+ // class use within the same function, because their
+ // interaction can be confusing and counter intuitive. Avoid
+ // the use of LC methods that leave objects on the cleanup
+ // stack, and use L methods instead.
+
+ // If a badly-behaved API were to offer only an LC variant,
+ // you would have to use it as follows
+ HBufC* raw = HBufC::NewLC(5);
+ // Must pop immediately to balance the cleanup stack, before
+ // instantiating the manager
+ CleanupStack::Pop();
+ LCleanedupPtr<HBufC> wrapped(raw);
+
+ // Never do this:
+ //LCleanedupPtr<HBufC> buf(HBufC::NewLC(5));
+ //CleanupStack::Pop();
+ // because the manager will be popped (having been pushed
+ // last), not the raw buf pointer as you might have hoped
+
+ // A cleaner alternative may be to write your own L function
+ // wrapper around the LC function supplied.
+
+ // Luckily this situation (an LC method without a
+ // corresponding L method) is rare in practice.
+ }
+
+ {
+ // Although rarely used on Symbian OS, C++ arrays are
+ // supported with a custom management class
+ LCleanedupArray<CTicker> array(new CTicker[5]);
+
+ // The array is cleaned up with delete[] on scope exit
+ }
+
+ {
+ // Although most cases are best covered by applying custom
+ // cleanup policies to the management classes already
+ // described, there is also a general TCleanupItem style
+ // cleanup option
+ TAny* data = NULL; // But could be anything
+ LCleanedupGuard guard1(BespokeCleanupFunction, data);
+ // On scope exit BespokeCleanupFunction is called on data
+
+ LCleanedupGuard guard2(BespokeCleanupFunction, data);
+ // But cleanup can also be disabled in this case, as follows
+ guard2.Dismiss();
+ }
+
+ {
+ LCleanedupHandle<RFs> managedFs;
+ managedFs->Connect();
+ //default cleanup strategy is to call RFs::Close() on scope exit
+ }
+
+ {
+ LCleanedupHandle<RSimple, TFree> simple;
+ simple->OpenL(23);
+ //Specified cleanup strategy is to call RSimple::Free() on scope exit
+ }
+
+ //Because the DEFINE_CLEANUP_FUNCTION is defined above, the default
+ //cleanup function for RSimple is RSimple::ReleaseData() rather than
+ //RSimple::Close()
+ {
+ LCleanedupHandle<RSimple> simple;
+ simple->OpenL(23);
+ //Custom cleanup strategy is to call RSimple::ReleaseData() on scope exit
+ }
+
+ {
+ RSimple simple;
+
+ //The RSimple class above defines a static cleanup function
+ //RSimple::Cleanup.
+ LCleanedupGuard guard(RSimple::Cleanup, &simple);
+
+ simple.OpenL(10);
+
+ //On scope exit RSimple::Cleanup() is called passing &simple
+ }
+ }
+
+void WalkthroughUsageL()
+ {
+ RFile file;
+
+ test.Printf(_L("Size of RFile = %d"), sizeof(file));
+
+ LCleanedupHandle<RFile> cFile;
+
+ test.Printf(_L("Size of LCleanedupHandle<RFile> = %d"), sizeof(cFile));
+
+ LCleanedupRef<RFile> crFile(file);
+
+ test.Printf(_L("Size of LCleanedupRef<RFile> = %d"), sizeof(crFile));
+
+ CTicker* tracker = new(ELeave) CTicker;
+ //coverity[resource_leak]
+ //As mentioned in the comment above any allocation failure is taken care of
+ test.Printf(_L("Size of CTracker* = %d"), sizeof(tracker));
+
+ LCleanedupPtr<CTicker> cTracker(tracker);
+
+ test.Printf(_L("Size of LCleanedupHandle<RFile> = %d"), sizeof(LCleanedupPtr<CTicker>));
+ }
+
+TInt TestL()
+ {
+ WalkthroughStringsL();
+ WalkthroughManagedL();
+ WalkthroughUsageL();
+
+ return KErrNone;
+ }
+
+TInt E32Main()
+ {
+
+ test.Start(_L("EUserHl Walkthrough"));
+ test.Title();
+
+ CTrapCleanup* trapHandler=CTrapCleanup::New();
+ test(trapHandler!=NULL);
+
+ __UHEAP_MARK;
+
+ TRAPD(status, TestL());
+
+ __UHEAP_MARKEND;
+
+ if (status != KErrNone) test.Printf(_L("Error: %d\n"), status);
+
+ test.Printf(_L("Test Completed with Error: %d"),status);
+
+ return status;
+ }
+
+
+// eof