Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 14
Текст из файла (страница 14)
Many APIs contain a good mixture of both: the GUI, forinstance, calls your code so that you can handle events, but providesfunctions that your code calls to draw graphics.52SYMBIAN OS C++Types of FunctionWe have defined library functions and framework functions. However,throughout the book, we use other terms to describe the role of differenttypes of function.Of course, there is the C++ constructor. We almost always use the fullterm, including ‘C++’, because, as we’ll see in Chapter 4, many classesalso have a second-phase constructor, usually called ConstructL().There’s only one destructor, though, so in that context we do not usuallyneed to refer to it as the ‘C++ destructor’.Convenience functions are trivial wrappers for things that could otherwise be done with a smaller API. If a class contains two functions Foo()and Bar(), which do all that the class requires, you may often find thatcode using your API contains sequences such as this:x.Foo();x.Bar();or this:x.Foo(x.Bar());y = (x.Foo() + x.Bar()) / 2;In that case, you may wish to code some kind of convenience functionFooAndBar() that represents the sequence.
This reduces code size,reduces mistakes, and makes code easier to read.The cost of convenience is another function to design and document,and the risk of being tempted to produce many convenience functionswhich aren’t really all that necessary or even convenient – and then beingforced to maintain them for ever more, because people depend on them.This is a fine judgment call: sometimes we provide too few conveniencefunctions and sometimes too many.DLLs and Other API ElementsAn object-oriented system delivers APIs mainly as C++ classes, togetherwith their member functions and data.
Classes that form part of an APIare:• declared in header files• implemented in C++ source files• delivered in DLLs.APIS53Library APIs (or the library parts of a framework API) are delivered inshared DLLs with a .dll file extension. The DLL’s exported functionsare made available in a .lib file to the linker at program build time.Framework APIs are usually defined in terms of C++ classes containingvirtual functions and an interface specification for the user to provideimplementations for those virtual functions.It’s important to make sure that only the interface – not the implementation – is made available to programs that use the API.
Classes that arenot part of the API should not be declared in API header files, and theirfunctions should not be exported from the DLLs that implement them.Functions and data that belong to the API classes, but which are not partof the API, should be marked private.Besides classes, C++ APIs may contain entities such as: enumerations,constants, template functions and non-member functions.Exported FunctionsFor a non-virtual, non-inline member function to be part of an API, itmust be:• declared public in a C++ class that appears in a public header file• exported from its DLL.You can see exported functions marked in their header files withIMPORT_C, like this:class RTimer : public RHandleBase{public:IMPORT_C TInt CreateLocal();IMPORT_C void Cancel();IMPORT_C void After(TRequestStatus& aStatus,TTimeIntervalMicroSeconds32 anInterval);IMPORT_C void At(TRequestStatus& aStatus, const TTime& aTime);IMPORT_C void Lock(TRequestStatus& aStatus, TTimerLockSpec aLock);};The IMPORT_C macro says that the function must be imported froma DLL by the user of that API.
In the corresponding implementation, thefunction is marked EXPORT_C, which means that it is exported from theDLL. A function without IMPORT_C is not exported from its DLL andcannot, therefore, be part of the public API. These macros are definedin e32def.h.
Their implementations are compiler-dependent and differbetween compilers.54SYMBIAN OS C++Virtual and inline functions don’t need to be exported – they form apart of the API, even without IMPORT_C in the header file.If you are writing an API to be delivered in a DLL for use by otherDLLs, you need to mark your IMPORT_Cs and EXPORT_Cs carefully.If you are not writing APIs – or you are not encapsulating them in DLLsfor export – then you do not need to worry about how to use IMPORT_Cand EXPORT_C.
It’s enough to understand what they mean in the headersof an SDK.Virtual Functions and APIsC++ is not particularly well designed for API delivery. There is no way toprevent further override of a specific virtual function and there is no wayto guarantee that a function is not virtual without looking down all thebase classes to check for the virtual keyword. You can simulate Java’sfinal at the class level, however, by making the constructor private.The access control specifiers of C++ are not good for API deliveryeither. The meaning of public is clear enough, but protected makesa distinction between derived classes and other classes that doesn’t putthe boundary in the right place, since derivation is by no means the mostimportant vehicle for code re-use in object orientation.
The keywordprivate does not mean private when it comes to virtual functions: youcan override private virtual functions whether or not this was intendedby the designer of an intermediate class (derived from a base class).C++ has no language support for packaging APIs, except classes andheader files. This is why Symbian had to invent its own rules for DLLs.These design issues are most awkward when it comes to virtual functions.Best practice in Symbian OS C++ includes the following guidelines:• declare a function virtual in the base class and in any derived classfrom which it is intended to further derive and override (or implement)this function• when declaring a virtual function in a derived class, include a comment such as // from CCoeControl, to indicate from where thefunction was defined• use private in a base class to indicate that your base class (or itsfriends) calls this function – this is usually the case for frameworkfunctions.
If you don’t like friends or the framework function isdesigned to be called from another class, then make it public in thebase class• use private in a derived class for a framework function that isimplementing something in a framework base class.These guidelines are, admittedly, incomplete and they are not alwayshonored in Symbian OS code. But they’re good for most cases.TEMPLATES55Finally, there is another issue with virtual functions: if your class hasvirtual functions and you need to invoke the default C++ constructorfrom a DLL other than the one your class is delivered in, then you needto specify, and export, a default C++ constructor:class CFoo : public CBase{public:IMPORT_C CFoo();...};And then in the source code:EXPORT_C CFoo::CFoo(){}If you do not do this, a program that tries to create a default C++constructor for your class cannot do it, because constructors need tocreate the virtual function table and the information required is insideyour DLL.
It will produce a link error.3.5 TemplatesSymbian OS uses C++ templates extensively, for collection classes, fixedlength buffers and utility functions. The use of templates in Symbian OS isoptimized to minimize the size in ‘expanded’ template code – basically,by ensuring that templates are never expanded. The thin-template patternis the key to this.Symbian OS also uses numeric arguments in templates to indicatestring and buffer sizes.Thin-Template PatternThe thin-template pattern uses templates to provide a type-safe wrapperround type-unsafe code.
It works like this: code a generic base class, suchas CArrayFixBase, which deals in ‘unsafe’ TAny* objects. This classis expanded into real code that goes into a DLL. Then, code a templateclass that derives from this one, and uses inline type-safe functions suchas:template <class T>inline const T& CArrayFix<T>::operator[](TInt aIndex) const{return (*((const T*)CArrayFixBase::At(aIndex)));}56SYMBIAN OS C++This returns the item at position aIndex of type const T& in theCArrayFix<T> on which it is invoked. It acts as a type-safe wrapperaround At() in the base class, which returns the pointer at positionaIndex of type TAny*.This code looks pretty ugly, but the good news is that applicationprogrammers do not have to use it. They can simply use the template API:CArrayFix<TFoo>* fooArray;...TFoo foo = (*fooArray)[4];The template guarantees that this code is type-safe. The fact that theoperator[]() is expanded inline means that no more code is generatedwhen the template is used than if the type-unsafe base class had beenused.Numbers in TemplatesSometimes, the parameter to a template class is a number, rather than atype.
Here’s the declaration of TBuf, a buffer of variable length:template <TInt S> class TBuf : public TDes{...};You can then create a five-character buffer with:TBuf<5> hello;This uses the thin-template pattern too: here’s the inline constructor:template <TInt S>inline TBuf<S>::TBuf() : TDes(0,S){}It calls the TDes base-class constructor, passing the correct parameters,and then completes the default construction of a TBuf (a couple of extrainstructions).3.6 CastingCasting is a necessary evil. Old-style C provides casting syntax thatenables you to cast any type to any other type.
Over time, differentcasting patterns have emerged, including:CLASSES57• cast away the const property (but don’t change anything else)• cast to a related class (rather than an arbitrary cast)• reinterpret the bit pattern (effectively, old-style C casting).C++ provides individual keywords for each of these types ofcast: const_cast<>(), static_cast<>() and reinterpret_cast<>(). These should be used in preference to old-style C casting, as through doing this you get the benefit of C++ cast checking.Previous versions of the SDK defined macros which expanded to thesekeywords, for compatibility with older versions of GCC which did notsupport C++ casting.