Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Container Owners?

    SDK Help
    0
    15
    1.2k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H
      Helper
      last edited by

      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

      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        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.. 😉

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          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

          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

            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.

            1 Reply Last reply Reply Quote 0
            • H
              Helper
              last edited by

              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?

              1 Reply Last reply Reply Quote 0
              • H
                Helper
                last edited by

                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.

                1 Reply Last reply Reply Quote 0
                • H
                  Helper
                  last edited by

                  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.

                  1 Reply Last reply Reply Quote 0
                  • H
                    Helper
                    last edited by

                    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.

                    1 Reply Last reply Reply Quote 0
                    • H
                      Helper
                      last edited by

                      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

                      1 Reply Last reply Reply Quote 0
                      • H
                        Helper
                        last edited by

                        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

                        1 Reply Last reply Reply Quote 0
                        • H
                          Helper
                          last edited by

                          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.

                          1 Reply Last reply Reply Quote 0
                          • First post
                            Last post