Ramblings: Scriptable Juce 2.0?


#1

It’s May and 3 years have passed since I decided to give Juce a try, for using with my PILS programming system.

I’ll celebrate this with a summary of my experience with Juce bindings and some thoughts on where to go.

Why Juce?
Juce wasn’t the obvious choice for binding to a dynamic language since it comes with no meta information, unlike Qt, wxWidgets, OpenOffice’s UNO, to mention some of the cross platform frameworks I had struggled with.

Of these, UNO seemed neat, well-structured and fairly well documented but impossible to get working on my MS Windows box. I felt like a kid glaring at sweets in a glass jar without being able to twist open the lid, and no mom to help.

Qt seemed rich-featured and well-documented but its introspection was half-baked and I disliked the preprocessing system and the signalling mechanism. Besides, there were some issues with the licensing before Nokia bought the framework.

So I went for wxWidgets which was a big mess, with inconsistent handling of object ownership and far too much platform-specific code, meaning I had to loads of manual tinkering to create useful bindings and I never could rely on things really working cross-platform.

The feature that made me go for Juce bindings was this method pair:

JUCEApplication::moreThanOneInstanceAllowed() JUCEApplication::anotherInstanceStarted()

To spare the more sensitive ones among you, I shall not detail the pains it took to implement a similar functionality in wxWidgets. But believe me, it took a month’s work. After that, I decided I’d rather do my own bindings generator for Juce than putting up with wxWidgets - or trying to get to terms with existing bindings generators.

Why dynamic programming?
I’ll also spare you from detailing the wonders of PILS programming here but I will throw in some general remarks on the usefulness of dynamic programming.

Sure, anything dynamic programming can do, C++ can do better and faster, provided you have enough programmer time and know what you want.

But when you experiment with new ideas, interpreted languages can do wonders in short time. They allow you to change critical parts of a program while it’s running, and see the results at once.

For my part, I have exploited this trait in creating specialised code analysis tools with PILS. Changing the search/rewrite rules dynamically while I was working in the app would led to fast and usable results.

But the most important use might be experimental UI design. Being able to adjust the looks of a running application can really boost development speed.

(In my opinion, one of the reasons opensource applications like Gimp etc. are stuck with arcane user interfaces is, the C/C++ programmers that create them don’t know how to experiment with UI concepts because their tools are lame in that respect. Explorative programming with dynamic languages is needed here.)

Also, deployment can be simpler, i.e. you can change the looks and workings of an application by storing parts of it as scripts in a database.

I am sure some of you Juce Geeks & Ubergeeks have had your teeth sunken in Python, Ruby, Javascript or whatever, and some of you might miss the taste and wonder what you could do with Juce bindings for these languages.

I’ll conclude this for now and add something about the strengths and weaknesses of Juce in its present form later, with some suggestions on what might be done about it.

(To the Great Leader Kim Il … oops i meant Julian: I acknowledge your supreme wisdom and I’m not instigating a revolt to throw you off the throne but I do think we need to talk about the future.)


#2

http://www.lua.org


#3

I find that building the user interface is typically the easiest and most enjoyable part of development. The place where I usually get bogged down, is with concurrency and with solving domain-specific problems (for example, high quality audio filtering).


#4

…and, of course http://www.rasterbar.com/products/luabind.html

However, a built-in scripting engine in JUCE wouldn’t be a bad idea. And Pils language looks neat ! I’ve seen on your webpage it runs on windows. What about other platforms ? (hint : macOS X)


#5

:arrow: TheVinn: A number-crunching intensive domain like DSP may not be the optimal case for dynamic languages. Still, scripting might enable you to experiment very swiftly with assembling filters from premade components. Given a powerful rule-based scripting language (I didn’t mention PILS :wink:), you would relatively easily be able to autogenerate fast C++ code once you had the filters working by script.

Concerning UI: I’m sure the Jucer is a great help in assembling the controls you need for using your filters. But say you want your users to be able to construct their own filters by manipulating objects on the screen. How would these objects change to reflect what the user is doing to them? In such a case, my bet is on scripting the rendering code.

:arrow: dinaiz: I’ve had JucePILS running on OS X and Linux but the integration with these platforms is less than perfect. I don’t quite understand their handling of file types etc. and I’m rather unhappy with XCode. Still, this could be solved, should interest arise.


