ClassBuilder
Displaying 1-20 of 32 total.
12 next
Please enter a numerical value for the importance of this sticky.
Enter 0 to unsticky.
Feyr

Okay, so I'm about as done with my ClassBuilder as I'm going to get. Here's the feature list.

* Public, read-only, write-only and private fields with autogenerated accessor functions. You can specify the size, whether it's signed or unsigned, and give a comment to appear in the class documents that are generated for each field.
* Autogenerated constructor with support for auto-filling fields with the constructor parameters, with the option to embed custom code.
* Autogenerated destructor with the option to embed custom code for cleaning up internal DMA-based fields, with the destructor code automatically passed down the inheritance hierarchy.
* Single inheritance with polymorphism - subclasses can be treated as their base class.
* Polymorphism - inheritable functions will autodetect the object type and call their base class code unless the subclass has overridden the function.
* Limited compile-time type checking - attempts to use fields that don't exist in the specified class will cause an error when the code is being generated.
* Limited run-time type checking - the default set/get functions check the type of the object being used to make sure it is compatible with the class that the set/get function is defined in. If you want type checking in your own functions, you can use the [Class]TypeCompatible(int object) function to determine whether the object is compatible with [Class]. An object is defined as compatible if it is either the same as [Class] or somewhere below [Class] on the inheritance hierarchy.
* Variable-sized class memory - You can specify a number or expression to use for allocating extra memory at the end of an object; I used this to build DMA-based strings and red-black tree nodes.
* class_builder.txt, which has a quick reference of all the commands you can use in your .dma files, as well as a longer description of each that explains how they work and gives examples of input and output.
* and possibly much, much more! ...but probably not -that- much more.

Some limitations:
* An inheritance hierarchy must be fully contained in a single file. I couldn't allow the classes to be split up without also writing a linker for the generator, and I don't love you that much. =D So while you can use multiple files if the classes aren't inheriting from each other, if you use inheritance you have to stick them all in the same file.
* VC syntax errors in your class file are a -little- annoying to track down, since when Verge reports a syntax error on line 53 you have to open the generated .vc file, find line 53, and then change the corresponding line in your class file.
* I've included both the Perl script and an exe version created with perl2exe. If you don't have a Perl interpreter on your machine (and don't want to go to www.activestate.com to get a free one) then you're stuck using the script the way I wrote it. If you do have Perl installed, feel free to rewrite the output functions to fit your style of coding. The default style of autogenerated function names is [Class]Get[Field], [Class]Set[Field], [Class]New and [Class]Free.

Some examples:
* simple.dma is about as simple as you can get, and contains more comments than code. It illustrates setting up a class with two fields and a constructor that will automatically fill the fields in for you.
* codeblock.dma shows you how to add custom functions to your classes.
* simple_inherit.dma shows you how to inherit from another class, and what that means. This example illustrates field inheritance only.
* polymorphic.dma shows how to write polymorphic code.
* system.vc has examples for all of these, and writes a bunch of stuff to v3.log so you can see that it's working.
* string.dma and rbtree.dma are more proofs of concept than examples. string.dma defines a dynamic string that can be translated back and forth between VC strings and DMA strings, and defines string copying and string concatenation operations.
* rbtree.dma is (half) of a red-black tree implementation. Deletion has been left out, because it's been a pain getting it to work without unbalancing the tree, and I don't want to waste any more time on it right now. But insertion and searching both work properly. Red-black trees, for those who aren't familiar with them, are data structures that guarantee search, insert and delete times that grow logarithmically with the number of items you insert.

Installation:
Unzip a fresh copy of the V3 engine into an empty directory and then unzip ClassBuilder on top of it, overwriting any files when it prompts you. Running verge.bat will attempt to recompile the ClassBuilder.pl script (if you have perl2exe installed on your machine), all of the *.dma files will be translated into *.vc files, and then Verge.exe will be run. Check v3.log after it completes, and walk through the example code while reading the log file.

Get it:
Right here

Posted on 2004-10-09 03:45:37

blues_zodiakos

O.O

Posted on 2004-10-09 03:53:20

Zip

Nice stuff, I'm trying to digest it all... perl ain't my thing. I love asc.vc though. :D

Zip

Posted on 2004-10-09 04:31:49

Feyr

Thanks. =) Unless you're planning on changing the way the generator names functions you shouldn't need to mess with the class_builder.pl script at all. It -might- give you some insight into how things work if the examples DMA files aren't clear enough, but more probably it'll just make you cry. I cleaned it up a little but it's still a tough bundle of code to work out.

I used Perl to generate asc.vc for me. ;) Perl rocks.

