[go: up one dir, main page]

Menu

[80c337]: / lpackage.h  Maximize  Restore  History

Download this file

588 lines (541 with data), 29.8 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
#ifndef LURCHPACKAGE
#define LURCHPACKAGE
#include <QObject>
#include <QMap>
#include <QPointer>
#include "lob.h"
#include "lobscript.h"
#include "lenv.h"
#include "flexibleevaluator.h"
class QScriptProgram;
/** \brief A Lurch Package functions like a Lurch library, but is compiled for speed
*
* Although all Lurch documents and libraries could be built on script code, it will be
* useful to have the most frequently used tools built in C++ for two reasons. First,
* they will be compiled, and thus much faster than script code. Second, they will be
* subjected to the same rigorous testing to which the rest of Lurch is subjected.
* Developers convert a library that should become C++ code into a LurchPackage.
* This can be done in phases, as needed. The first phase is simplest, as documented
* in \ref makepackageShellScript "the makepackage.sh section, below".
*
* Each LurchPackage subclass registers itself with the LurchPackage class and the QMetaType
* system, providing the specific document URN that it implements. The idea is that a
* LurchPackage is just like any other dependency, but instead of a document with scripts
* in it, it is a virtual document. Thus it implements asDocument(), so that it can look
* like a document from script land, but it also adds other features to the scripting
* environment beyond simply appearing as a document.
*
* LurchEnvironments create their own instances of LurchPackage subclasses, as they
* encounter documents that depend on the class's URN. Thus a LurchPackage object can assume
* that it has been created to live inside one environment only; other LurchEnvironments will
* create other instances of the same class if they need it. The important consequence of
* this for designing subclasses of LurchPackage is that, if you're writing a package that
* stores data about the currently loaded document, you only need to declare variables
* and store data for one document; other instances of your class will exist for other
* documents, and the creation of those instances and attaching them to other documents
* will be handled by LurchEnvironment.
*
* LurchEnvironment guarantees three things in its relationship to LurchPackage subclasses.
* <ol>
* <li>The virtual document provided by asDocument() will be scanned for auto-run scripts
* just as if it were a real document. This enbles the simple conversion of scripts
* to packages using \ref makepackageShellScript "the makepackage.sh script described
* below". Such scanning takes place when the document is loaded, and again in a
* fresh scripting environment if important changes take place that necessitate
* it.</li>
* <li>Immediately before any such scan of a LurchPackage's asDocument() representation,
* there will be a call to that object's setup() routine. This enables any
* script-land setup that needs to be done from the C++ side to happen before any
* scripts in asDocument() are called.</li>
* <li>Whenever a new document is loaded, after the load complete successfully, the
* initialize() member of every package on which it depends is called. This is so
* that any internal data the package wants to track about the document and/or its
* dependencies can be initialized. If the document wants to track changes to the
* document and update any internal data on an ongoing basis, it should use the
* initialize() call to connect one of its slots to the environment's
* documentModified() signal. (See initialize() documentation, below.)</li>
* </ol>
*
* \anchor makepackageShellScript
* <h2>The <code>makepackage.sh</code> script</h2>
* Take your existing .lurch file,
* and simply paste it into a giant C++ string inside a subclass definition, like this.
* \code
* doc = Lob::fromXML(
* "<OMATTR>\n"
* " <OMATP>\n"
* " ...\n"
* " </OMATP>\n"
* " <OMA>\n"
* " <OMS name=\"document\" cd=\"LurchCore\"/>\n"
* " ...\n"
* " Make this an enormous C string containing\n"
* " all the data in your existing .lurch file.\n"
* " ...\n"
* " </OMA>\n"
* "</OMATTR>\n"
* ).child().copy();
*
* // ...
*
* Lob asDocument () const
* {
* return doc;
* };
* \endcode
* A LurchPackage created this way tells the world about itself solely through
* its Lob form, which is what a .lurch file does anyway. So this is a simple wrapping
* of a .lurch file in a LurchPackage, and thus is a very easy way to create a LurchPackage
* from an existing library that is in a .lurch file.
* In fact, there is a handy shell script
* <a href='http://lurch.svn.sourceforge.net/viewvc/lurch/Lurch/trunk/makepackage.sh'
* >makepackage.sh</a> that will do this conversion for you, reading in a .lurch file
* and outputting a .h file and a .cpp file.
*
* \anchor howToWriteALurchPackage
* <h2>How to write a LurchPackage subclass</h2>
*
* To subclass LurchPackage, you need to follow these steps. Failure to do so will result
* in your package not being registered with the master list kept in static data structures
* of the LurchPackage class, and thus not being visible to LurchEnvironment, documents,
* scripts, etc.
* <ol>
* <li>Subclass LurchPackage as usual, implemeting all pure virtual functions and
* whatever other functions are necessary for your particular package.
* (There are two virtual functions you do not need to implement, metaTypeID()
* and classURN(); these will be implemented for you by the LURCH_PACKAGE macro,
* described in the following step.)
* Be sure to provide your package with an URN unique to your new class
* in your implementation of getURN(). When implementing asDocument(),
* a good place to start is with Lob::newDocument( getURN() ); thereafter,
* comments or scripts can be added as children.</li>
* <li>In your header file:<br>
* Ensure that your class is declared with the Q_OBJECT macro, and immediately
* below it, also include the LURCH_PACKAGE macro, like this.
* \code
* class MyPackage : public LurchPackage
* {
* Q_OBJECT
* LURCH_PACKAGE
* ...
* \endcode
* This step ensures that static variables to hold metatype information are added
* to your class.
* </li>
* <li>Also in your header file:<br>
* After your class's declaration, include a line like the following, as per
* <a href='http://doc.trolltech.com/4.4/qmetatype.html#Q_DECLARE_METATYPE'>the
* Qt documentation for this macro</a>.
* \code
* Q_DECLARE_METATYPE( MyPackage )
* \endcode
* This step ensures that your subclass is registered with the QMetaType system,
* so that it can be constructed and deconstructed from just its name or id
* at runtime. (This enables LurchPackage to function as a factory for objects of
* your class.)</li>
* <li>In your source file:<br>
* Include in your class's constructor an invocation of the
* REGISTER_LURCH_PACKAGE() macro, called with two arguments,
* the name of your class, and the URN it returns from getURN().
* Here is an example.
* \code
* MyPackage::MyPackage ()
* {
* REGISTER_LURCH_PACKAGE(
* MyPackage,
* Lob::makeLurchURN( "My Package Name", "My Name", "en", "v1.0" ) );
* }
* \endcode
* This step ensures that the metatype data in your class is correctly
* set up, and that the static variables in the LurchPackage class know about your
* class, and how to ask the QMetaType system to construct instances of it.
* </li>
* </ol>
*/
class LurchPackage : public QObject
{
Q_OBJECT
public:
/** \brief Constructor registers the package in the list of all instances
*
* Because this parent class constructor handles registering the package with the
* global list stored in the static variable allInstances (and accessed with methods
* like lookupPackage()), it is essential for descendants to remember to call the parent
* class constructor.
*/
LurchPackage ();
/** \brief Copy constructor necessary for QMetaType
*/
LurchPackage ( const LurchPackage& other );
/** \brief Destructor unregisters the package from the list of all instances
*
* \see LurchPackage()
*/
virtual ~LurchPackage ();
/** \brief The representation of this package as a Lob
*
* This has three purposes; the third is the most significant.
* <ol>
* <li>Every dependency of a document, whether it be another document stored on disk
* as a .lurch file or a package built into Lurch, should be inspectable by scripts
* at runtime. Just as scripts can call <code>numDependencies()</code> and
* <code>dependency(i)</code> to fetch actual document dependencies, as Lob
* objects that pass the Lob::isDocument() test, they should also be able to do
* this for packages. Thus every package needs to represent itself as a Lob
* that is a document. It can simply be an empty document with the appropriate
* URN for the package, or just a document containing comments or documentation,
* but it must be there. The default implementation provides an empty document
* whose URN matches that given by getURN().</li>
* <li>Developers creating packages usually do not wish to code every part of the package
* in C++; sometimes it's more convenient to write some of the package in script.
* The most common way to do this is to have a separate <tt>.js</tt> file containing
* the script as a Qt resource, which you load with the source() function and pass
* to evaluateInNamespace() during the setup() routine. However,
* an alternative is to put one or more auto-run script Lobs inside the
* document returned from this function. Because a LurchEnvironment treats the
* document returned from this function just like any other dependency document,
* running any of the auto-run scripts in it, that is a perfectly valid way to
* do some or all of the setup this package requires. This is why
* \ref makepackageShellScript "the makepackage.sh script described above"
* is viable.</li>
* <li>Developers using packages will expect to be able to open the package and learn some
* basic facts about what it does, ideally with a help link to full documentation with
* examples. Thus the document returned from this function can have content that
* introduces the package and describes briefly what it does, and can have an attribute
* that points to a help document, which will show up in the Help menu in interfaces
* like Lurch. See how subclasses of LurchPackage use the static functions of
* this class to create simple representations of themselves.
* </ol>
*
* Before a LurchEnvironment processes a LurchPackage as a document Lob,
* running the auto-run scripts in it, it first calls its setup() function appropriately,
* which is what makes packages more than just document Lobs.
*
* One convenient implementation of this method is in the way that
* \ref makepackageShellScript "the makepackage.sh
* script described above" does, by simply returning a Lob created from the full XML
* code for a document. Another convenient implementation is to create a new document
* using documentSkeleton(), and then appending blocks using addText(), addScript(),
* describeLoadedScriptCode(), and describeSetupSlotsAsFunctions().
*
* If the package is only going to be loaded as a dependency, creating all that visual detail
* is just a waste of time. In fact, calls to addScript() are particularly costly (sometimes
* even taking a full second for large scripts). For this reason, the asDocument() method
* takes the \a forDisplay parameter. When it is true, the results of the call may be
* displayed to the user on screen, and thus all visual detail should be included; otherwise,
* it can be omitted. Descendants reimplementing asDocument() are encouraged to make use of
* this parameter for efficiency reasons. It defaults to false because that is the most
* common use case.
*
* \see LurchEnvironment, setup(), getURN()
*/
virtual Lob asDocument ( bool forDisplay = false ) const;
/** \brief The URN for this package
*
* This is a pure virtual function because it must be unique for each package.
* If asDocument() is implemented to create an entire document Lob with an URN,
* then one simple implementation of this function is as follows.
* \code
* return asDocument().getURN();
* \endcode
* However, note that the default implementation of asDocument() relies upon getURN()!
* So you should only use the code above if you have reimplemented asDocument(), to
* prevent infinite recursion.
*
* The routine Lob::makeLurchURN() will probably come in handy in implementing this
* routine in any way other than the one just given.
*/
virtual QString getURN () const = 0;
/** \brief Sets up a QScriptEnvironment with the routines and data this package provides
*
* This routine will be called by LurchEnvironment immediately before loading all
* auto-run scripts in the result of asDocument(). This is the difference between a
* .lurch file dependency and a package dependency; packages get to run this routine,
* and use it to add C++-implemented functions to the script environment.
*
* Note that you can add functions or data or anything you like.
* Note further that the setupSlotsAsFunctions() routine in this object is very useful
* for making it easy to implement this function with minimal trouble.
*
* The default implementation of this function calls setupSlotsAsFunctions(), and
* that's it.
*
* \param scope The QScriptValue into which any new data and/or functions should be
* placed. Think of it as a namespace. Thus you should avoid making calls
* like the following, which add data to the global object.
* \code
* scope.engine().globalObject().setProperty( "name", value );
* \endcode
* Rather, try to use code like the following (unless of course you actually
* want to add data/functions to the global namespace).
* \code
* scope.setProperty( "name", value );
* \endcode
*/
virtual void setup ( QScriptValue scope );
/** \brief Stores the pointer \a env in a member variable for later use
*
* This function does three things.
* <ol>
* <li>If the member variable \a environment has a nonzero value, then all signals from
* that object to this object are disconnected. This is convenient in case some
* descendant class of this one listens to signals in the environment; they will
* automatically be disconnected when a new environment is initialized, if that
* class's implementation of initialize() remembers to call this version.</li>
* <li>Stores the parameter \a env in a protected member variable, one thus
* accessible to all subclasses.</li>
* <li>Because the protected member variable is a QPointer, this means that if the
* object to which \a env points is ever destroyed, that pointer will automatically
* be set to NULL rather than become a dangling pointer.</li>
* </ol>
*/
void setEnvironment ( LurchEnvironment* env );
/** \brief Allows a LurchPackage to set up internal data computed from the environment
*
* A LurchPackage may have internal data stored about the state of the environment
* (document and dependencies) to help speed up the functions it provides in script.
* This function will be called by the LurchEnvironment whenever a new document is loaded
* into the environment, and the document depends on this package. You can assume that
* the call is taking place immediately after a successful load of the document, and
* you should also assume that your class's data structures may still be filled with old
* data from a previous call to initialize() on a previous document, and thus they need
* to be cleared out before being filled with new data.
*
* It is guaranteed that both setEnvironment() and setup() have been called by the
* LurchEnvironment on this object before this call to initialize(). Thus it is
* guaranteed that the member variable \a environment is non-NULL and points to the
* correct, existing LurchEnvironment object, and futhermore that any scripts run by
* the setup() function are already complete. See the documentation for setEnvironment()
* and setup() for more details on the useful consequences of these guarantees.
*
* In this function, if you want to establish an ongoing monitoring of the document's
* changes, to keep your internal data structures constantly up-to-date with its state,
* you should declare a slot such as this one
* \code
* public slots:
* void documentWasModified ( const LobChange& change );
* \endcode
* and maintain a single connection to the environment's documentModified()
* signal in your initialize routine, like this.
* \code
* void initialize ( LurchEnvironment* env )
* {
* connect( environment, SIGNAL(documentModified(const LobChange&)),
* this, SLOT(documentWasModified(const LobChange&)) );
* }
* \endcode
* This is <b><i>not</i></b> the same thing as connecting to the document's modified()
* signal, because LurchEnvironment::documentModified() filters out impermissible
* changes before they even happen.
*
* \see LurchEnvironment::documentModified()
*/
virtual void initialize ();
#if 0
// this used to make sense, but isn't needed anymore
/** \brief Fetch the LurchPackage instance that corresponds to the given URN
*
* If there exists in memory a LurchPackage instance whose URN matches the given one,
* then it is returned. Otherwise, NULL is returned.
*/
static LurchPackage* lookupPackage ( QString urn );
#endif
/** \brief Compute a list of all URNs corresponding to all LurchPackage classes registered
*
* This list is complete if and only if users faithfully call the REGISTER_LURCH_PACKAGE
* macro \ref howToWriteALurchPackage "as described above".
*/
static QStringList allURNs ();
/** \brief The QMetaType ID of an individual subclass
*
* Because this function is only for subclasses, it is pure virtual.
* Subclasses do not need to explicitly implement it; if they follow
* \ref howToWriteALurchPackage "the procedure described above" when writing their
* subclass, the REGISTER_LURCH_PACKAGE macro will implement it appropriately.
*/
virtual int metaTypeID () const = 0;
/** \brief The URN of an individual subclass
*
* Because this function is only for subclasses, it is pure virtual.
* Subclasses do not need to explicitly implement it; if they follow
* \ref howToWriteALurchPackage "the procedure described above" when writing their
* subclass, the REGISTER_LURCH_PACKAGE macro will implement it appropriately.
*/
virtual QString classURN () const = 0;
/** \brief Construct an instance of the subclass whose URN is given
*
* URNs are unique to a subclass, and are bound to that subclass with a call to the
* REGISTER_LURCH_PACKAGE macro \ref howToWriteALurchPackage "as described above".
* This function uses the QMetaType system to construct an instance of the subclass
* uniquely identified by the given URN. If the URN matches no subclass, NULL is
* returned.
*/
static LurchPackage* create ( QString urn );
/** \brief Register a LurchPackage subclass's QMetaType ID, indexed by its URN
*
* An internal static map associates subclass URNs with QMetaType IDs.
* This function adds a class's URN and ID to that map. Subclass authors should not
* need to call this function directly, but just use the REGISTER_LURCH_PACKAGE
* macro \ref howToWriteALurchPackage "as described above".
*/
static int registerSubclass ( QString urn, int id );
protected:
/** \brief Search for slots that should be added to the script environment as functions
*
* This function looks through all slots in this object, and for each slot whose name
* begins with <code>script_</code>, it adds that slot to the script environment
* \a scope, after having stripped the <code>script_</code> prefix from its name.
* For instance, rather than creating a function outside your class with a signature
* like the following,
* \code
* QScriptValue doFoo ( QScriptContext* context, QScriptEngine* engine )
* \endcode
* and then having to construct an appropriate script function object in setup(),
* as well as somehow track this particular LurchPackage object in your function's
* data field, you could instead simply write a slot with this signature,
* \code
* QScriptValue script_doFoo ( QScriptContext* context, QScriptEngine* engine )
* \endcode
* and then call setupSlotsAsFunctions() in your setup() routine. All those details
* will be handled for you, and because the slot is in this object, you have access
* to all its members when implementing the slot.
*/
void setupSlotsAsFunctions ( QScriptValue scope );
/** \brief Read the full contents of the named file as text, and return it
*
* Useful for reading in a file full of script code, so that it can be passed to
* a script engine for interpreting. If any error occurs, an empty string is returned.
*/
static QString source ( QString filename );
/** \brief Creates a simple representation of this package as a document
*
* That representation includes title, author, language, and version, as well as flagging
* the document with an attribute that indicates that it is the document representation of
* a LurchPackage.
*
* For information about how to use this function, see asDocument() and how subclasses
* reimplement that function using this one.
*/
Lob documentSkeleton ( QStringList dependencies = QStringList() ) const;
/** \brief An HTML paragraph describing what setupSlotsAsFunctions() does for this package
*
* Many packages install JavaScript functions by implementing them in C++ as package slots,
* and using a setupSlotsAsFunctions() call. So this function is a convenient way to
* document that fact in a consistent way across packages. It appends to the given document
* a list of such script slots installed this package. It does so with one call to addText(),
* after having generated the HTML to pass to that call.
* If there are no slots in this package that setupSlotsAsFunctions() would install as
* script functions, this method does nothing.
*/
void describeSetupSlotsAsFunctions ( Lob document ) const;
/** \brief An HTML paragraph and script block stating that this package uses loaded script code
*
* Many packages load script code from a Qt resource, so this function is a convenient way to
* document that fact in a consistent way across packages. It appends to the given document
* a statement that the code in the given \a filename is executed by this package. It
* includes the code in the document for the user to read, but does not mark it with any
* script type, so that it will not impact any LurchEnvironment into which this package is
* loaded. It does so with calls to addText() and addScript().
*/
static void describeLoadedScriptCode ( Lob document, QString filename );
/** \brief Adds to a package representation document a link to its help file
*
* For information about how to use this function, see asDocument() and how subclasses
* reimplement that function using this one.
*/
static void addHelpAttribute ( Lob document, QString helpURN );
/** \brief Adds to a package representation document a segment of text
*
* No HTML tags are placed in the text, so for example the caller may wish to surround
* \a text in paragraph tags, if that is the intent.
*
* For information about how to use this function, see asDocument() and how subclasses
* reimplement that function using this one.
*
* It can be convenient to use this function with describeSetupSlotsAsFunctions().
*/
static void addText ( Lob document, QString text );
/** \brief Adds to a package representation document a block of JavaScript code
*
* This is simply a call to addText(), passing along a syntax-highlighted version of the
* given code. It does not include it as a script Lob, but rather just as an HTML
* representation of the code.
*
* For more information about how to use this function, see asDocument() and how subclasses
* reimplement that function using this one.
*/
static void addScript ( Lob document, QString code );
/** \brief Caches a string of script code under a given name, for later retrieval
*
* Later you can retrieve it with program().
* This works well when used in tandem with source().
*
* This is global data storage, and thus names must be unique within a subclass of
* LurchPackage. That is, each object does not get its own cache, but rather each
* subclass does. So only store data that is determined by the class, and not a function
* of the individual object of that class. This improves efficiency.
*/
void cacheProgram ( QString name, QString code );
/** \brief Retrieves a cached string of script code, given the name under which it was stored
*
* See cacheProgram().
*/
const QScriptProgram& program ( QString name ) const;
/** The environment into which this package was last initialized
*/
QPointer<LurchEnvironment> environment;
signals:
/** \brief Emitted when this package wishes to communicate a message to the UI, if any
*
* The environment in which this package was loaded will pass this signal on. If there is
* a user interface (e.g., not in simple_script) then it may listen to and react to this
* signal.
*/
void packageMessageToInterface ( QString message );
private:
#ifdef LURCH_UNIT_TEST
friend class LURCH_UNIT_TEST;
#endif
/** Where the LurchPackage() constructor stores the <code>this</code> pointer, so that all
* created instances of the class can be tracked using methods like lookupPackage().
*/
static QList<LurchPackage*> allInstances;
/** Find at which index into allInstances this object lies (inverse of lookupPackage())
*/
int index ();
/** Look up a package object given its index (inverse of index())
*/
static LurchPackage* lookupPackage ( int index );
/** A function outside of the LurchPackage class which is used by
* LurchPackage::setupSlotsAsFunctions() to dispatch function calls to slots within a
* particular LurchPackage object.
*
* Declaring it <code>friend</code> allows it to call lookupPackage() in this class.
*/
friend QScriptValue dispatcher ( QScriptContext* context, QScriptEngine* engine );
/** Provides access to a static member that is guaranteed to be initialized before being
* needed. If this were simply declared as a static member variable, it might not be
* initialized before calls to registerSubclass() during static initialization time
* (at program startup). This causes bus errors. So instead we create this function
* that returns the object in question, and which guarantees its existence.
*/
static QMap<QString,int>& urn2id ();
/** Provides a way to store code loaded from (real or virtual) files.
*
* \see cacheProgram(), program()
*/
static QCache<QString,QScriptProgram> codeCache;
};
#define LURCH_PACKAGE \
private: \
static int typeId; \
static QString urn; \
public: \
int metaTypeID () const; \
QString classURN () const;
#define REGISTER_LURCH_PACKAGE(_class,_urn) \
QString _class::urn = _urn; \
int _class::typeId = LurchPackage::registerSubclass(_class::urn,qRegisterMetaType<_class>()); \
QString _class::classURN () const { return urn; } \
int _class::metaTypeID () const { return typeId; }
#endif // LURCHPACKAGE