/*serialization4.m. Begin from serialization3.m. We want Agents to know of their environment. Data has new Ivars that refer to external structures: id theGrid; id controllerList; Controller * controller; //explicit type avoids some compiler warnings We don't want those archived. When we later reload the serialized data, we will explicitly set the values of those variables with set methods. When the Controller is recreated and the list is read from collection.scm, then the Controller has to have the list set for it. Then, when Data objects are created, they can get most IVARS from the file, but not all. Data objects are told the Controller's identity with [setController:] and then after that they can ask the controller for references to theGrid and controllerList. I've done that in a method "lispInReset" and that method presupposes the controller is known to the Data object so the Data object can ask the controller for the grid ([controller getGrid]) and its list ([controller getList]). In Data's lispOutDeep: method, I stop Swarm from trying to archive objects and references to thes things I call external structures by changing: [super lispOutVars: stream deep: YES]; to [super lispOutVars: stream deep: NO]; By setting deep:NO, I still get automatic saves of primitives, doubles, ints, and statically allocated arrays of doubles, ints, and such. But it ignores all pointers, as in: int * x; and when it finds a pointer to an Object like id myList; the archiver saves myList as a "nil". After you run this, in collection.scm you will see myList is set twice, once as nil and once with values, because of the way lispOutDeep: is written for Data. And then I add individual commands, one for each object I want archived. run ./serialize4 -m create and look over collection.scm. then run ./serialize4 Add the cpp flag DYN_MEM to use dynamically allocated arrays inside the Data class. Note I've moved the interfaces of Data and Controller to the top of file so the declarations are available when needed. By Paul Johnson Adapted from a post by Marcus Daniels Mar 29, 2000 swarm-support email and including "lispOutDeep" method by Marcus, Feb. 6, 2002 */ #import // initSwarm #import // Archivers #import // CreateDrop #import // getZone #import // Swarm Grid2d #define CATEGORY_COUNT 12 #define SCALE_COUNT 7 @interface Integer: CreateDrop { int value; } - setInt: (int)theValue; - (int)getInt; @end @interface Controller: CreateDrop { id aGrid; id collection; } - buildObjects; - setList: aList; - getList; - getGrid; - sayHi; @end @interface Data: CreateDrop { const char *date; double someDouble; int someInt; id myList; //IVARS that refer to external structures, but are not copies id controllerList; id theGrid; id controller; #ifndef DYN_MEM int category[CATEGORY_COUNT]; double scales[SCALE_COUNT]; #else int * category; double * scales; #endif } - createEnd; - buildObjects; - setDate: (const char *)date; - (void)describe: stream; - (double)getValue; - setVal: (double)value; - (void)setGrid: it; - setController: it; - setControllerList: it; - describeControllerList; - (void)lispOutDeep: stream; - lispInReset; @end @implementation Integer - setInt: (int)theValue { value = theValue; printf("INTEGER CREATED: %d\n", theValue); return self; } - (int)getInt { return value; } @end #define INTEGER(value) \ [[[Integer createBegin: scratchZone] setInt: value] createEnd] @implementation Data - createEnd { return [super createEnd]; } //can't dump these into createEnd, because createEnd gets run //automatically when lisp loads these objects again. - buildObjects { int i, randNumber1; randNumber1 = [uniformIntRand getIntegerWithMin: 0L withMax: 5L]; #ifdef DYN_MEM printf("DYN_MEM is set\n"); scales = [globalZone allocBlock: (SCALE_COUNT* sizeof(double)) ]; category = [globalZone allocBlock: (CATEGORY_COUNT* sizeof(int))]; #endif myList = [List create: [self getZone]]; for ( i = 0; i < randNumber1; i++) { int randNumber2 = [uniformIntRand getIntegerWithMin: 10L withMax: 20L]; [myList addLast: INTEGER(randNumber2)]; } someInt = 55; return self; } - setDate: (const char *)theDate { date = STRDUP (theDate); return self; } - setVal: (double)theValue { int i; someDouble = theValue; for (i = 0; i < CATEGORY_COUNT; i++) { category[i] = i; } for (i = 0; i < SCALE_COUNT; i++) { scales[i] = [uniformDblRand getDoubleWithMin:0 withMax:1L]; } return self; } - (void)describe: stream { int i; id anObject, index; [stream catC: "Hello, I'm a Data object. Here's my story \n"]; [stream catC: "Date: "]; [stream catC: date]; [stream catC: " someDouble: "]; [stream catDouble: someDouble]; [stream catC: "\nHere is myList. It is a bunch of objects storing integers. I have "]; [stream catInt: [myList getCount]]; [stream catC: " items in myList. Their values are:" ]; index = [myList begin: getZone(self)]; while (( anObject = [index next]) ) { [stream catC: " "]; [stream catInt: [anObject getInt]] ; } [stream catC: " \n"]; [stream catC: " The integer array called category: "]; for (i = 0; i < CATEGORY_COUNT; i++) { [stream catInt: category[i]]; [stream catC: " "]; } [stream catC: "\n And the double valued array called scale: "]; for (i = 0; i < SCALE_COUNT; i++) { [stream catDouble: scales[i]]; [stream catC: " "]; } [stream catC: "\n"]; if (controllerList) { [stream catC: "I also know the controller list. It has "]; [stream catInt: [controllerList getCount]]; [stream catC: " elements \n"]; } } - (double)getValue { return someDouble; } //Tell Data agents about the grid they are in - (void)setGrid: it { theGrid = it; } - setController: it { controller = it; return self; } //Agents might need this in order to sample from their environment - setControllerList: it { controllerList = it; return self; } - describeControllerList { printf("\nHi, In the \"describeControllerList method\", I'm a Data Object named %s, and I am aware of this collectorList. \n", date); printf ("The collectorList's xfprint() says is:\n"); xfprint (controllerList); printf ("End of xfprint(controllerList) in describeControllerList"); return self; } - (void)lispOutDeep: stream { [stream catStartMakeInstance: "Data"]; //If Data has values set for "theGrid" or "controllerList" //I get a seg fault. If I save the IVARs one-by-one, it is OK. [super lispOutVars: stream deep: NO]; [stream catSeparator]; [stream catKeyword: "myList"]; [stream catSeparator]; [myList lispOutDeep: stream]; //??Is previous 4-line thing same as: //[stream catStartMakeInstance: "myList"]; //[myList lispOutDeep: stream]; //[stream catEndMakeInstance]; // If i wanted to specifically save a string, a double, and Int, I'd // do this instead of lispOutVars:. lispOutVars: with deep:NO causes a nil to be // written for objects, although it appears harmless // [stream catSeparator]; // [stream catKeyword: "date"]; // [stream catSeparator]; // [stream catString: date]; // [stream catSeparator]; // [stream catKeyword: "someDouble"]; // [stream catSeparator]; // [stream catDouble: someDouble]; // [stream catSeparator]; // [stream catKeyword: "someInt"]; // [stream catSeparator]; // [stream catInt: someInt]; #ifdef DYN_MEM { int categoryCount = CATEGORY_COUNT; int scaleCount = SCALE_COUNT; [stream catSeparator]; [stream catKeyword: "category"]; [stream catSeparator]; lisp_process_array (1, &categoryCount, fcall_type_sint, category, NULL, stream, NO); [stream catSeparator]; [stream catKeyword: "scales"]; [stream catSeparator]; lisp_process_array (1, &scaleCount, fcall_type_double, scales, NULL, stream, NO); } #endif [stream catEndMakeInstance]; } //After a Data object is "reborn" by lisp, there are //some variables that refer to external structures. //Data now asks the controller for those structures. - lispInReset { controllerList = [controller getList]; theGrid = [controller getGrid]; return self; } @end #define NAME "myCollection" @implementation Controller #define DATA(date,value) \ [[[[[Data createBegin: getZone (self)] createEnd] buildObjects] setDate: date] setVal: value] - buildObjects { aGrid = [Grid2d create: getZone(self) setSizeX: 100 Y: 100]; collection = [List create: getZone (self)]; [collection addLast: DATA ("1999-01-01", 1.0)]; [collection addLast: DATA ("1999-01-02", 2.0)]; [collection addLast: DATA ("1999-12-31", 3.0)]; [aGrid putObject: [collection atOffset: 0] atX: 10 Y: 20]; [aGrid putObject: [collection atOffset: 1] atX: 20 Y: 30]; [aGrid putObject: [collection atOffset: 2] atX: 50 Y: 40]; //Tell the Data objects where they are: [collection forEach: M(setGrid:):aGrid]; [collection forEach: M(setControllerList:):collection]; return collection; } - lispInReset { aGrid = [Grid2d create: getZone(self) setSizeX: 100 Y: 100]; return self; } - setList: aList { collection = aList; return self; } - getList { return collection; } - getGrid; { return aGrid; } - sayHi { printf("I'm a Controller. How are you? \n\n"); return self; } @end int main (int argc, const char **argv) { id archiver; id controller; initSwarmBatch (argc, argv); #ifdef USE_HDF5 archiver = [HDF5Archiver create: globalZone setPath: "collection.hdf"]; #else archiver = [[[LispArchiver createBegin: globalZone] setPath: "collection.scm"] createEnd]; #endif controller = [Controller create: globalZone]; if (strcmp ([arguments getAppModeString], "create") == 0) { id list = [controller buildObjects]; [archiver putDeep: NAME object: list]; #ifndef USE_HDF5 [archiver sync]; //required only for Lisp anymore? #endif } else { id collection; collection = [archiver getObject: NAME]; //After lisp in magic creates the list and the objects in it, we need //to set some variables in both collector and for each item in the collection. //Instead of calling buildObjects for Collector, the Controller //needs only reset some things: [controller setList: collection]; [controller lispInReset]; [collection forEach: M(setController:):controller]; [collection forEach: M(lispInReset)]; xfprint (collection); } [archiver drop]; //must drop to make it write in hdf5 mode. Replaces effect of sync? return 0; } /* Local Variables: compile-command: "$SWARMHOME/bin/libtool-swarm --mode=link gcc3 -D_GNU_SOURCE -o serialize4 -Wall -g -Wno-import -I$SWARMHOME/include -I$SWARMHOME/include/swarm -L$SWARMHOME/lib -L$SWARMHOME/lib/swarm serialization4.m -lswarm -lobjc " End: */