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