ScopedPointer semantics, STL


#1

I’m making a container full of Drawables that I can access by name. In the Juce demo, there’s an example of something similar, but it takes a couple of data structures, like this (edited for clarity):

StringArray iconNames;
OwnedArray <Drawable> icons;
// ...

// create a named drawable
iconNames.add ("myName");
icons.add (Drawable::createFromImageDataStream (*svgFileStream));

 // fetch a named drawable
Drawable* image = icons [iconNames.indexOf ("myName")]->createCopy();

Using std::tr1::shared_ptr and STL containers instead, doing the same thing looks like

typedef std::tr1::shared_ptr<Drawable> DrawablePtr;
std::map <String, DrawablePtr> icons;
// ...

// create a named drawable
icons["myName"] = (Drawable::createFromImageDataStream (*svgFileStream));

// fetch a named drawable
Drawable* image = icons["myName"];

With the STL method there is no need to mess around with two data structures that could possibly get out of sync. The std::map code also has the advantages of working with any kind of sortable object you want (such as a hash) for a key, and of being highly optimized.

JUCE’s ScopedPointers are by design not copyable, a quirk that is documented but can lead to some crazy-making situations if you are unaware (me earlier today.) For example, this compiles OK

ScopedPointer<Drawable> p;
p = (Drawable::createFromImageData(data, dataSize));

but this doesn’t.

ScopedPointer<Drawable> p = (Drawable::createFromImageData(data, dataSize));

For the same reason, ScopedPointers can’t be contained in STL containers. Since Jules is a busy guy, and JUCE has its own set of container classes, I’m not saying this should be changed. But if like me, you rely on both JUCE and the STL, take note: there are some gotchas.


#2

Gotta be honest, I’ve tried it all ways and I prefer the Juce way (or my own home-brew).

When I am in a hurry to implement code, yeah I’ll use a std::vector or a shared_ptr just to whip something out fast and see if it works but when I write it “for real” I will use something else like a juce::Array or ReferenceCountedObject, etc…


#3

http://www.rawmaterialsoftware.com/juce.php


#4

It doesn’t on Mac, and probably Linux (never tried). On Windows it works.
It also isn’t, much a problem since you can write the following instead:

ScopedPointer<Drawable> p(Drawable::createFromImageData(data, dataSize));

Chris


#5

No, it’s not really a problem as far as getting things done. But it was confusing to me. So I made this topic here—I hope it helps some other confused person who searches on ScopedPointer or STL.


#6

You’re welcome to mix std containers with juce stuff - my intention was never to replace them or ban them, just to provide some container types that were more to my own taste, as I’ve never been a fan of the naming conventions or style of the std ones, and there are some tasks where they can be a bit of a pain. There’s not really much point comparing the std::map class though, because juce doesn’t have one of those… if I wrote one, it’d presumably produce just as good a result as your example with the std::map.

Re: this…

…yes, it’s just a fact of life in c++ that you can’t write that. If you use std::auto_ptr, you’ll find it behaves exactly the same way. Any kind of single-ownership pointer class must use a copy constructor with a non-const rhs, because it modifies the thing it is copying from. That means the compiler can’t automatically convert the statement into the correct form of:

But IMHO (and in the humble opinion of all the c++ gurus that I’ve read too) you should always write your non-primitive type declarations like this anyway. You don’t really want the compiler to call the object’s default constructor, then call its operator= method as two separate steps, do you? So why write it like that and hope that the compiler will sort it out for you?


#7

Because the standard doesn’t say it’ll act like you’re saying, it even says the opposite, it’ll act like the original poster says:

From ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages - C++ §8.5 Initializers [dcl.init] para. 12
Class a = b; // Call the constructor of Class(taking a b as parameter if any)

What you’re saying is only valid if there is no accessible copy constructor (like a private one), like most Juce classes does, but it’s not the rule, it’s merely the exception I would say.
However, don’t make a rule out of the few, as if you force newbies writing code like you say, they’ll hit the issues shown below.

