안드로이드 Platform : Porting / Devices / Source / CTS / TV / GMS 
Dynamic Library Design Guidelines[윗글 연속]
작성자
작성일 2008-03-04 (화) 19:11
ㆍ추천: 0  ㆍ조회: 2109      
IP: 221.xxx.120

C++–Based Libraries

Using C++ to implement a dynamic library presents a couple of challenges—mainly exporting symbol names and creating and destroying objects. The following sections detail how to export symbols from a C++–based dynamic library and how to provide clients with functions that create and destroy instances of a class.

Exporting C++ Symbols

C++ uses name mangling to encode size and type information in a symbol name. Name mangling in C++ makes exporting symbols in a standard way across different platforms impossible. When a dynamic library is compiled, each symbol is renamed to include that information, but the encoding used is not standard between platforms. The dynamic loader uses string matching to locate symbols at runtime; the name of the symbol searched must match exactly the name of the target. When the symbol names have been mangled, the dynamic loader has no way of knowing how the name of the symbol it’s searching for has been encoded.
To export nonmember symbols from a C++–based dynamic library so that the dynamic loader can find it, you must add the extern "C" directive to the symbols’ declarations. This keyword tells the compiler not to mangle the symbol name. For example, the following declaration makes the NewPerson function available to the library’s clients:
<PRE>extern "C" Person* NewPerson(void);</PRE>
Without this directive, the function name could be changed to _Z9NewPersonv , which would make it impossible for the dynamic loader to find the NewPerson symbol at runtime.
The only nonmember functions that must be exported are constructors and destructors, especially in dynamic libraries that can be used by clients as dependent libraries instead of runtime-loaded libraries. This is because clients must have access to a class’s constructors and destructors so that they can use the new and delete operators on the class.

Defining C++ Class Interfaces

A dynamic library should always publish its public interface to clients through header files. (Although clients can use dynamic libraries without their header files, doing so is very difficult and is prone to error.) The header file for a class that’s available to clients must include the declarations of its public methods. Dynamic libraries that make a class available to its clients must include the virtual keyword in the declaration of all the class’s methods, except for its constructors and destructors. For example, Listing 9 shows the declaration for the Person class.

Listing 9  Declaration for the Person class

<PRE>class Person {</PRE>
<PRE> private:</PRE>
<PRE> char _person_name[30];</PRE>
<PRE> public:</PRE>
<PRE> Person();</PRE>
<PRE> virtual void set_name(char person_name[]);</PRE>
<PRE> virtual char* name();</PRE>
<PRE>};</PRE>

Creating and Destroying C++ Objects

When a C++–based dynamic library is a dependent library of its client, the client can use the new and delete operators to create and destroy instances of a class the library defines. However, clients that open a C++–based library at runtime through dlopen do not have access to the library’s constructors because the constructors are exported with their names mangled, preventing the dynamic loader from locating them. See Listing 11 for details about name mangling.
Clients that load a library at runtime to use a class must have a way to create and destroy a class’s objects without using the new and delete operators. A library provides this functionality to its clients by exporting at least two class factory functions. Class factory functions create and destroy objects of a class on behalf of a class’s user. Creator functions create an object of the class and return a pointer to it. Destructor functions dispose of an object created by a creator function for the same class. In addition to the factory functions, the library must define a data type for each exported factory function.
For example, Listing 10 shows the header and implementation files of the Person class, exported by the Person library. The header file includes the declaration and type definition of a pair of factory functions, NewPerson , and DeletePerson :

Listing 10  Interface and implementation of a C++ class in a dynamic library