#6

Some JucePILS internals

In this post, I’ll describe in some detail how I created Juce bindings for PILS, with emphasis on what the pitfalls were. Skip it if you don’t want the internals.

After considering various tools like PyBoost and Smoke, I decided I’d do my own stuff from scratch, based on my experience with wxWidgets bindings.

The interface generator is a PILS program (121K of PILS source) which parses juce_amalgamated.h, analyses the data in various ways, and generates C++ files with declarations, meta-objects, wrappers and thunks that constitute the bulk of the PILS Juce bindings. This takes 4.2 seconds on a 2.8GHz Core2 which is fast enough for me. The resulting win32 executable is 6MB which IMO isn’t bad for the Juce framework, bindings and the PILS interpreter.

Creation, deletion and wrapping of objects
JucePILS wraps Juce objects together with class meta-objects, in various types of wrappers, depending on their class and how they were created. Deletion is fully automatical.

Here, things get slightly complicated. PILS can get Juce object pointers in at least 4 different ways, with different implications for wrapping:

[list][]by calling a constructor[/][]by calling a factory method[/][]by calling an accessor method[/][]passed as argument to a callback[/][/list]

When PILS calls a Juce constructor for a class with virtual methods, it actually creates an object of a derived class with some special logic that allows PILS to override virtual methods. Generally, the object is owned by the by the wrapper and deleted when the wrapper is freeed.

Components are dealt with specially. When a top level window is put on the desktop, the PILS wrapper’s reference count is incremented so that it will stay alive while the component is shown or owned.

This was quite difficult to get right, and the solution I found isn’t completely watertight. Although it works fine with the things I do with Juce components in PILS, there are things that won’t work and that I don’t know how to handle, such as dragging components around.

If I were to have free hands in restructuring Juce, I think I would make all components into reference counted objects, with virtual functions for refcounting.

The other ways of getting an object - factory methods, accessors and callbacks - are somewhat simpler to handle. Factory methods create wrappers with ownership, accessors and callbacks create unsafe weak wrappers which isn’t ideal but hard to fix as-is.

Calling methods

In the wxWidgets bindings I created earlier, method calls were implemented by searching the method descriptors associated with the object’s class, then its superclass etc. until a suitable method descriptor was found. The method descriptor would then call a virtual method to do all the work.

With jucePILS I decided to go for something faster and more compact. Instead of organising methods by class, I organised them by method name. Each method name has a list of method descriptors containing of a descriptor of a class to which the object must be cast, a thunk for marshalling arguments to a struct, and a thunk for calling the method. Code size is reduced by sharing the marshalling thunks for all methods having the same set of parameter types.

Parameter marshalling is generally rather straightforward but there is stuff like raw pointers etc. that can’t be marshalled sensible so these methods are omitted. Also, methods that output results through reference parameters don’t work. However, methods that pass a struct as a result work fine.

Some combinations of a ptr followed by an int are taken to mean arrays.

If I were to have free hands …, I’d get rid of the output parameters and wrap the pointers in templates or macros that tell how to marshal them, or supply alternative methods with marshalable parameters in these cases.

Objects vs. values

Many of the Juce classes are really values rather than objects, examples are Colour and AffineTransform.

In PILS, such values should ideally be uniquely represented and stored in a global hash table, as is the case with PILS constants in general. So it would be nice to have a uniform mechanism to hash them.

If I were to have free hands …, I’d divide the Juce classes in object classes vs. value classes, the latter similar to .net value types. I’d derive all object classes from a base class with a virtual method to get an introspection object, and I’d provide boxing classes for the value types. I might make all object classes reference counted, though I’m not sure this would please the C++ guys.

Enough for now. I’ll be back with some long-term considerations about Juce’s future maintainability and Julian’s workload.


#7

Which products and companies use PILS ?


#8

None, so far.


#9

Here’s the thing, Lua is used quite extensively by commercial software developers. It has a rich history with a variety of purposes, especially in interactive media and entertainment titles. Check out this list of users:

There are literally TONS of open source and liberally licensed bindings for Lua that have gotten plenty of testing and extensive use. In fact, World of Warcraft uses Lua for its addons, as does Starcraft II. FreeNode has a very active #Lua channel where the developers routinely answer questions and solicit feedback.

