Callbacks and pointers :)


#1

hello :smiley:

i’m just having a think about the method i’ve used for handling data in my program and think that it’s a bit sloppy, and would just like to check for opinions.

my main component (sitting in a dialog window) holds an array of objects. this array is stored there as it is used thruout the program, but there are two ‘page’ components which operate on the data in different ways.

at the moment, each of these subcomponents are created with a pointer to the array, which they use to access the data. however, i’m sure that this is a stupid and naive way of doing things and i’d like to get into the habit of doing things sensibly.

what is recommended? do i put all the code that manipulates the data within the main component, and have some kind of callback system? i’m not entirely sure how i’d implement that kind of thing.

if, for example, a page component wishes to set a value of an object in the array, what would be the best way? at the moment, it simply calls a class member function to perform some operation on the array from the pointer. this just feels like bad practise. should the function that manipulates the data exist within the main component?

actually, obviously it’s best that the data live in its own class to handle all manipulation. (that’s very stupid of me to not do that here)… but how does the page component best tell the data to operate?

i’d greatly appreciate any help on this topic. cheers


#2

oooh, i think i have an idea-

do i inherit ActionBroadcaster in my page component, and have it send an action message with some defined name; then the main component would have an action for this message which would tell the data to operate?

is this right? :?:


#3

In oo parlance, each ‘page’ “is a” thing, but has slightly different ‘behaviors’. This implies inheritance. You can make ‘page’ a base class, but declare the ‘data operation’ function virtual, then derive two classes from ‘page’, and implement the ‘data operation’ differently in each. This assumes of course that the interface (calling convention) for the different data operations is the same.

[edit]
Thought I’d also mention that calling functions with parameters is hundreds of times faster than passing messages between JUCE components.


#4

if these page type classes were similarly derived then, how would they actually access the data which is stored in the parent class?

forgetting details, if the main structure looked like this:

class Component maincomponent
{
DataStore data
PageType page1
PageType page2
};

how would page1 and page2 actually access the data? at the moment they recieve a pointer to the DataStore which they use to operate, but something tells me that that’s sloppy, and there should be a more ‘solid’ method of them accessing the data, perhaps getting the parent to ‘do’ stuff with it.

for example, if there was a ‘incrementAllDataValues()’ function they could call - who would actually ‘do’ that? would they indicate to the maincomponent that they want to do it, and let the parent handle whether or not it should call the function? or should they just do what they do now and call it themselves?

and if they want to retrieve a data value? something tells me that it’s better to have a temporary storage in the page; it notifies the maincomponent that it needs a value, the value is retrieved by the maincomponent and stored in the page’s ‘currentValue’ space, and then the page is updated? or is it an acceptable method to simply use a pointer within the page to just get it itself?

i feel that it may be ‘better’ to keep each class contained, so that the actual ‘dataStore’ can be independant. does that make sense? or am i needlessly complicating things? i’m just wary of developing bad practises when there may be a really tight acceptable way of doing such things.


#5

Hmmm. Not sure if I’m getting you completely. But…

I’d make a singleton class to hold the array AND carry out manipulation itself.

e.g. ( with much omitted )


typedef OwnedArray<ObjectType> ObjectTypeArray;

class DataStore
{
public:
       DataStore();
       ~DataStore();

      bool doSomethingInterestingWith( int arrayIndex )
      {
              array[ arrayIndex ]->interestingProcedure();
      }

      juce_DeclareSingleton (DataStore, false)


private:
      ObjectTypeArray array;
}

Then your calling classes just do…

This assumes you only have one datastore of course!

see juce_Singleton.h

klf


#6

yes, having a class which contains the data and operations is what i have done (not sure why i didn’t do that in the first place) but actually getting the other components to be able to REFER to an instance of that class stored in a parent component is what i was troubling myself with.

i.e. the ‘pages’ are stored in the same component as the data; should the pages have direct access to the classes stored in their parent? or just tell the parent that they want to do something with it and the parent tells the data to do it?

i’ll look at this singleton thing tho - i’ve no idea what that is! :o


#7

am i being stupid? am i worrying about nothing?


#8

possibly. I’m no expert but I see nothing wrong with objects having member variable pointers to objects in the parent.

calling the parent to do something with the data would mean passing a “this” pointer to the child object. So thats passing a pointer anyway.
or
using juce getParentComponent() which is essentially the same thing but you’d have to somehow cast the result to get at any specialised methods ( extensions to base Component ) (which caused me all sorts of circular header file nightmares - I gave up on that and went with resending mouseevents to the parent instead)

so yeah. just give em a pointer unless you like the singleton thang.

klf

PS stoned


#9

i would say go with the action listener… that’s what i usually do (not sure if it is the best approach though)


#10

yeah, actionListeners. wasn’t thinking of them as my subcomponents don’t have buttons, just graphics so I was mouseListening.


#11

:hihi: that’s the spirit!

i was hoping to see at least one person say something about actionlisteners!

so i inherit an actionbroadcaster, and then send a string message to the parent to instruct it to ‘do’ something with the data class?

when it comes to actually getting info from the data class tho, what would the approach be?

lets say i want to retrieve a colour from the datastore. is the following approach a sensible one?

(from within page1)

page1::initiateColourRetrieval()
{
    sendActionMessage(T("getcolour"));
}


mainParent::actionListenerCallback(const String msg)
{
   if (msg == T("getcolour"))
      col = dataStore->getColour();
      myPage1->giveColour(col);
}

crumbs it all looks rather convoluted...
i imagine if i just wanted to take a number from the store to do a calculation with then this kind of approach is stupid...

am i missing something here? have i just demonstrated a complete misunderstanding of callback technique?

#12

If you’re worrying about references, maybe ReferenceCountedObjectPtr’s are what you’re after?


#13

[quote=“haydxn”]:hihi: that’s the spirit!

i was hoping to see at least one person say something about actionlisteners!

so i inherit an actionbroadcaster, and then send a string message to the parent to instruct it to ‘do’ something with the data class?

when it comes to actually getting info from the data class tho, what would the approach be?

lets say i want to retrieve a colour from the datastore. is the following approach a sensible one?

(from within page1)

page1::initiateColourRetrieval()
{
    sendActionMessage(T("getcolour"));
}


mainParent::actionListenerCallback(const String msg)
{
   if (msg == T("getcolour"))
      col = dataStore->getColour();
      myPage1->giveColour(col);
}

crumbs it all looks rather convoluted...
i imagine if i just wanted to take a number from the store to do a calculation with then this kind of approach is stupid...

am i missing something here? have i just demonstrated a complete misunderstanding of callback technique?[/quote]

hmmm. don't like that much meself. seems like far too much going on for a simple data retrieval. if mainParent, page1 and page2 all need to speak to the dataStore then just give the fuckers all a  pointer keeping mainParent as in charge of creation/deletion.

or if you can see your dataStore as a sort of "global registry" then I fancy the singleton as you can "summon" the bastard from anywhere. I like that way but it has to fit with your design. (you wouldn't want ALL your objects as singletons ALL summonable from ANYWHERE! LOL!)


[code]
page1::initiateColourRetrieval()
{
    _colour = DataStore::getInstance()->getColour();
}
[/code]

#14

yes, indeed having callbacks and whatnot for data retrieval does create a lot of red-tape. i can imagine there being times when it’s useful, but there’s no point in keeping the main object in charge of everything - after all the page object should dictate its own instructions over the data.

thanks a lot for discussing this guys. it’s much easier to see the best options when people help shine their opinions on things.


#15