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