Container Owners?
-
On 02/03/2014 at 12:07, xxxxxxxx wrote:
Originally posted by xxxxxxxx
This is confusing to me.
I myself have switched off the normal way of thinking when programming C4D plugins.
In such cases as this, I write a lot of test cases and experiment a lot, trying to find a sensible and understandable pattern. Hours, even days, go by, doing this. Solving tasks that I normally would use half an hour getting done, when programming using the familiar class hierarchy, methods, properties (C#) etc.C4D is a big black box, and I can just guess what happens behind the scenes. Feel like a stone age man, watching a radio for the first time..
-
On 02/03/2014 at 13:00, xxxxxxxx wrote:
It's always much easier for me to see the code rather than talking about things abstractly with words.
So here's an example.
I create a SubContainer inside of the document's default container. Then store a string value in it.
It seems to work as expected:Bool SimplePlugin::Execute(BaseDocument *doc) { BaseContainer *docBc = doc->GetDataInstance(); //Get the document's default container BaseContainer newBc; //Create a new container docBc->SetContainer(PLUGIN_ID, newBc); //Set the new container as a subcontainer inside of the document's container newBc.SetString(1, "Hello"); //Put some data in the new container //Now lets look through the document's container and find our new container //Then when we find it...Get the string value stored in the container's #1 ID slot LONG i=0; while(docBc->GetIndexId(i) != NOTOK) { LONG id = docBc->GetIndexId(i); if(id = PLUGIN_ID) { GePrint(newBc.GetString(1)); //<--This Works..We get the the word "Hello" in the console break; } i++; } EventAdd(); return TRUE; }
But when I try to get the string data from inside of this new SubContainer using a python script.
The SubContainer is there. But the string data is not there anymore:import c4d def main() : docBc = doc.GetDataInstance() newBc = docBc.GetContainer(654321) #654321 is my CD plugin's ID# print newBc.GetString(1) #<--Ouch!...No data is there!! Why? if __name__=='__main__': main()
So I'm left wondering how people are using these SubContainers to store things?
The data seems to be getting erased when the CommandData plugin closes.-ScottA
-
On 02/03/2014 at 13:56, xxxxxxxx wrote:
Hi Scott,
I made a test. I have two tag plugins.Tag plugin A
void TestTag_A::AddSubContainer(GeListNode* node) { BaseContainer* docBc = node->GetDocument()->GetDataInstance(); BaseContainer bcSub; bcSub.SetLong(123, 456); docBc->SetContainer(789, bcSub); }
Tag plugin B
void TestTag_B::ReadSubContainer(GeListNode* node) { BaseContainer* docBc = node->GetDocument()->GetDataInstance(); BaseContainer bcSub = docBc->GetContainer(789); LONG test = bcSub.GetLong(123); GePrint(LongToString(test)); }
This works for me, all the time.
I can add the subcontainer from Tag A, delete Tag A, and still read the correct value using Tag B. -
On 02/03/2014 at 14:45, xxxxxxxx wrote:
Thanks for trying it with Tag plugins.
I'm guessing it works with them because Tag A is always there hosting the SubContainer?
But I can't seem make the document do the same thing (host the SubContainer with it's data in tact).Robert said containers are scope based. So what I would have expected is that a SubContainer created in a CommandData plugin would get deleted once the plugin closes.
But what I'm seeing from accessing the document's default container through the script manager is that the SubContainer itself does not get deleted...the data inside of it gets deleted.
Maybe that's what he meant?I still don't understand how people are storing retrievable data into the document with containers (or SubContainers).
-ScottA
Just to hopefully make it clearer.
If I were to use the same code I posted in a GeDialog type plugin instead of a CommandData type plugin. Then I'm guessing what would theoretically happen is that the SubContainer (including it's data) would be accessible from within the Script Manager and from within other plugins.
If...The GeDialog itself was open.
But once the GeDialog is closed..The data in the SC will be erased from the SubContainer and no loner accessible. Just like how it's working now in my CD plugin.
Is that correct? -
On 02/03/2014 at 16:48, xxxxxxxx wrote:
Ok. Something is very, very wrong here.
It works fine in Python. Even when using a CommandData plugin to create the SubContainer
But it's not working in C++.Python version that works as expected
#In a python CommandData plugin #Create the document's SubContainer and store data in it class MyPlugin(c4d.plugins.CommandData) : def Execute(self, doc) : docBc = c4d.BaseContainer() docBc.SetString(1, "Hello") doc[11223344] = docBc #<--- What's the C++ version of this code? c4d.EventAdd() return True #In the Script Manager you can then grab the SubContainer's data value import c4d def main() : docBc = doc.GetDataInstance() newBc = docBc.GetContainer(11223344) print newBc.GetString(1) #This works. Even when a CommandData plugin creates the SubContainer if __name__=='__main__': main()
Obviously the C++ code I'm using is not the equivalent to the python code I'm using.
Can anyone tell me what the C++ version of my python code is?-ScottA
DOH!
Never Mind. I found my errors.
SetContainer() has to be executed after the data is added to the SubContainer
And also another mistake I was making was using the plugin's ID#
In this case the ID has to be unique and not even the same ID as the plugin.The working C++ version
BaseContainer *docBc = doc->GetDataInstance(); //Get the document's default container BaseContainer newBc; //Create a new container newBc.SetString(1, "Hello"); //Put some data in the new container docBc->SetContainer(11223344, newBc); //Set the new container as a subcontainer inside of the document's container
Sheeesh. Some days it seems like you can't do even the simplest things correctly.
Thanks a lot for all the help guys. -
On 02/03/2014 at 23:13, xxxxxxxx wrote:
To underline your solution with some facts: The container you pass to SetContainer() is copied, that is
why modifying the original one after calling the method has no effect on the parent container. -
On 02/03/2014 at 23:14, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Sheeesh. Some days it seems like you can't do even the simplest things correctly.
Welcome, joining the club!
Normally, you would think that the BaseContainer is an instantiated class, a living object, floating around, always accessible, until it is destroyed. So that the order of operations is indifferent.
That's the way it is outside the C4D world.
I have to switch off normal thinking, when programming for C4D. In fact, I try not to think.
Over the last 9 months, I have adopted what I would call an instinct, for the weird C4D SDK world. So I more or less use my intuition, not common sense, when writing plugins.In my daily work, I am a programmer, I switch back to common sense, and get things done, fast. So, so far C4D plugins is a [time consuming] hobby, but I must admit I like it. And I cannot resist the thought, on a rather philosophical level, that maybe this strange world is the reason that C4D is so darn rock solid. Steady like a mountain.
-
On 03/03/2014 at 08:23, xxxxxxxx wrote:
Originally posted by xxxxxxxx
To underline your solution with some facts: The container you pass to SetContainer() is copied, that is
why modifying the original one after calling the method has no effect on the parent container.Gotcha. Thanks Niklas.
Robert said the same thing. But I didn't quite understand what he was talking about.
But after I wrote the code out. I can now see what he meant.-ScottA
-
On 03/03/2014 at 23:00, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Originally posted by xxxxxxxx
Sheeesh. Some days it seems like you can't do even the simplest things correctly.
Welcome, joining the club!
Normally, you would think that the BaseContainer is an instantiated class, a living object, floating around, always accessible, until it is destroyed. So that the order of operations is indifferent.
That's the way it is outside the C4D world.
I have to switch off normal thinking, when programming for C4D. In fact, I try not to think.
Over the last 9 months, I have adopted what I would call an instinct, for the weird C4D SDK world. So I more or less use my intuition, not common sense, when writing plugins.In my daily work, I am a programmer, I switch back to common sense, and get things done, fast. So, so far C4D plugins is a [time consuming] hobby, but I must admit I like it. And I cannot resist the thought, on a rather philosophical level, that maybe this strange world is the reason that C4D is so darn rock solid. Steady like a mountain.
It has really nothing to do with the Cinema 4D API, though. You are programming with C++, you
are not in a managed an environment such as C# as you are used to. Memory must be managed
by the developer.This is the reason why BaseContainer::SetContainer() must copy the passed container instead
of storing a reference to it!------------------------------------------------------------------------------------------------------------------------
For now, let us assume that it would not copy but store the reference (aka pointer to the
container).1.
void func(BaseContainer& parent) { BaseContainer child; parent.SetContainer(1000, child); }
This results in a crash when the parent container is destroyed.
2.
void func(BaseContainer& parent) { BaseContainer* child = new BaseContainer; parent.SetContainer(1000, *child); }
This results in a memory leak when the parent container is destroyed.
Keep in mind that SetContainer() accepts a BaseContainer& (reference), that is why I
dereference the pointer in the second example. It does take a reference to avoid that
the container is copied twice! (Once the function is called, a second time when the container
is actually stored in the parent container).Best,
-Niklas -
On 04/03/2014 at 01:23, xxxxxxxx wrote:
Originally posted by xxxxxxxx
It has really nothing to do with the Cinema 4D API, though. You are programming with C++, you
are not in a managed an environment such as C# as you are used to. Memory must be managed
by the developer.Thank you for clarifying this!
I'd just like to mention that, while I program mostly in C# these days, I grew up programming assembler and Object Pascal (Delphi). So I am used to taking care of memory all the time, and used to pointers, and have used pointers a lot.My plugins are now getting very complex. I ran a memory leak test on all my code, and got zero leakages
But still I maintain that the C4D way of doing things cannot simply be explained by "it is C++, not C#". In my world, even when programming C++, I would be able to find the iCustomGui by letting the BaseDocument search for it, or whatever. Then, when returning a valid iCustomGui, I would have access to all public methods of it, and be able to query all gadgets for their user edited values. Directly. In C++. As it is now, I still do not know how to build my own, working, iCustomGui.
Let us take the SplineData custom gui.
To add knots, to adjust tangents, one have to go through a lot of hokus pokus to do that. Not to mention read back the y value based on the x position. Hokus pokus this too.To sum it up - C4D SDK programming is far from Object Oriented programming, the way I am used to do it. And it might well be that I do not really understand C++, yet, but that is not all there is to it.