Posted on 2004-10-09 04:50:00

mcgrue

Perl is a language designed by madmen for madmen.

You cannot contest this. The fact that you made this insane (yet awesome) script proves your madness! :D

Posted on 2004-10-09 05:15:15

gannon

wow just wow
will have to look though this :)

Posted on 2004-10-09 07:00:10

Sungam

This is so ridiculously cool that I don't know what to say about it. You're a god among gods. I wish to have half-god babies with you.

Of course, it's going to take me a while to even figure out what this does, but from what little I can make out so far, it's the best thing since Baby Jesus.

Posted on 2004-10-09 11:36:41

rpgking

Awesome. :D Time to check this out.

Posted on 2004-10-09 18:25:32

gannon

I know I should test this out myself but do you allow multiple constructors for a single class?

Posted on 2004-10-09 20:46:30

Feyr

Unfortunately not. It didn't occur to me until just a little while ago, when I realized I needed them for the thing I'm working on now. Here's what I did instead:


Class:Event

Field:TriggerType:byte
PrivateField:Callback:quad
Field:Trigger:quad
Field:Timestamp:quad

Init:int EventNew(string handler)
InitCode:
$Callback$ = StringNew(handler);
$TriggerType$ = TRIGGER_NONE;
$Timestamp$ = systemtime;
EndCode

// some other stuff

int EventNewOnce(string handler, int interval) {
int this = EventNew(handler);
$Trigger$ = $Timestamp$ + interval;
$TriggerType$ = TRIGGER_ONCE;
return this;
}

int EventNewRecurring(string handler, int interval) {
int this = EventNew(handler);
$Trigger$ = $Timestamp$ + interval;
$TriggerType$ = TRIGGER_RECURRING;
return this;
}

int EventNewCondition(string handler, string condition) {
int this = EventNew(handler);
$Trigger$ = StringNew(condition);
$TriggerType$ = TRIGGER_CONDITION;
return this;
}


Then I use EventNewOnce, EventNewRecurring and EventNewCondition as constructors, depending on the type of event I want to create. You don't get the nifty auto-filling of fields, but it's not hard to do it like this.

Also, note that that's not exactly how my code reads...I took out some extra stuff in the constructors (like managing the linked list of scheduled events) that would have been in the way for the purpose of illustration.

*edit* Another way of doing this occurred to me:


Class:Event
Field:TriggerType:byte
PrivateField:Callback:quad
Field:Trigger:quad
Field:Timestamp:quad

Init:int EventNew()
InitCode:
MessageBox('Error: Event is an abstract class. Use '+
'EventNewSingle(), EventNewCondition() or '+
'EventNewRecurring() instead.');
EndCode

// all the event-handling functions and stuff

Class:SingleEvent
Inherit:Event:no_duplication

Init:int EventNewSingle(string handler, int interval)
InitCode:
$Callback$ = StringNew(handler);
$TriggerType$ = TRIGGER_ONCE;
$Timestamp$ = systemtime;
$Trigger$ = $Timestamp$ + interval;
EndCode

Class:ConditionEvent
Inherit:Event:no_duplication

Init:int EventNewCondition(string handler, string condition)
InitCode:
$Callback$ = StringNew(handler);
$TriggerType$ = TRIGGER_CONDITION;
$Timestamp$ = systemtime;
$Trigger$ = StringNew(condition);
EndCode

// and similarly for the RecurringEvent class

It basically the same as the previous one, but you don't have to worry quite so much about forgetting and using the default constructor that doesn't set things up properly. Since the subclasses are all types of Event, the functions in the Event class will work for them, too, which means that once you've created them you can just forget what class they are.

Posted on 2004-10-09 20:58:36 (last edited on 2004-10-09 21:11:52)

gannon

question what is the Define marker
its used in the rbtree and its not in the txt

Posted on 2004-10-10 08:46:14

Feyr

Ah, I must've missed it when rewriting the docs.
Everything between 'Define' and 'EndDefine' is copied into your .vc file, just under the header. I use it to set #defines that the whole file needs to see. And also global variables that I don't want to have to dig through the class code to find if I forget what I named them.

Oh, and the macros $FILE$, $DATE$ and $VERSION$ are available in the Define section, if you want them. Just stick them in and they'll be expanded to the filename the .vc file was generated from, the date it was generated at and the version of ClassBuilder that was used, respectively.

Posted on 2004-10-10 10:13:57

zonker6666

Very nice stuff - played around a bit and made
a panel class with derived classes for text panel and icon panel .. worked just fine ;)

Posted on 2004-10-11 00:08:27

