On 12/02/2017 at 13:47, xxxxxxxx wrote:
Cinema 4D Version: 13
Platform: Windows ;
Language(s) : C++ ;
I have a working tree gui. And I would like to use GetCellPosition() on it. But it crashes.
What is the correct way to use it?
GeUserArea *UA = NULL; UA = tree->GetTreeViewArea(); LONG *x = NULL; LONG *y = NULL; LONG *w = NULL; LONG *h = NULL; LONG *offset = NULL; void *selected = tree->GetSelectionItem(); tree->GetCellPosition(selected, 0, UA, *x, *y, *w, *h, *offset);//<---crashes!! tree->GetCellPosition(selected, COLUMN_OBJECT, UA, *x, *y, *w, *h, *offset);//<---crashes!!
I guess this function does not do what I thought it did.
Apparently we need to feed it values. But what values?
If I feed it these values. It returns 0 if I click anywhere in the tree. But as soon as I click on a tree item it returns 1. Even if I click in an area without an item....WUT!?
LONG x = mouseX; LONG y = mouseY; LONG w = mouseX + 10; LONG h = mouseY + 10; LONG offset = 0; void *selected = tree->GetSelectionItem(); Bool pos = tree->GetCellPosition(selected, COLUMN_OBJECT, UA, x, y, w, h, offset); GePrint(LongToString(pos));
a) What values are supposed to feed this thing?
b) What the heck is this thing useful for?
On 14/02/2017 at 02:01, xxxxxxxx wrote:
maybe just a myth:
Niklaus Wirth, the creator of Pascal language, when asked for the pronunciation of his last name, once answered: You can either call me by name, pronouncing it Wirt, or call me by value, pronouncing it Worth.
Probably my brain mangled his words a bit, but that was roughly the bottom line.
What he was joking about are the two fundamental ways of passing parameters:
- By value (e.g. void Niklaus(Int32 worth) )
- By name (or as we'd say nowadays by reference, e.g. void Niklaus(Int32 & wirt) )
The first case (passing by value) is clear, I guess.
The second one is maybe not completely clear, here a reference gets passed to the function (note, this could also be a pointer argument to get similar results). Also note those [In] or [Out] notes on the parameters of GetCellPosition().
There are mainly two different use-cases or reasons to do so:
Passing larger objects into a function, as basically only the address of the object needs to be copied onto the stack (or even faster, is transferred via a register), result: It's faster and there's less stack usage. In this case the parameter is usually marked as [In], meaning an object gets passed via reference In to the function, in order to be read in there. Often this gets enforced by an additional const (e.g.: void Niklaus(const BaseObject & wirt)), emphasizing, that the object can only be read but not modified inside of the function.
Important here: The referenced object needs to exist before passing the reference to the function.
Note: void Niklaus(const Int32 & wirt) is pretty similar to void Niklaus(const Int32* wirt).
Using a parameter to return something from a function (because the return value is needed for other things or because multiple values need to be returned). Using the above mentioned parameter passing by value void Niklaus(Int32 worth) the function is not able to modify the value of worth for the caller of the function, as worth gets copied, when the function is called.
The reference helps in this case, we usually mark such a parameter as [Out] (but can also be [In, Out]). So, you are supposed to pass a reference to an object, and the function will use this reference to access and modify (maybe only parts of) the referenced object.
Again, the referenced object needs to exist before calling the function, what would be referenced otherwise? And what would be supposed to be modified by the function? Or where would the function return the result?
C) Of course both A) and B) can be mixed so you have a [In, Out] parameter, but that should be quite obvious.
Now, I hope, it's a bit more understandable, why functions use this kind of parameter passing, how it's supposed to be used and also why your code crashed in the first place. I reduced the example to pass just one parameter:
void Niklaus(Int32& x);
Int32 *x = nullptr; Niklaus(*x); //<---crashes!!
Of course this crashes. The function expects a reference to an integer variable.
But you declared a pointer Int32*, assigned a nullptr value to it (basically saying: "this pointer points nowhere") and tried to dereference that pointer (*x), basically telling the processor: "Look at nowhere (address null) and take the Int32 from there."
That is what is usually called a "Null-Pointer dereference", forbidden on (almost) all systems and processor types. And it happens before the function is even called. The crash has nothing to do with the function, but with your way of trying to provide what the function wants to have as a parameter, a reference to an integer value. But you fail to declare the "integer object" somewhere in memory.
The object passed by reference needs to exist.
Usually in this simple case of an Int32 variable you'd write:
Int32 x = 42; // declare the integer variable MyGetCellPosition(x); // pass it as reference (one doesn't see it here, a shortcoming C++ in my opinion), due to the added & in the prototype of MyGetCellPosition()
Just for the fun of it, you could also use your way of dereferencing a pointer and the result would be the same:
Int32 x = 42; // declare the integer variable Int32* pX = &x; // declare an integer pointer and assign it the address of variable x MyGetCellPosition(*pX); // dereference the integer pointer and pass the pointed object as reference to the function
But that doesn't mean you need to pass any information into the function. The parameters are marked as [Out], so the function does not want to read information from the referenced objects, but wants to return information in them.
So this is enough:
Int32 x; // declare the integer variable _without_ initialization MyGetCellPosition(x); // pass it as reference
So, in the end, GetCellPosition() does what you expected in the first place, but you obviously got confused by the parameter declaration or function prototype.
Note: Remembering the discussion we had a few month ago (https://plugincafe.maxon.net/topic/9779/13149_create-and-insert-material), in all of the described cases you are the owner of the passed/referenced object.
On 14/02/2017 at 08:02, xxxxxxxx wrote:
On man. I'm sorry that you had to post all of that Andreas.
I know that we should not normally assign NULL to a LONG. But I didn't want to just ask "How do I do this?" without posting some code. Even though I knew it was wrong.
Here's my working code
GeUserArea *UA = tree->GetTreeViewArea(); LONG x; //Returns the X position of the tree item relative to the UA LONG y; //Returns the Y position of the tree item relative to the UA LONG w; //Returns the width of the column LONG h; //Returns the heigth of the row LONG offset; //<--- Can also be used to determine which branch level an item is on void *selected = tree->GetSelectionItem(); Bool pos = tree->GetCellPosition(selected, COLUMN_OBJECT, UA, x, y, w, h, offset); GePrint(LongToString(x) + " " + LongToString(y) \+ " " + LongToString(w) \+ " " + LongToString(h) + " offset " + LongToString(offset));
Thanks for the help,