class ClassLoader

A ClassLoader attempts to create an instance of the given class, using a dynamically-loaded shared object file. More...

Full namefun::ClassLoader
Definition#include <fun/ClassLoader.h>
List of all Methods
Annotated List

Public Methods

Public Static Methods

Public Members

Detailed Description

A ClassLoader attempts to create an instance of the given class, using a dynamically-loaded shared object file. (C++ probably has a better way to do this, but I don't know what it is.)

The classes it instantiates must have a constructor which takes no arguments, and the shared object which instantiates the class must contain a symbol void *new[classname]() which returns a new instance of the class. (The macro "INSTANTIATOR(classname)" or "NAMESPACE_INSTANTIATOR(namespace, classname)" defined in fun.h will do this for you.) Your class must also inherit (at some point in its ancestry) from LoadableClass.

By default, a ClassLoader instance just looks in your LD_LIBRARY_PATH for a shared object named [classname].so. However, there are several ways in which you can change a ClassLoader's behavior.

If there's a specific directory which your application uses for its class definitions, or for class definitions of a particular kind of class, create a new ClassLoader and call setDSODir("/path/to/dsos"). Each time the ClassLoader is asked to instantiate a class, it will look in that directory for [classname].so.

If your application uses a single specific DSO which you want to load a bunch of classes from, create a new ClassLoader, and call setDSO("/path/to/"), or dlopen() the DSO yourself and call setDSOHandle(handle). Each time the ClassLoader is asked to instantiate a class, it will look in that DSO.

If you want to be able to load classes from multiple directories or DSO's, you can create a ClassLoader for each and chain them together with setNextLoader(). If the first ClassLoader is unable to instantiate the class, the next in line will try. All ClassLoaders share the same cache, so subsequent attempts to instantiate the class will not require the list to be traversed again.

Also, if a ClassLoader fails to find the shared object it's looking for, or fails to find the symbol it expects in the shared object, it will attempt to load the symbol from the main program as a last-ditch effort. In order for this to work (and you probably do, unless you want to require a shared library), your program must allow its own symbols to be resolved with dlsym; using automake and libtool, add "-export-dynamic" to your program's LDFLAGS.

ClassLoaders cache the results of their class lookups, regardless of their outcome; no class is ever searched for twice, unless it's been deliberately removed from the cache through uncache() or clearCache(). As a result, ClassLoader::instantiate() is pretty fast; 1000000 calls to "new" and 1000000 calls to instantiate() each take about a second on my machine.

All ClassLoaders share the same cache of class information; the difference between ClassLoader instances is the list of places they look for the shared object to open. This means that if a class has been loaded by one ClassLoader, attempts to load another class of the same name through another ClassLoader will return the first class, which might not be what we want.

bool debug


 ClassLoader ()


Creates a ClassLoader with the default class-finding behavior.

 ~ClassLoader ()



Cleans up the ClassLoader; if this ClassLoader opened its own shared object, it will dlclose() it here.

void  setDSODir (const char *path)


Cause the ClassLoader to look for class DSO's in the given directory during subsequent calls to the ClassLoader's instantiate() method. For namespace-qualified class names like foo::Bar, ClassLoader really will look for a file named "".

const char *  getDSODir ()


void  setDSO (const char *path)


Cause the ClassLoader to open the given shared object and look in it for classes during subsequent calls to the ClassLoader's instantiate() method. This is passed on to dlopen(), so it will be searched for in the LD_LIBRARY_PATH if it doesn't start with a "/". (or a . or something... not sure what the rule is.)

void  setDSOHandle (void *handle)


Cause the ClassLoader to use the given DSO handle during subsequent calls to the ClassLoader's instantiate() method. Because this does not cause the ClassLoader to open the DSO, the DSO will not be closed by ClassLoader::emptyCache(), and will continue to be used by the ClassLoader instance to which it was passed.

void  setNextLoader (ClassLoader *next, bool deleteWhenDeleted = false)


If this ClassLoader fails to locate an instantiator, it will pass the request on to the next ClassLoader in line. next may be NULL.

Note that because all ClassLoaders share the same class cache, instantiate() will only recurse the first time a given class is instantiated, if at all. Subsequent instantiations can be handled by "this" ClassLoader, not the next one in line.

If deleteWhenDeleted is true, this ClassLoader is responsible for the next ClassLoader's memory, and will delete the next ClassLoader when it is itself deleted. This lets you create a chain of ClassLoaders, and keep a pointer to only the head of the chain.

void  setLastLoader (ClassLoader *last, bool deleteWhenDeleted = false)


Passes the given ClassLoader to setNextLoader() on the last ClassLoader in the chain.

If you're reading the names of DSO's or directories from the command line or an environment variable, and want to search them in the order given, you can keep just the head of the list, and pass subsequent ClassLoaders to its setLastLoader() to append them to the end of the list. See setNextLoader() for more information.

LoadableClass * instantiate (const char *className)



The moment ClassLoader lives for. Attempts to create an instance of the given class name. XXX what about error handling? Add an equivalent to dlerror()?

template void  instantiate (const char *className, LC *&)


A convenience version of instantiate() which makes sure the instantiated object is of the given type, and assigns it to the given pointer if so. (Otherwise it deletes it again.)

void  translate (const char *from, const char *to)



Causes future calls to this ClassLoader's instantiate("from") method to return instantiate("to") instead. This can be useful if you have a case where you always want a specific subclass to be returned instead of a base class, or if you want to associate some symbolic name with a class.

Passing NULL as the "to" argument causes the translation to stop being performed.

Note that translations are performed on the given class names, but in translate(), not instantiate(). For example, if you translate("foo", "bar"), and then translate("bar", "baz"), subsequent attempts to instantiate "foo" will give you "baz", as you would expect, not "bar". However, because the translation was done in translate(), not instantiate(), a later call to translate("bar", NULL) will leave "foo" mapped to "baz", instead of causing it to revert to "bar". While not exactly what you might expect, it makes instantiate() faster, which is most important.

const char * getTranslation (const char *className)



Returns the class name which will be substituted for the given name, or NULL if the given class name is not mapped to another.

QStringList  getTranslations ()



Returns all of the translated class names. You'll have to call getTranslation() for each element in the list to find out what it's mapped to.

void  clearTranslations ()



Undoes the effect of all translate() calls on this ClassLoader.

typedef LoadableClass *  (*ObjectFactory) ()


registerObjectFactory() registers a classname with the classloader, mapping it to a function which is capable of creating objects of that class name (which may be any key - not necessarily a formal class name). Note that this is essentialy useless for mapping to DLL-loaded factories, since the factory function cannot be pointed to at link-time. However, perhaps it can be used by the classloader to determine which symbol in a DLL to use for purposes of creating a new class (instead of hard-coding the value set via the INSTANTIATOR() family of macros, for example).

It's original intention is to allow dynamic loading of template classes or other types with "odd" names.

It's experimental, in any case.

Justification for it being static is: C++ would not allow the same class with different definitions, so it would be useless to be able to store such conflicting info in two classloaders in the same app. It could be argued that specific classloaders should not know about specific classes, or may use different paths, may use the same key for logically-unrelated classes, etc., so there are certainly several valid arguments for not making this static. The fact is, however, that having it static greatly simplifies it's usage in libGCom ;).

Note that this function uses the same internal class table as the "classic" methods, so it can be used to override a particular class instantiator function. e.g., if you want to use a custom loader for Foo, instead of relying on being found:

ClassLoader::registerObjectFactory( "Foo", myFooFunction );

Sample theoretical usage:

LoadableClass * myFactory() { return new Foo(); } ClassLoader::registerObjectFactory( "MagicMysteryFoo", myFactory ); ... Foo *foo = dynamic_cast( myclassloader->instantiate( "MagicMysteryFoo" ) );

This code is largely taken from/inspired by Andrei Alexandrescu's "Modern C++ Design", chapter 8.

void  registerObjectFactory ( const QString &classname, ClassLoader::ObjectFactory fp )



QStringList  getClassNames (bool openFiles = false)


Gets the list of class names which this ClassLoader believes it can instantiate.

ClassLoaders for whom setDSODir() has been called look at the list of files in their DSO directory.

ClassLoaders who load multiple classes from a single DSO or open DSO handle look for a symbol, classLoaderClassListFP, and treat it as a pointer to a function which returns a const char * containing a whitespace-delimited list. (Remember such a symbol should be declared extern "C".) For example, if you put your list of classes in the Makefile variable foo_classlist, and add -DFOO_CLASSLIST="\"$(foo_classlist)\"" to the list of compile-time flags, your classLoaderClassListFP could be:

extern "C" const char *classLoaderClassListFP() { return FOO_CLASSLIST; }

ClassLoaders who will be loading DSO's from the default path don't attempt to determine which DSO's in the path might contain loadable classes.

If openFiles is true, the ClassLoader will attempt to actually dlopen() files which it believes contain loadable classes, and retrieve their instantiators. This should yeild more accurate (but slower) results. (Err, scratch that; that's not implemented.)

If setNextClassLoader has been called on this ClassLoader, it will append its list to the list returned by the "next" ClassLoader, and will not attempt to remove duplicates. (I didn't see a QStringList/QValueList member for getting a sorted unique list, so see ClassLoader::sortUniq(QStringList &).)

void  uncache (const char *className)



This removes the given class from the cache; the next attempt to instantiate() the class will cause its shared object to be reopened and reexamined.

void  emptyCache ()



This destroys the cache of class info; subsequent calls to any instantiate() method will cause shared objects to be reopened and reexamined as needed.

void  sortUniq (QStringList &list)



Utility function for the missing (?) "sort unique" QValueList member function.

QString  decodeTypeIDName (const char *)



"With the version of GCC on my box," typeid(*foo).name() comes out as "N3fun3FooE" instead of the more aesthetically pleasing "fun::Foo". This attempts to return the demangled name. (Well, it's not exactly mangled... I don't know what it is. c++filt, for example, doesn't know what to do with it.)

If this isn't able to figure out how to decode the given name, it just returns the original.

Generated by: stephan on cheyenne on Mon Aug 11 14:06:52 2003, using kdoc 2.0a54.