Feyr

Thanks, glad to hear it's getting some use. And especially glad that you got the panel/textpanel/iconpanel thing working, since that's what I'm going to be doing before too long. *snicker*

Posted on 2004-10-11 00:52:02

zonker6666

Quick question for ya feyr, is there a limit to the number of nested classes we could use ?

I did the test and i can see that we can at least have 1 level of depth just wonna make sure before i start doing something too crazy that ill have to backtrack on... :)

Posted on 2004-10-11 20:32:15

Feyr

Nope no limits built into the code. It should go as far as your RAM + virtual memory will stretch. Unless there are some strange limits on the size of hashes and arrays in the Perl language, which I really doubt.

If you want to put it to the test, though, try something like this:

Class:One

Class:Two
Inherit:One

Class:Three
Inherit:Two

Class:Four
Inherit:Three

// ...and so on, as deep as you like.

Then run it through the generator and check the output. You should get a function 'int OneTypeCompatible(int this)' that returns 1 for class type IDs of CLASS_One_TYPE_ID through CLASS_Four_TYPE_ID (or however far you went) and 'int TwoTypeCompatible(int this)' that returns 1 for IDs of CLASS_Two_TYPE_ID through CLASS_Four_TYPE_ID, and so on. I did this for a depth of 10 classes and it worked fine.

Also, if you'd like a couple more features, you can get the latest script version right here. The replacement script to overwrite an old version of the file with a new one doesn't seem to work.

This version remains compatible with 2.3 scripts, but it adds improved runtime error messages (translates a class type ID into the name of the class if it can, rather than just giving an unhelpful number) and macros to make it easier to do type checking in your functions, like this:


if($Window.some_variable.INVALID_TYPE$) {
$Window.some_variable.TYPE_CHECK_ERROR$
}


In ClassBuilder 2.4, this will cause a Log() and a MessageBox() with details about the error (including the variable name, the function name, the expected class, and if possible, the class that was actually found) if 'some_variable' isn't a pointer to an object of class 'Window' or one of its subclasses. The braces are important, since the TYPE_CHECK_ERROR macro expands into two lines.

...I think I've babbled on long enough. =P

Posted on 2004-10-11 21:26:07

Feyr

Oh, err...did I answer the question you were asking, or did you mean stuff like this:

Class:Container
// A pointer to another container that might hold
// another subcontainer, that might hold another
// subcontainer, ad infinitum.
Field:SubContainer:quad
// A pointer to another kind of class that contains
// a pointer to another kind of class that contains
// a pointer to another kind of class
Field:MyNested1:quad

Class:Nested1
// A pointer to another kind of class that contains
// a pointer to another kind of class...
Field:MyNested2:quad

Class:Nested2
// A pointer to another kind of class...
Field:MyNested3:quad

// Another kind of class.
Class:Nested3


In that case, there's still no problem. You might have to do some juggling to get access to the variables you need, though. Like:


// The following is equivalent to
// MyContainer->MyNested1->MyNested2->MyNested3 = 0

// cur = MyContainer->MyNested1
int cur = $Container.MyContainer.MyNested1$;

// cur = cur->MyNested2
cur = $Nested1.cur.MyNested2$;

// cur->MyNested3 = 0
$Nested2.cur.MyNested3$ = 0;


If you find the $Class.Instance.Field$ syntax too hard to read and write, or you want run time type checking, you can use the *Get* method that's generated (for all Fields and ReadFields, anyway):


int nest1, nest2;
nest1 = ContainerGetMyNested1(MyContainer);
nest2 = Nested1GetMyNested2(nest1);
Nested2SetMyNested3(nest2, 0);


