diff -r 000000000000 -r e4d67989cc36 lowlevellibsandfws/genericusabilitylib/example/src/euserhl_walkthrough.cpp --- /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 +#include +#include +#include + + + +// 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 + 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 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 one(CStringUserTwoPhase::NewL(KOne)); + test.Printf(_L("Single phase name: %S\n"), &one->Name()); + + LCleanedupPtr 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, 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 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 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 iTicker; + LManagedHandle 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 iTicker; + LManagedHandle 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(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 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 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 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 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 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 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 t(static_cast(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 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 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, 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 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 rar; + // calls to functions that cannot leave here + rar.Append(HBufC::NewL(5)); + rar.Append(HBufC::NewL(5)); + + + LCleanedupRef, 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 wrapped(raw); + + // Never do this: + //LCleanedupPtr 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 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 managedFs; + managedFs->Connect(); + //default cleanup strategy is to call RFs::Close() on scope exit + } + + { + LCleanedupHandle 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 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 cFile; + + test.Printf(_L("Size of LCleanedupHandle = %d"), sizeof(cFile)); + + LCleanedupRef crFile(file); + + test.Printf(_L("Size of LCleanedupRef = %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 cTracker(tracker); + + test.Printf(_L("Size of LCleanedupHandle = %d"), sizeof(LCleanedupPtr)); + } + +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