<PRE>/* File: Person.h */</PRE>
<PRE>class Person {</PRE>
<PRE> private:</PRE>
<PRE> char _person_name[30];</PRE>
<PRE> public:</PRE>
<PRE> Person();</PRE>
<PRE> virtual void set_name(char person_name[]);</PRE>
<PRE> virtual char* name();</PRE>
<PRE>};</PRE>
<PRE> </PRE>
<PRE>// Constructor function and function type.</PRE>
<PRE>extern "C" Person* NewPerson(void);</PRE>
<PRE>typedef Person * Person_creator(void);</PRE>
<PRE> </PRE>
<PRE>// Destructor function and function type.</PRE>
<PRE>extern "C" void DeletePerson(Person* person);</PRE>
<PRE>typedef void Person_disposer(Person*);</PRE>
<PRE> </PRE>
<PRE>/* File: Person.cpp */</PRE>
<PRE>#include <iostream></PRE>
<PRE>#include "Person.h"</PRE>
<PRE> </PRE>
<PRE>#define EXPORT __attribute__((visibility("default")))</PRE>
<PRE> </PRE>
<PRE>EXPORT</PRE>
<PRE>Person::Person() {</PRE>
<PRE> this->set_name("<no value>");</PRE>
<PRE>}</PRE>
<PRE> </PRE>
<PRE>EXPORT</PRE>
<PRE>Person* NewPerson(void) {</PRE>
<PRE> return new Person;</PRE>
<PRE>}</PRE>
<PRE> </PRE>
<PRE>EXPORT</PRE>
<PRE>void DeletePerson(Person* person) {</PRE>
<PRE> delete person;</PRE>
<PRE>}</PRE>
<PRE> </PRE>
<PRE>void Person::set_name(char name[]) {</PRE>
<PRE> strcpy(_person_name, name);</PRE>
<PRE>}</PRE>
<PRE> </PRE>
<PRE>char* Person::name(void) {</PRE>
<PRE> return _person_name;</PRE>
<PRE>}</PRE>
Listing 11 shows how a client might use the Person library.

Listing 11  Client using a C++ class implemented in a runtime loaded library

<PRE>/* File: Client.cpp */</PRE>
<PRE> </PRE>
<PRE>#include <iostream></PRE>
<PRE>#include <dlfcn.h></PRE>
<PRE>#include "Person.h"</PRE>
<PRE> </PRE>
<PRE>int main() {</PRE>
<PRE> using std::cout;</PRE>
<PRE> using std::cerr;</PRE>
<PRE> </PRE>
<PRE> // Open the library.</PRE>
<PRE> void* lib_handle = dlopen("./libPerson.dylib", RTLD_LOCAL);</PRE>
<PRE> if (!lib_handle) {</PRE>
<PRE> cerr << "[" << __FILE__ << "] main: Unable to open library: "</PRE>
<PRE> << dlerror() << "n";</PRE>
<PRE> exit(EXIT_FAILURE);</PRE>
<PRE> }</PRE>
<PRE> </PRE>
<PRE> // Get the NewPerson function.</PRE>
<PRE> Person_creator* NewPerson = (Person_creator*)dlsym(lib_handle, "NewPerson");</PRE>
<PRE> if (!NewPerson) {</PRE>
<PRE> cerr << "[" << __FILE__ << "] main: Unable to find NewPerson method: "</PRE>
<PRE> << dlerror() << "n";</PRE>
<PRE> exit(EXIT_FAILURE);</PRE>
<PRE> }</PRE>
<PRE> </PRE>
<PRE> // Get the DeletePerson function.</PRE>
<PRE> Person_disposer* DeletePerson =</PRE>
<PRE> (Person_disposer*)dlsym(lib_handle, "DeletePerson");</PRE>
<PRE> if (!DeletePerson) {</PRE>
<PRE> cerr << "[" << __FILE__</PRE>
<PRE> << "] main: Unable to find DeletePerson method: "</PRE>
<PRE> << dlerror() << "n";</PRE>
<PRE> exit(EXIT_FAILURE);</PRE>
<PRE> }</PRE>
<PRE> </PRE>
<PRE> // Create Person object.</PRE>
<PRE> Person* person = (Person*)NewPerson();</PRE>
<PRE> </PRE>
<PRE> // Use Person object.</PRE>
<PRE> cout << "[" << __FILE__ << "] person->name() = " << person->name() << "n";</PRE>
<PRE> person->set_name("Floriane");</PRE>
<PRE> cout << "[" << __FILE__ << "] person->name() = " << person->name() << "n";</PRE>
<PRE> </PRE>
<PRE> // Destroy Person object.</PRE>
<PRE> DeletePerson(person);</PRE>
<PRE> </PRE>
<PRE> // Close the library.</PRE>
<PRE> if (dlclose(lib_handle) != 0) {</PRE>
<PRE> cerr << "[" << __FILE__ << "] main: Unable to close library: "</PRE>
<PRE> << dlerror() << "n";</PRE>
<PRE> }</PRE>
<PRE> </PRE>
<PRE> return 0;</PRE>
<PRE>}</PRE>
For further details on how clients use C++ classes implemented in dynamic libraries, see “Using C++ Classes.”

Objective-C–Based Libraries

