Crumbs! This is going to be a bit of a blog… I’ve got a lot of stuff planned in this area. Feel free to skip the ‘introduction’ bit, I just thought it might be prudent to explain what the heck I’m up to.
NOTE: The latest version of Tampon should hopefully always be obtainable by clicking this link: Download Tampon source.
[size=200] Introduction to Project Tampon [/size]
I’m trying to develop a framework to make it easier to build ‘full’ applications. By that, I mean more serious apps than the basic tools you make when cutting your teeth with Juce.
The jucer is provided as an example of the implementation of more advanced features and design tricks - but it’s not exactly up to the level of intuitivity set by the rest of the juce documentation; it’s an example of more advanced application features, but it doesn’t quite lend itself to being a ‘guide’ on how to do complex things.
And so, I’m trying to develop an extended toolkit for Juce - extra classes on top of the already mountainous supply it already has - specifically geared towards doing useful groundwork for you when making a ‘big’ application. If you consider the difference between ‘MyColourPicker’ [*fictionalexample] and The Jucer, you’ll surely appreciate that there is a lot more to the design/construction of a complex application than a simple tool.
The main thing I’m working towards is a more full-featured set of classes for a ‘document/view’ model. Bases that are not only easy to use, but informative in their role within an Application [If assembly is a bag of just the tiny lego studs, the OS API is one of those ‘1x1’ unit bricks, Juce is the whole set of standard blocks in all colours, and what I’m aiming for is one of those big flat pieces you can put all the bits on, and it comes with a nice illustrated booklet explaining how to make a car or a wendy house].
Part of this is the whole ‘archive’ business I’m working on, but there are lots of other things that need addressing. I should point out that this isn’t really just selfless contribution - I need all this stuff! I don’t want to be crawling through sewers trying to hack together the application I’m building - I want it to be built on a solid framework that I can depend upon, and one that I know will be expandable in the future without exposing hideous cracks from hacketty hacks. Because it’s so important to me, I imagine the various bits could be useful to others, so I’ll present them here as I go.
INTRODUCTION ENDS HERE!
So, what I’ve made this time is a set of helper classes/macros to make it very easy to have your own ‘type handlers’, for when your program uses lots of objects of various subclasses of a particular base type.
[size=150]Chapter One: Type Handlers[/size]
Now follows an explanation of the purpose of this chapter…
As an example, consider the Jucer; it has lots of userdata made up of different types of component. Each type of component has different properties, each has a different appearance, etc…
Think about how that’s all done; the user can interact with the components all in the same way (drag/size/view properties, etc…), and the user can ADD components from a single menu. But, they’re all different - adding a Button obviously creates a different object to adding a Label. Clicking on a Label shows different properties to clicking on a ComboBox. That might not seem like such a big deal, but of course the components themselves don’t contain the information on what those properties are, or how to display them. The document holds an arrangement of Components - but as far as its concerned they’re all just Components.
When it comes to saving/loading the layout, the complexity is highlighted; it’ll need to store stuff about these components - not just easy stuff like where, how big, etc… no, more obvious stuff like - er, what component it is! When loading the document back - a bunch of XML comes in, and it has to create the correct Component type (and apply appropriate properties) for each piece of data…
There are obviously a scrillion ways of doing that; the most obvious one (certainly to a novice) would be to have a cascade of if/else blocks, checking the name of the tag, creating the correct object, applying properties, etc… It’s not ‘wrong’ but it does get unwieldy - plus that sort of approach leaves you destined to having such things scattered far and wide around your program, and it becomes difficult to maintain or update.
The way the Jucer does it is to have a bunch of ‘type handlers’; a base ComponentTypeHandler describes stuff common to all components, and subclasses define specific stuff for specific components. It’s not just a bunch of handlers though - there’s a system for determining automatically which handler is suitable for the job in hand [getting the handler for a given component, or getting the right handler for a potential component by a tag name].
If this is all new to you, and sounds interesting (or like the solution to a problem you’ve faced), then take a look at the jucer code. It’s pretty complex! There’s a lot of stuff to take in if you want to replicate that sort of system for your own purposes.
What this all means to me (doubling as another example!)
I have a need for exactly this sort of thing. I’m working on a Quiz game to run in the classroom as a teaching aid. Amongst the data structures is a ‘Question’ base class, but each type of question is different (some are just text, some have pictures, etc…) - and so each type of question also requires a different kind of editor, and a different kind of ‘game view’. If I were to have a handler for each type, it could have a ‘createEditor()’ function, and a ‘createGameView()’ function - e.g. when clicking on a question in the editor, it finds the appropriate handler is found and used to provide the correct editor component.
[size=150]What it involves[/size]
What I’ve written is a base class and a set of macros. It’s used in a manner similar to the juce Singleton; you design a class, and you have to use the declare/implement macros in the appropriate places. The main difference here is that you also derive your class from TypeHandlerBase. What you’re making is a base for handlers of a specific type [so, for example, Component, or Question].
The system makes use of RTTI (run time type indexing). People snarl about using some of this stuff, about how it’s not ‘proper’ object oriented design. I can appreciate what they’re saying, but - heck, you NEED to have RTTI enabled for Juce, so it’s not going to hurt to use it in the odd place, is it? Most importantly though, if you don’t know much about it - don’t worry - the one place you use it with this helper is entirely obvious.
So, to do all this stuff, this is what you do:
Let’s say we have a base type - the old tutorial staple ‘Shape’.
- Subclass TypeHandlerBase, making sure your constructor takes values to pass on to its constructor. We’ll call it ShapeTypeHandler.
- add members to do handling stuff. For example, you’ll likely want a ‘createShape()’ pure virtual function. [remember, each handler subclass defines handling for a particular Shape subclass]
- add the macro ‘tampon_DeclareTypeHandlerBase (ShapeTypeHandler, Shape);’ to the class declaration body.
e.g.
class ShapeTypeHandler : public TypeHandlerBase
{
public:
ShapeTypeHandler (const String& typeTagName, const std::type_info& typeClass)
: TypeHandlerBase (typeTagName, typeClass)
{
}
virtual Shape* createShape () = 0;
tampon_DeclareTypeHandlerBase (ShapeTypeHandler, Shape);
};
- In the implementation file, put the macro ‘tampon_ImplementTypeHandlerBase (ShapeTypeHandler, Shape);’.
That’s the base done! Now, you need to make your subclasses for each type you need a handler for.
- Derive your handlers from your base. E.g. TriangleHandler and SquareHandler should derive from ShapeTypeHandler.
The constructors don’t require any parameters, but you can of course demand whatever you like. The important thing is that the base gets its parameters, and they are fixed per type subclass (so you don’t need to expose them here like you did with the base)…
- The first parameter is the ‘tag name’ - a String used to identify the type. This can be used in a user document - to store the type of the object so it can be recreated later.
- The second parameter is the alien bit - it uses the C++ RTTI operator ‘typeid’, but you don’t have to understand it. Literally all you need to do is put typeid (ShapeSubclass). That’s it. The name of the class your handler is going to be handling.
So… TriangleHandler’s constructor could be…
TriangleHandler::TriangleHandler ()
: ShapeTypeHandler (T("Triangle"), typeid (Triangle))
{
}
- make one of these classes for each type you need a handler for, and of course define the bodies of any virtual functions you’ve made.
Once you’ve done that… that’s it! They’re ready to use! The macros take care of everything else, giving you functions to track down the handler you need. To use them, you need to do the following:
- At the start of your program, register all the handlers via a static function in your base:
ShapeTypeHandler::registerHandler (new TriangleHandler);
ShapeTypeHandler::registerHandler (new SquareHandler);
- At the end of your program, destroy them all, via a single call to this…
ShapeTypeHandler::destroyHandlers ();
Now, whenever you need to get a handler, you can do stuff like this…
ShapeTypeHandler* hander = ShapeTypeHandler::getHandlerForTypeTagName (xml.getTagName());
// or
ShapeTypeHandler* handler = ShapeTypeHandler::getHandlerFor(selectedShape);
You’ll be presented with the appropriate handler, with access to all the functions in the base class [with all virtual function behaviour from the corresponding subclass]. Easy!
No, seriously, DEAD EASY.
The code for the macro/base stuff is in the next post (my web space is perpetually screwed).
Now, I know this post has been epic in length, but I imagine this project is going to be going on for a while, so I wanted to set the scene. I guess it’s a bit like a series of highly specialised seminars in comparison to the previous tutorial… I’m sorry that I’ve not had the time to work on that in so long, I have my own code to write! But I’m going to share it as if it were a tutorial anyway.
So, until the next chapter…
[*] You’re probably wondering about the name of the thread… I was bored of a generic acronym based name for the project. The thought process was: it’s supposed to be a toolkit to make creating applications easy… a kindof application creator… applicator… And applicator of course leads on to Tampon, and so the name has stuck. There.
[size=75](the fact that it sits so uncomfortably in the mind next to the word Juce just adds to the appeal)[/size]