ResourceFinder - an object for searching through filesystem paths.
* Copyright (C) 2002 stephan beal (email@example.com). * All rights reserved. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 2 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * ********************************************************************
This is just a simple interface for objects which have some sort of description string which can be set and retrieved. I wanted it to be a QObject so that it could emit a signal when its description changed; see Nameable.h for why it isn't.
ConfigFile loads and saves configuration files which can be represented as simple key/value pairs of strings. They can be loaded from & saved to plain text or XML files.
The most interesting methods are probably getString(), getInt(), etc., as those are how you get at the configuration values loaded from file. If you need to know whether a value was explicitly set in the file, use isSet(). That might be a little cumbersome; hopefully there aren't that many cases where it will matter whether a value was explicitly set, or couldn't be interpreted as an int or whatever.
The text format used will treat lines ending in "\" as continued on the next line. \t and \n will be expanded into tab and newline during reading, and will be escaped again during writing.
This is just a simple interface for objects which have some sort of name string which can be set and retrieved. I wanted it to be a QObject so that it could emit a signal when its name changed, but that added a lot of stuff (including 8 pointers per object!), and moc can't handle (or "can almost handle") a class which inherits from two QObject subclasses at once (such as Nameable & Describable).
Also, I tried making this & Describably be virtual public Serializables, but then I got SEGV's during deserialization. Nuts!!
Serializer is an abstract base class for objects which can store Serializable objects to files or streams. Depending on the subclass' implementation, the data may be stored natively, or it may be converted to strings, or stuck in a database, etc.
Think of a Serializer as taking key/value pairs of data, where the values may be Serializable objects, which implies a tree structure. Duplicate keys are allowed, but note that the only way to get at the values for duplicate keys in a Deserializer is to use one of the forms of get() which takes a reference to a list of Serializables or QStrings.
As Serializer is abstract, subclasses must provide implementations for the following methods: - put(const QString &key, const QString &val) - put(const QString &key, const Serializable &val) - setSerializableClass(const char *name) Also, setComment() does nothing in the base class.
I didn't want to make this public, but if you want to plug in your own die roll parsers, you'll need it to return a subclass of this. Each method has the same semantics as the DieRoll method of the same name.
|typedef SolidPlastic * (*PluginDieRollParser) (const QString &)|
To plug in your own parsers, you'll need a PluginDieRollParser. It will be
A DieRoll is a fixed collection of dice, such as 3d6 or 1d20. Its most useful method is roll(), although it does have methods such as min(), max(), isFlat(), and distribution() for examining its expected results.
DieRoll instances are not constructed directly; use DieRoll::fromString().
|extern std::ostream & operator<< (std::ostream &os, const DieRoll &d)
Writes the given DieRoll's toString() to the ostream.
This is a wrapper around QProgressDialog for classes which want to report progress information, but don't want to know whether there's a QApplication which needs to have processEvents() called, or whether other chunks of code already have a progress dialog up, etc.
If there are places where you notice multiple progress dialogs popping up in series, you can fix that by nesting them in an outer start()/finish() pair. For example, when loubetomy loads a map, it performs three steps:
(If the library code was responsible for fondling the progress dialog itself, it would be more complicated, and either there would be gaps when the program was busy but no dialog was up, or there would be points when two progress dialogs were up, & in either case it would be messier for loubetomy to know whether the user had hit "cancel" in the dialog displayed by the library.)
This is an extreme bummer. ClassLoader::instantiate() wasn't working when INSTANTIATOR(FooClass) was returning a void * instead of a pointer to a polymorphic object. (Nuts!) So unfortunately this stuff must intrude even more into your code. Any class loaded & instantiated by ClassLoader must inherit from LoadableClass.
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/your.so"), 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.
This class provides a generic hashtable-like interface for perstantly storing arbitrary key/value pairs.
Deserializer is an abstract base class for objects which can retrieve Serializable objects from files or streams.
See Serializer for additional notes.
As Deserializer is abstract, subclasses must provide implementations for:
- get(const QString &, QString &, const QString &) const
- get(const QString &, QStringList &list) const
- get(const QString &, Serializable *&, const QString &, Serializable *) const
- get(const QString &, std::vector
Note that Deserializer error handling currently sucks vacuously: there isn't any. Some of the methods which could be complex operations should throw exceptions or something. I know! I'll wait until there's a lot of code using this, and then change the API!
Serializable objects can save and restore themselves from Serializers and Deserializers.
As a Serializable is a LoadableClass, somewhere in the source file for class Foo, you need this line:
You also want to make sure the serialize() method for the Foo class has the line
after calling any base class serialize() methods.
See Serializer and Deserializer for methods for storing and retrieving elements from (S|Des)erializers, and for examples.
Right now, there's no error handling during loading etc.; this should throw exceptions.
This is another simple command-line parser.
This one takes references to the variables which you want to fill from the command line, and
For example, if you take an input file & output file, some integer, and a boolean flag, you might declare these variables:
QString infile("foo.in"), outfile("foo.out"); int its = 10; bool hose = false;
and register the arguments (and their descriptions) like this:
OtherSimpleCLParser args; args.add("i filename", infile, "the input file"); args.add("o filename", outfile, "the output file"); args.add("it iterations", its, "number of times to iterate"); args.add("h", hose, "hose things up"); ... args.parse(argc, argv);
If there were errors, a usage message was generated & printed, and the program was terminated. You can do additional checking afterwards:
if (infile == outfile) args.usage("infile == outfile, you bozo");
When you're all done checking the command line arguments, let the OtherSimpleCLParser object go out of scope, or call its dispose() method. (The variables you gave it will continue to contain the values which were passed on the command line, of course.)
SerialTree is a simple Serializer and Deserializer which currently can be saved to & loaded from XML if your Qt has XML support. It should also have saveToTextFile and loadFromTextFile methods, but at the moment, it doesn't.
I would probably be using STL if I had a larger brain.
The templated deserialize() is NOT a member function because I couldn't get the compiler to allow
a static template, declared as:
Another form of deserialize() which takes an XML string instead of a SerialTree. use like this: Foo *foo = ::deserialize( xml_string, (Foo *)NULL, "Foo" ); The last argument, a string, must be the base class of the node. For libGCom, this is always "GCom". todo: clsPtr should be const.
SerType class to be loaded MUST inherit from Serializable for these templates to work.
|Generated by: stephan on cheyenne on Mon Aug 11 14:06:52 2003, using kdoc 2.0a54.|