There are a few issues to consider while designing or updating an Objective-C–based dynamic library:
  • Publishing the public interface of an Objective-C class or category is different from the way symbols are exported in C.
    In Objective-C every method of every class is available at runtime. Clients can introspect classes to find out which methods are available. However, so that client developers don’t receive a flurry of warnings about missing method implementations, library developers should publish the interface to their classes and categories as protocols to client developers.
  • Objective-C–based libraries have access to more initialization facilities than those available to C-based libraries.
  • Objective-C has an class-alias facility that allows library developers to rename classes in a revision but allow clients to link against that revision to continue using the names used in earlier revisions.
The following sections explore these areas in detail.

Defining Class and Category Interfaces

Because client developers generally don’t have access to the implementation of Objective-C classes and categories defined in dynamic libraries, library developers must publish the public interfaces of classes and categories as protocols in header files. Client developers compile their products using those header files and are able to instantiate the classes correctly by adding the necessary protocol names to variable definitions. Listing 12 shows the header and implementation files of the Person class in an Objective-C–based library. Listing 13 shows the header and implementation files of the Titling category in the same library, which adds the -setTitle method to the Person class.

Listing 12  Header and implementation files of the Person class

<PRE>/* File: Person.h */</PRE>
<PRE>#import <Foundation/Foundation.h></PRE>
<PRE> </PRE>
<PRE>@protocol Person</PRE>
<PRE>- (void)setName:(NSString*)name;</PRE>
<PRE>- (NSString*)name;</PRE>
<PRE>@end</PRE>
<PRE> </PRE>
<PRE>@interface Person : NSObject <Person> {</PRE>
<PRE> @private</PRE>
<PRE>NSString* _person_name;</PRE>
<PRE>}</PRE>
<PRE>@end</PRE>
<PRE> </PRE>
<PRE>/* File: Person.m */</PRE>
<PRE>#import <Foundation/Foundation.h></PRE>
<PRE>#import "Person.h"</PRE>
<PRE> </PRE>
<PRE>@implementation Person</PRE>
<PRE>- (id)init {</PRE>
<PRE> if (self = [super init]) {</PRE>
<PRE> _person_name = @"";</PRE>
<PRE> }</PRE>
<PRE>}</PRE>
<PRE> </PRE>
<PRE>- (void)dealloc {</PRE>
<PRE> [_person_name release];</PRE>
<PRE> [super dealloc];</PRE>
<PRE>}</PRE>
<PRE> </PRE>
<PRE>- (void)setName:(NSString*)name {</PRE>
<PRE> [_person_name release];</PRE>
<PRE> _person_name = [name retain];</PRE>
<PRE>}</PRE>
<PRE> </PRE>
<PRE>- (NSString*)name {</PRE>
<PRE> return _person_name;</PRE>
<PRE>}</PRE>
<PRE>@end</PRE>

Listing 13  Header and implementation files of the Titling category to the Person class

<PRE>/* File: Titling.h */</PRE>
<PRE>#import <Foundation/Foundation.h></PRE>
<PRE>#import "Person.h"</PRE>
<PRE> </PRE>
<PRE>@protocol Titling</PRE>
<PRE>- (void)setTitle:(NSString*)title;</PRE>
<PRE>@end</PRE>
<PRE> </PRE>
<PRE>@interface Person (Titling) <Titling></PRE>
<PRE>@end</PRE>
<PRE> </PRE>
<PRE>/* File: Titling.m */</PRE>
<PRE>#import <Foundation/Foundation.h></PRE>
<PRE>#import "Titling.h"</PRE>
<PRE> </PRE>
<PRE>@implementation Person (Titling)</PRE>
<PRE>- (void)setTitle:(NSString*)title {</PRE>
<PRE> [self setName:[[title stringByAppendingString:@" "]</PRE>
<PRE> stringByAppendingString:[self name]]];</PRE>
<PRE>}</PRE>
<PRE>@end</PRE>
Listing 14 shows how a client might use the library.

Listing 14  Client using the Person library