The language was designed from the beginning with embeddability in mind. The language is minimal but powerful, the C api is refreshingly simple, and if you ever need to hire someone who knows Lua, there are plenty of people out there.

PILS is fine for hobbyists but for a commercial endeavor it would make sense to go with an existing solution that has a lot more years and people behind it.


#10

:arrow: TheVinn:I’m not trying to push PILS over Lua or Python, a metadata system should support any of these as well as the Jucer, instead of each of them using their own metadata system.


#11

I’ve never used Lua seriously, but have been told “use it for holding data, but not as a serious programming language!”. There’s also angelscript, which is nice (and has a C-style syntax that’d be comfortable for folks like us), but it seems to be less widely used. Python would be the ideal embedded language, but it’s a pretty bulky and complex codebase. Lua’s popularity is probably down to the fact that it only requires a handful of C files to do it.

I’m currently in the middle of asking myself deep “future direction of juce” questions, as there are far more possibilities than I can possibly actually implement. One interesting idea that was suggested this week (thanks, Tom Swirly!) is to concentrate on making it easy for people to publish and use 3rd party code in their juce projects. By that I mean that juce.com (which I’ve just acquired!) could keep a database of 3rd-party code modules, which would be published in a standard format, so that the introjucer would be able to show you a list of useful modules that you may want to use in your app. So if you wanted to use e.g. TheVinn’s dsp classes, you’d just tick it in the introjucer’s list, and it’d autmatically download it and integrate it into your project. This’d be a great mechanism for delivering extra functionality like scripting languages.


#12

@Jules: great idea!


#13

I also agree, that is a wonderful idea!

Juce has grown far enough to where Jules cannot keep up with all of the useful improvements that are possible.


#14

I guess maybe I didn’t fully understand what you were saying. Why are the changes to Juce that you proposed necessary? A reference count on every Colour object and every other object? I don’t see why…I haven’t used PILS, but in Lua you merely create a “user object” that fully contains the Juce object in its storage, and point the user object’s ‘finalizer’ to the Juce object destructor. This lets you easily embed objects.

While certainly non a trivial undertaking, it seems that writing a layer to bind Juce to Lua or any other scripting language should not require any changes to Juce itself.


#15

[quote=“TheVinn”]
I guess maybe I didn’t fully understand what you were saying. Why are the changes to Juce that you proposed necessary? A reference count on every Colour object and every other object? I don’t see why…[/quote]
I’d certainly not want reference counts for Colour objects. I want them for Component objects and certain Component-like objects such as TreeViewItem

I’m sure this is fine for Colour objects etc. which you create, use and ditch within a Lua script. But UI programming is a different matter. The lifetime of windows and their controls is controlled by the user’s actions, not by your script. This means, the binding layer needs to be told when a component is destroyed. At present, jucePILS uses the ComponentListener to detect this but this isn’t really safe and places some annoying restrictons on how components can be dealt with. I suspect this is one of the reasons why an earlier attempt to create Juce bindings for Python seems to be stale: http://pyjuce.tuxfamily.org/nouveau/index.php?page=pyjuce.en

Maybe Julian wasn’t aware of the implications of the Component class design for bindings or didn’t consider them relevant when he moulded this cornerstone of the framework. This needs to be changed for binding systems to work properly.

In Juce 1.x, deletion of a component doesn’t generally delete its child components, except those that are considered owned by the parent component, such as min/max buttons etc.

Quite recently, the ResizableWindow::setContentComponent method was deprecated and replaced by the Dupont&Dupont like twins setContentOwned and setContentNonOwned. IMO a better and more consistent solution would be to store a reference to the content control in a ReferenceCountedObjectPtr in case you want to preserve it, and let controls dereference their child controls on release. But then, Component would have to be reference counted, right?

The other stuff I mentioned is more of a nice-to-have.


#16

Ah…well this is something else entirely. So we are talking about a subset of Juce objects.

You are right, this is a weakness in Juce. And Jules pointed it out, I think his to-do list includes some kind of abstraction for an “owned object”, that allows the caller to determine whether or not the owned object should be destroyed along with the parent. Currently this is implemented in constructors which take an additional boolean parameter indicating if the owned object should be deleted. For example, ResamplingAudioSource takes “bool deleteInputWhenDeleted” in the constructor. However, not all the objects in Juce are consistent. Some of them don’t provide the option to not retain ownership.

