Container Owners?
-
On 02/03/2014 at 10:09, xxxxxxxx wrote:
You need to store the BaseContainer into the document or object BaseContainer - with its own unique ID. BaseContainers can have sub-containers. See BaseContainer::SetContainer() and GetContainer() in the documentation.
-
On 02/03/2014 at 10:49, xxxxxxxx wrote:
Hi Robert.
So when people say that they save data in the document using their own custom container. What they really mean is that they are creating a new sub container inside of the doc->GetDataInstance() container?Also.
Who are the owners of these containers that I'm making from scratch?:
BaseContainer myBc1;
BaseContainer *myBc2 = Null;When I create these container inside of plugins. The plugin is the owners right?
The reason I'm wondering about this is what if I do these in a CommandData plugin?
A CD plugin only runs once then it closes. So I'm wondering about the ownerships.
Does the container belong to the CD plugin?
Does the container get deleted after the CD plugin closes? Or does it still hang around in memory?When I see this kind of thing in code: owner- >whatever. It's obvious who the owner is.
But when the code has no explicit owner like this. I often get confused who the owner is.-ScottA
-
On 02/03/2014 at 11:07, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Hi Robert.
So when people say that they save data in the document using their own custom container. What they really mean is that they are creating a new sub container inside of the doc->GetDataInstance() container?Yes.
Originally posted by xxxxxxxx
Also.
Who are the owners of these containers that I'm making from scratch?:
BaseContainer myBc1;
BaseContainer *myBc2 = Null;In the first case, the owner is scope (the container will be created on the stack and removed once it goes out of scope). When you SetContainer(), the data (not the container itself) is then owned by the parent container but if you allocate the BaseContainer, you still need to free it. Note that there is no SetContainer() with a pointer - the container is 'instantiated' as data in the parent container and you still own the original (as it were).
Originally posted by xxxxxxxx
When I create these container inside of plugins. The plugin is the owners right?
The reason I'm wondering about this is what if I do these in a CommandData plugin?
A CD plugin only runs once then it closes. So I'm wondering about the ownerships.
Does the container belong to the CD plugin?
Does the container get deleted after the CD plugin closes? Or does it still hang around in memory?When you set any data to a BaseContainer, it exists as long as there is a 'physical form' (an instance). In the case of anything that belongs to a BaseDocument, it will exist in there as long as the document is opened and longer if saved to disk. In the case of a CommandData, only during a C4D session unless you save data using SetWorldPluginData() and GetWorldPluginData() (again, to disk).
Ownership here isn't the same as transferring pointers from my experience. If you allocate a BaseContainer onto the heap (id est: with gNew or GeAlloc() et al), even if you set it somewhere, you retain ownership of that memory and must free it. Cinema 4D 'recreates' the container otherwise and, I would imagine, handles ownership of that new instance internally.
The odd thing about BaseContainers is that they don't typically work like regular pointer instances. You can get a pointer to one, but you never own that. You can declare/allocate one, set it up, and pass it somewhere (in SendModelingCommand() or as a sub-container or numerous other ways), but C4D doesn't track the memory, only the data as a sort of clone of the BaseContainer. You own that instance whether scope-based on the stack or memory-based on the heap.
-
On 02/03/2014 at 11:39, xxxxxxxx wrote:
Originally posted by xxxxxxxx
When you SetContainer(), the data (not the container itself) is then owned by the parent container
This is confusing to me. Because if I create a new container using: BaseContainer myBc1;
This container doesn't have a parent container. Or does it?If I create a new container like this in the Execute() method of a CD plugin. And run the plugin.
Will this container then be accessable by other plugins for the current session(until I close C4D)?
Or will the end of the scope in the Execute() method of the CD plugin delete that container?
If the container does indeed get deleted by the scope.
I'm wondering if there's a way to create a container that will persist, and not get deleted when created in a CD plugin. For at least as long as the current session?
Or if creating a sub container inside of doc->GetDataInstance() inside of my Execute() method is the only way we can do this?I know how to write preferences plugins. And that's so far been my favorite way to create global gizmos that can be used as persistant global variables by any plugin or scripts.
I'm essentially using the gizmos in my custom preferences as global variables, or containers.
But people keep talking about storing data in the document using containers. So I'm trying to get a better grasp on that.
I didn't know they were storing things in a sub container of the document's default container.-ScottA
-
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.