<PRE>/* File: Client.m */</PRE>
<PRE>#import <Foundation/Foundation.h></PRE>
<PRE>#import <dlfcn.h></PRE>
<PRE>#import "Person.h"</PRE>
<PRE>#import "Titling.h"</PRE>
<PRE> </PRE>
<PRE>int main() {</PRE>
<PRE> NSAutoreleasePool* pool = [NSAutoreleasePool new];</PRE>
<PRE> </PRE>
<PRE> // Open the library.</PRE>
<PRE> void* lib_handle = dlopen("./libPerson.dylib", RTLD_LOCAL);</PRE>
<PRE> if (!lib_handle) {</PRE>
<PRE> NSLog(@"[%s] main: Unable to open library: %sn",</PRE>
<PRE> __FILE__, dlerror());</PRE>
<PRE> exit(EXIT_FAILURE);</PRE>
<PRE> }</PRE>
<PRE> </PRE>
<PRE> // Get the Person class (required with runtime-loaded libraries).</PRE>
<PRE> Class Person_class = objc_getClass("Person");</PRE>
<PRE> if (!Person_class) {</PRE>
<PRE> NSLog(@"[%s] main: Unable to get Person class", __FILE__);</PRE>
<PRE> exit(EXIT_FAILURE);</PRE>
<PRE> }</PRE>
<PRE> </PRE>
<PRE> // Create an instance of Person.</PRE>
<PRE> NSLog(@"[%s] main: Instantiating Person_class", __FILE__);</PRE>
<PRE> NSObject<Person,Titling>* person = [Person_class new];</PRE>
<PRE> </PRE>
<PRE> // Use person.</PRE>
<PRE> [person setName:@"Perrine LeVan"];</PRE>
<PRE> [person setTitle:@"Ms."];</PRE>
<PRE> NSLog(@"[%s] main: [person name] = %@", __FILE__, [person name]);</PRE>
<PRE> </PRE>
<PRE> // Dispose of person.</PRE>
<PRE> [person release];</PRE>
<PRE> </PRE>
<PRE> // Close the library.</PRE>
<PRE> if (dlclose(lib_handle) != 0) {</PRE>
<PRE> NSLog(@"[%s] Unable to close library: %sn",</PRE>
<PRE> __FILE__, dlerror());</PRE>
<PRE> exit(EXIT_FAILURE);</PRE>
<PRE> }</PRE>
<PRE> </PRE>
<PRE> [pool release];</PRE>
<PRE> return(EXIT_SUCCESS);</PRE>
<PRE>}</PRE>

Initializing Objective-C Classes

Objecive-C–based dynamic libraries provide several initialization facilities for modules, classes, and categories. The following list describes those facilities in the order they are executed.
  1. The +load method: Initializes resources needed by a class or a category. The Objective-C runtime sends the load message to every class a library implements; it then sends the load message to every category the library implements. The order in which sibling classes are sent the load message is undetermined. Implement the +load method to initialize resources needed by a class or category. Note that there’s no corresponding “unload” method.
  2. Module initializers: Initializes a module. The dynamic loader calls all the initializer functions (defined with the constructor attribute) in each of a library’s modules. See “Module Initializers and Finalizers” for more information on module initializers.
  3. The +initialize method: Initializes resources needed by instances of a class before any instances are created. The Objective-C runtime sends the initialize message to a class just before creating an instance of the class. Note that there’s no corresponding “finalize” message sent to a class when the library is unloaded or the process terminates.

Creating Aliases to a Class

When you rename a class in a revision of a dynamic library, you can reduce the adoption burden to client developers by adding an alias to the new name in the library’s header file. This practice allows client developers to release clients that take advantage of the new version of the library quickly. Client developers can later update references to the class at their leisure.

Design Guidelines Checklist

This list provides a summary of the guidelines for improving specific aspects of a dynamic library:
  • Ease of use
    • Reduce the number of symbols a library exports.
    • Provide unique names to public interfaces.
  • Ease of maintenance
    • Export accessor functions to variables. Don’t export variables.
    • Implement public interfaces as wrappers to internal, private interfaces.
  • Performance
    • Minimize the number of references to symbols in dependent libraries. Use dlsym(RTLD_GLOBAL, <symbol_name>) to obtain the address of symbols exported by dependent libraries when they are needed.
    • Minimize the number of dependent libraries. Consider loading libraries with dlopen when absolutely necessary. Remember to close the library with dlclose when done.
    • Implement public interfaces as wrappers to internal, private interfaces.
  • Compatibility
    • Export symbols as weakly linked symbols.
    • Encode a library’s major version number in its filename.
 
 
 
이름아이콘 rndmania
2009-06-10 20:09
원문이 apple로 링크가 걸려있는거 같은데 안드로이드에도 동일하게 적용할 수 있는건가요?
   
 
덧글 쓰기 0
3500
※ 회원등급 레벨 0 이상 읽기가 가능한 게시판입니다.