The $Class.Instance.Field$ syntax does a direct memory access with no type checking. The Get/Set functions do two memory accesses (one to find the object's type, one to access the specified field) and a minimum of one extra function call (to check to see if the object type is compatible with the expected type...more functions may be called if it's not, but you'll have better things to worry about then, like getting rid of the popup window telling you that you screwed up. ;P)

Posted on 2004-10-11 21:38:56 (last edited on 2004-10-11 21:47:52)

zonker6666

Thanks for the answer - i meant #2 but its good to know that inheritance can also go deep. That was my main concern with structs they became kinda useless since your ability to properly break down the problem was cut short by not being able to nest them.

Posted on 2004-10-12 00:16:18

Feyr

I'm rewriting ClassBuilder from the ground up, now that I know what the architecture should look like, because I didn't do enough (read: any) planning. I was just going to add the ability to use include files (my windowing code is getting a bit out of hand) and I ran headlong into the wall I built with my lack of planning. =p Humans might even be able to understand the next version!

Anyway, the point is that I'm going to be in the planning stage for the next couple days, and then I'm going to be spending the weekend at the Renaissance Festival with my girlfriend. So if anyone out there is actually using it and has a feature request, now would be the time to let me know.

Planned for the next version:
* #includes
* HTML documentation generation, which will look a lot nicer than the current plaintext docs that are created for the classes. I'll probably write a CSS file to use with the docs, based on the Textpad syntax file for VC.
* Centralized error handler, like this:
int TypeCheckErrorHandler(

string function_name,
string variable_name,
string class_name,
int class_id,
string expected_class,
int expected_class_id
);

that will be called whenever a type check error is found. This way you can choose what sort of errors you want to get on type-checks, rather than having it automatically pop up a message box and return from the function. Returning 0 from the error handler will cause an immediate exit from the function that was called. Since I'm going to be doing much more in-depth parsing of the functions, I should be able to have it return; for a void, return 0; for an int, etc.
* The ability to split up your classes between several .vc files, rather than chunking them all into a single file. The main .vc file will then #include the sub-vc files. If you use a single main .dma file that you #include all of your classes into, this will have the benefit of making the human-readable types of each class visible to all classes, not just the ones that happen to appear in the same file, plus you can get up-to-date docs and code for all of your classes just by running the main .dma through the generator.
* Going to see if I can come up with a nice way to reference DMA structures that are contained within other DMA structures. The only way I can see to allow it would be to allow typed fields, like this:


Class:cEnhancement
Inherit:cListNode

Comment:A node that can be placed inside a LinkedList.
Comment:Stores data on a weapon enhancement.
Field:Stat:byte:Which stat to modify.
Field:Amount:sword:How much to modify.

Class:cWeapon
Field:Enhancements:cEnhancement:A list of enhancements.

Class:cEnemy
Inherit:cListNode

Field:Weapon:cWeapon

Class:cPlayer
Field:Weapon:cWeapon

Class:cBattlefield
Field:BadGuys:cEnemy:A linked list of enemies.
Field:GoodGuy:cPlayer:The good guy.

// ...
void PrintBattledata(cBattlefield bfield) {
Log('Data for the good guy's weapon enhancements:');
PrintList($bfield.GoodGuy.Weapon.Enhancements$);
}


I'm fairly sure I can manage the parsing required to handle typed fields, but it would require generating something like this as well:


#define cBattleField int
#define cEnemy int
#define cPlayer int
#define cWeapon int
#define cEnhancement int


which would mean you couldn't use any variables with the same name as a class. Which is why I've prefaced the class names with a 'c'. The good news is that this would let you easily reference structures within other structures, up to any depth. The bad news is that the generated code is going to be hellishly ugly.
Probably along the lines of:
PrintList(dma.quad[dma.quad[dma.quad[bfield + CLASS_cBattlefield_FIELD_GoodGuy] + CLASS_cGoodGuy_FIELD_Weapon] + CLASS_cWeapon_FIELD_Enhancements]);

Which makes it rough for anyone trying to read the generated code. The generator will insert comments with the original code above lines that are expanded like that, though, so it shouldn't be too rough. I could probably do a second version, like:

PrintList($bfield->GoodGuy->Weapon->Enhancements$);

that would generate something like this:

int bfield_GoodGuy_Weapon_Enhancements;
bfield_GoodGuy_Weapon_Enhancements = BattlefieldGetGoodGuy(bfield);
bfield_GoodGuy_Weapon_Enhancements = GoodGuyGetWeapon(bfield_GoodGuy_Weapon_Enhancements);
bfield_GoodGuy_Weapon_Enhancements = WeaponGetEnhancements(bfield_GoodGuy_Weapon_Enhancements);
PrintList(bfield_GoodGuy_Weapon_Enhancements);

that would do type checking for each link in the chain, allowing you to determine where things went wrong if you screwed up. Using the . syntax would be more than twice as fast as -> but if you're not in a tight loop I think the benefits of type checking would override the need for speed.

Posted on 2004-10-13 03:44:44 (last edited on 2004-10-13 05:28:24)

gannon

cool
that easily referencing structures within structures sounds very nice :)

Posted on 2004-10-13 04:03:41


Displaying 1-20 of 32 total.
12 next
 
Newest messages

Ben McGraw's lovingly crafted this website from scratch for years.
It's a lot prettier this go around because of Jon Wofford.
Verge-rpg.com is a member of the lunarnet irc network, and would like to take this opportunity to remind you that regardless how babies taste, it is wrong to eat them.