Using “Class a();” like code leads to:
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.2
and
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.21


#8

No, that’s not what I said at all - I would never write a no-parameters constructor like that, and would not encourage anyone else to do so.

I’m only talking about constructors that take one parameter. You should write a 1-param constructor with the same syntax you’d use if it took 2 or more params. It’s the same number of keystrokes, you can’t argue that it’s any quicker to write it as an assignment!


#9
But IMHO (and in the humble opinion of all the c++ gurus that I've read too) you should *always* write your non-primitive type declarations like this anyway. You don't *really* want the compiler to call the object's default constructor, then call its operator= method as two separate steps, do you? So why write it like that and hope that the compiler will sort it out for you?

Point well taken. I tend to use = here because I find it more readable—probably a C coding habit I should break.

Yes I follow you about auto_ptr implementations and I hope I made it clear I don’t think this is a juce problem. Rather than using auto_ptr, I either try to use objects directly, or shared_ptr when I need to put something into a container.

Like C++ in general, STL can be a mine field for the unwary. Some parts are excessively complex, but the useful parts are very well designed and very efficient. I find the Google C++ style guide to be a great introduction to these parts.

I brought up std::map because I need it. Keeping maps of (hashed) names is a great tool for managing medium to large systems effectively. The comparison explains to someone reading why I would want to use STL and std::map in the first place. I don’t think there needs to be an equivalent in Juce.


#10

No, for sure it’s the same. It’s just that, as soon as a newB start seeing

Class a( param);

They might start using the same syntax everywhere, and then, start writing:

Class a();

And get a really really strange error.
That said, that’s how we all learn, so it might worth a note somewhere to save some time.


#11

Here is the Hash and OwnedHash classes in perfectly 100% JUCE style (took me a while!) that was never accepted in the main development trunk by Jules.

This is designed for basic types (like Array):
http://code.google.com/p/juced/source/browse/trunk/juce/src/extended/containers/jucetice_Hash.h

This is specially crafted for owning objects pointers (like OwnedArray):
http://code.google.com/p/juced/source/browse/trunk/juce/src/extended/containers/jucetice_OwnedHash.h

Hope these helps !


#12

Cool stuff, kraken, thanks for sharing.


#13

Jules, if you still monitor this thread, these classes are really nice looking, and might worth inclusion, don’t you think ?


#14

Sorry, I hadn’t followed the end of this thread - yes, they do look like really nice classes! Kraken, would you be happy to let me take them over and add them to the library?


#15

Yeah sure, that was the original purpose of those writings in (quite) perfect juce style :slight_smile:

feel free to add those classes in the framework, i’m very happy i don’t have to manage to include my own ones all the time !

PS. one thing that can be optimized is the usage of const KeyType& instead of const KeyType in all functions taking the key, allowing a faster lookup by const reference (especially when used with strings).


#16

[quote]Yeah sure, that was the original purpose of those writings in (quite) perfect juce style

feel free to add those classes in the framework, i’m very happy i don’t have to manage to include my own ones all the time ![/quote]

Cool, much appreciated, I’ll take a look through very soon! Sorry if you’ve posted them before and I failed to notice/remember… it’s hard to keep on top of all the forum activity sometimes!


#17

Ok, I’ve added a hash-map class. I started with your code, but I think by the time I’d finished tweaking it, the result contains absolutely none of the original code at all! Might be interesting for you to compare-and-contrast though…!


#18

Interesting. What i don’t know if it can be optimized is that all the accessors/manipulation functions of the map take a KeyTypeParameter by copy, and when using strings that holds the keys, there will be much copying around and a general overhead when asking for a value.

What about using a const KeyTypeParameter& instead ? (or i don’t get what’s the PARAMETER_TYPE macro for ?)


#19

The PARAMETER_TYPE macro provides the best version of the type for use as a parameter - so if your type is a MyObject, then PARAMETER_TYPE (MyObject) == “const MyObject&”, but PARAMETER_TYPE (int) == “int”.


#20

it’s in juce ? cunning trick !!!