I agree that this is not ideal. In fact, I don’t see why these objects can’t be reference counted, including Component. This will definitely solve the scripting problem, and also clean up the api for composition of objects.

Jules?


#17

That’s obviously something I’ve thought about, but there are a couple of show-stoppers:

  • There are lots of situations where you have child components which need references to their parents, and that creates complicated circular dependencies, so ref-counting would either cause huge leaks, or require careful clean-up methods to release any internal pointers (and since the clean-up method is analogous to a destructor, when would you call it!?)
  • This is unashamedly a C++ framework, and C++ lets you create stack objects, and do proper object composition, and both these are valid ways to use components. If they were also ref-counted, it’d require a lot of work-around code to prevent these non-heap objects from being accidentally deleted.

A more suitable system for components would have to be something more like garbage-collection, so that components which are no longer contained in a visible window would get deleted (by a garbage collector running occasionally on the event thread). But they’d still have to opt-in to being collected, to avoid messing up non-heap objects.


#18

That’s obviously something I’ve thought about, but there are a couple of show-stoppers:

  • There are lots of situations where you have child components which need references to their parents, and that creates complicated circular dependencies[/quote]

Child components should use WeakReference for referencing their parents (or raw ptrs when it’s safe).

IMO WeakReference forms a natural pair with ReferenceCountedObjectPtr (which might be renamed Reference or StrongReference to reflect this), it should be confined to ReferenceCountedObject refs, and the internals should be in that class. Btw you should use double-checked locking http://www1.cse.wustl.edu/~schmidt/PDF/DC-Locking.pdf when creating the sharedPointer (juce_WeakReference.h). It goes like this:

if (sharedPointer == nullptr)
{
      ScopedLock lock(someStaticCriticalSection);
      if (sharedPointer == nullptr)
      {
          sharedPointer = new SharedPointer (object);
      }
}

Shame on you for creating components on the stack. It’s a bad thing that’s gonna get you into trouble sooner or later. Your Android will explode right in your face when you feed it with stack allocated dialogs.

It’s like atom bombs and electric eyelash curlers. Our mere ability to create such stuff is not a valid reason to do so.

Object composition is a different matter but there are ways to deal with it. A simple way would be to enforce the convention that the enclosing object of a ReferenceCountedObject must, in its constructor, increment the child object’s reference count. This increment could have a fancy name such as disableAutoDelete().


#19

Slow down there cowboys. First of all, putting a component on the stack is perfectly valid. For example, if you want to create an Image snapshot.

Second, there are two parts to making Component reference counted:

  1. Derive from ReferenceCountedObject

  2. Replace all Component pointers with ReferenceCountedObjectPtr

Jules are you assuming that we need to do #2? Because I wasn’t suggesting that at all. You can add ReferenceCountedObject to Component’s list of base classes with no ill effect if that’s all you do. We don’t need to start adding reference counts for children (i.e. childComponentList). We don’t need to dig into the top level window manager and make that reference counted either.

To address the scripting use-case, and as a neat side-effect solve your problem with the “bool shouldDelete” in constructors, is use a reference count only for situations where an object wants to “contain” another object created externally. For example, the ResamplingAudioSource.


#20

I make no claim at all that the class is thread-safe, so it’s up to users to implement thread-safety if necessary. For things like components, it isn’t necessary, so it’d be wasteful to add a lock every time a component needs to get a WeakReference. For classes that do have multiple threads creating references to the same objects, a more efficient solution would be to simply add a dummy call to getWeakReference() in their constructor, which would initialise its shared pointer, and make it all thread-safe without any need for locking.

…no, it won’t get as far as exploding, because it wouldn’t compile! I share your dislike of stack-based modal loops, and I’ve added a lot of code recently to provide alternatives. But it can still be the neatest solution in some situations.

As for composition, I’d strongly urge people to use it wherever possible, as it provides the ideal way of managing your object lifetimes, with zero overhead, and is certainly the best-practice way to do it in C++. So to make it dangerous to use composition, by forcing users to remember to make all their objects opt-out of ref-counting would be sending entirely the wrong message, IMHO.