Returning another class' function value



  • On 17/02/2013 at 03:58, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   R14 
    Platform:   Windows  ;   
    Language(s) :     C++  ;

    ---------
    Hi Folks,
     
    Could someone please give me a quick C++ lesson in returning another class' function value? I've done copius Google searches but as none are C4D-related in their coding, I'm having a bit of a rough time converting it over.
     
    I've got something like the below (which might not quite be written right, but I'm sure you brainiacs will understand what's going on!) :

    class MyClass1
    {
    public:
     
    String MyString(String temp)
        {
            temp = String("Print successful!");
            return temp;
        }
    }
     
    class MyClass2
    {
        MyClass1 MyC1;
     
    public:
     
    virtual Bool MainFunction()
        {
            String bogus = String("Bogus..");
            String Pnt = MyC1.MyString(Bogus);
            GePrint(Pnt);
            return TRUE;
        }
    }
    

    But my prints aren't returning anything. I've tried all all sorts of variations of the above (some were compile erronous, other compiled but gave no print result and etc), but none seem to be giving me what I'm after.
     
    Regards,
     
    WP.



  • On 17/02/2013 at 05:04, xxxxxxxx wrote:

    But you code is working.
    You will get

    Print successful!
    

    You can also change it this way and get

    Bogus..Print successful!
    
      
      
        
        
        class MyClass1
        {
        public:
        	String MyString(const String &temp)
        	{
        		//temp = String("Print successful!");
        		//return temp;
        		return temp + String("Print successful!");
        	}
        };
         
        class MyClass2
        {
        	MyClass1 MyC1;
        public:
        	virtual Bool MainFunction()
        	{
        		const String bogus = String("Bogus..");
        		const String Pnt = MyC1.MyString(bogus);
        		GePrint(Pnt);
        		return TRUE;
        	}
        };
      
        
        
        MyClass2 mc2;
        mc2.MainFunction();
    

    But one point is that I am using a bit improved String class with C++11 move support  :)

    Remo



  • On 17/02/2013 at 07:54, xxxxxxxx wrote:

    Thanks Remo,
     
    I did some further testing and it does appear to be working as you say. And I may have come across where the problem is occuring.
     
    Using the code example above, I'm getting the value of the string temp (in the MyClass1 function) from another class level string - i.e. temp = AnotherString; AnotherString is being defined elsewhere in MyClass1. But passing that is returning an empty value when printed from the second class. When I print the AnotherString value from inside the first class it prints fine, but it (AnotherString) seems to get wiped if the MyString function's value returning temp is called from another class....
     
    For now I'll need to let this one go for the evening and pick it up again tomorrow. Cheers again!
     
    WP.



  • On 17/02/2013 at 10:49, xxxxxxxx wrote:

    Sharing the top class's members with the bottom class is easy.
    But trying to share the bottom class with the top class. Or both classes with each other doesn't work in these C4D plugins the same way it works with raw C++.

    In raw C++ all you have to do is declare the bottom class before the first class.
    Then you can create an instance of the bottom class in the first class.
    But that will not work in these C4D plugins. If you try it you'll get an undefined class compile error.
    Even though the class has been declared above the first class. 😠

    Trying to use the friend class option brings up other problems.
    In Raw C++ it's not a problem at all. But in these plugins, the big problem is how do call the methods from the other class:
    https://plugincafe.maxon.net/topic/6851/7642_allowing-plugins-to-talk-to-eachother

    In short.
    Sharing classes works a little bit differently in these plugins compared to raw C++.
    And there's no information anywhere how to do this kind of class sharing with C4D plugins.

    -ScottA



  • On 17/02/2013 at 12:09, xxxxxxxx wrote:

    Hi ScottA,

    I don't really understand the problem. "these plugins" are written in C++, and it stays C++: You
    can syntactically and semantically rely on the same behavior as on writing "raw C++". Could you
    please elaborate more on the topic, maybe provide an example? I really don't see where you see
    a difference between "raw C++" and "Cinema 4D Plugin's C++". Thanks!

    -Niklas



  • On 17/02/2013 at 12:31, xxxxxxxx wrote:

    Sure.
    Here's a small example:

    class MyTag;                <-- In raw C++ this is how you tell the compiler to go get the MyTag class  
                                  Problem: This works fine in raw C++. But not when doing it inside of a C4D plugin.  
      
    class MyDialog : public GeDialog  
    {  
      public:  
      
          MyTag tag;           <--- Complier error: Undefined class!!?  
      
          GeDialog Overrides  
          Blah, blah, etc..  
        
    };  
      
      
    class MyTag : public TagData  
    {  
      public:  
      
          MyDialog dlg;         <--- MyDialog has been seen by the compiler and works fine  
                                     Note: This works fine in both raw C++ and in C4D plugins  
      
          TagData Overrides  
          Blah, blah, etc..  
       
    };
    

    The friend class problem I'm having is a different one.
    I can set up the classes. But the problem is how to call the methods that have params. in them
    In raw C++ we would call the methods from the main() code block.
    But in C4D plugins. I don't know how to call them from the various plugin's methods. Instead of the main() code block in raw C++.

    These are two examples of how the slight differences between raw C++ and the C++ applied in these plugins can be different enough that it's difficult to apply the raw C++ techniques on the internet to our plugins code.

    -ScottA



  • On 17/02/2013 at 12:35, xxxxxxxx wrote:

    Hi Scott,

    this shouldn't work in a "raw C++" environment, either. At the time "MyTag" was declared (which is
    the first line in your example), the compiler does not know the size it needs to allocate for the class.
    It requires this information for calculating the size of the other classes that store the "MyTag" class
    as a direct member.

    A typical error-message for this error, is like "field 'tag' has an incomplete type" like the G++
    compiler will tell you: http://codepad.org/yQkC2tpl

    A pointer however has a fixed size known by the compiler at all time. You can easily create a
    pointer to an incomplete type: http://codepad.org/MsXOmycf

    A forward-declared class can only be stored by reference, since the compiler does not  know
    the  size of the class!

    class MyTag;
      
    class MyDialog {
      
    public:
      
        MyTag* tag;
      
    };
      
    class MyTag {
      
    };
    

    Originally posted by xxxxxxxx

    But in C4D plugins. I don't know how to call them from the various plugin's methods. Instead of the main() code block in raw C++.

    Well, to call a method, you usually have an instance of the class, right? Probably just calling the
    method on it should do it. 🙂

    Maybe you can give me an example for this as well, since I don't seem to understand your "friend
    class" problem.

    Best,
    Niklas



  • On 17/02/2013 at 12:55, xxxxxxxx wrote:

    Forward declaring a class does work in a raw C++ environment.
    Raw C++ example using a forward declared class:

      
        
        
        class tile_tree_apple;     <--Same as in my plugin example
        
        class tile_tree : public tile
        {
          public:
              tile onDestroy();    <-- These bottom class members work fine in raw C++. But not in C4D plugins
              tile tick();
              void onCreate();        
        };
        
        class tile_tree_apple : public tile
        {
          public:
              tile onDestroy();
              tile tick();
              void onCreate(); 
              tile onUse();       
        };
    
    Note: I just plucked this code from the internet to show an example. I didn't check it to see if it works.  
    But I've done this kind of thing myself. And believe me it does work.  
    You can definitely use a forward class definition this way to share class members between classes.  
    But it doesn't work in C4D plugins... At least I can't make it work.  
      
    I've already posted a friend example in the other thread.  
      
    -ScottA  
      
    \*Edit - It's been a while since I worked on this problem and I forgot one other thing.  
    If you use: MyTag\* tag;   instead of: MyTag tag;  
    You just move the undefined class problem to where you try to use the class instance variable "tag".  
      
    For example: tag->SomeMemberFromTheTagClass //<---- Undefined class error  
    


  • On 17/02/2013 at 13:48, xxxxxxxx wrote:

    What do you mean with 'not working'? Declaring, implementing, overriding, calling, referencing, .. ?

    Edit: Just seen your edit, wait a moment pls.

    --------

    Were do you define the class then? Could you please show me the
    (complete) headers and the implementation files? It is very important
    where you do the forward-declaration, the definition, the implemenation
    and the usage.

    The Cinema API is using many forward-declarations btw.



  • On 17/02/2013 at 14:00, xxxxxxxx wrote:

    Sorry but this example makes no sense to me.

    What exactly do you want to do?

    > You can definitely use a forward class definition this way to share class members between classes.
    Share class members, may be you should take a look at std::shared_ptr or boost equivalent of it.

    This would make sense.
    Of course you need to make sure that the pointer are always valid, because C4D (user) may delete you Tag any time and the point will be invalid.
    NodeData* GetNodeData(LONG index = 0) can be used to get it.
    Look at  hair_render.cpp  example for this.

      
        
        
        class MyTag;     
         
        class MyDialog : public GeDialog
        {
        public:
        	MyTag   *tag;  //Actually this is really dangerous and should not be made !!!  
            
        	//GeDialog Overrides
        	//Blah, blah, etc..
        
        };
        
        class MyTag : public TagData
        {
        public:
         
        	MyDialog  *dlg; //Actually this is really dangerous and should not be made !!!    
         
        	//TagData Overrides
        	//Blah, blah, etc..
         
        };
      
    

    Actually this is really dangerous and should not be made !!!



  • On 17/02/2013 at 14:05, xxxxxxxx wrote:

    And btw: Using cyclic non-pointers would lead to infinite memory consumption.

    class B;
      
    class A {
        B b;
    };
      
    class B {
        A a;
    };
    

    This would be technically impossible.

    Remo: Would you mind to elobarate more on why it is dangerous, what
    you have mentioned?

    Edit: ah I see. Dangerous because the pointer could be invalid, not
    because of security reasons. :)



  • On 17/02/2013 at 14:46, xxxxxxxx wrote:

    If I use a pointer like in Remo's example. The minute I try to use that pointer in my plugin code. I get an undefined class error.

    Sharing the top class's members with the bottom class is a no brainer. It's like pouring water down a drain. It's simple and easy.
    But sharing a bottom class's members with the the previous class (top class) is something I've only been able to do in raw C++. And not at all in C4D plugins.
    The friend class is a completely separate approach I was trying out.
    I was looking for a way to share the members of two different classes without using a third shared class or struct. And on the internet there's lots of examples of using a friend class to do this sharing in both directions.
    But in C4D plugins none of this stuff is working for me like it does in raw C++.

    The main differences I'm seeing between working in Raw C++ vs C4D plugins:
    Raw: In raw C++ I spend lots of time inside the main() function.
    C4D: In C4D plugins I spend almost no time at all in the main function (main.cpp).
    Raw: In raw C++. I almost never inherit from another class. It's not something I need to do very often.
    C4D: In C4D plugins. Almost everything is inherited from another class.

    Sharing a class's members with a lower class                --> simple in Raw C++ and C4D
    Sharing a class's members upwards to a previous class -->simple in Raw C++...But cannot do it in C4D plugins

    -ScottA



  • On 17/02/2013 at 15:09, xxxxxxxx wrote:

    My be you want to do something like this:
    This is based on triangulate.cpp and menutest.cpp from c4d_sdk

      
        
        
        ///menutest.cpp
        
      
        
        
        #include "c4d.h"
        #include "c4d_symbols.h"
         
        #include "../object/triangulate.h"
        
        
        class MenuTest : public CommandData
        {
        	public:
        		virtual Bool Execute(BaseDocument *doc);
        };
         
        Bool MenuTest::Execute(BaseDocument *doc)
        {
        	if(!doc){ GePrint("!doc");  return false; }
        	BaseObject *obj = doc->GetActiveObject();  
        	if(!obj){ GePrint("No Object Selected!");  return false; }
         
        	TriangulateData *tri_data = static_cast<TriangulateData*>( obj->GetNodeData() );
         
        	GePrint("String from Object:  <" + tri_data->GetMyString() + ">");
         
        	tri_data->mdata_ref = std::make_shared<MyData>(" MyData Name ");
         
         
        	return true;
        }
         
        Bool RegisterMenuTest(void)
        {
        	// be sure to use a unique ID obtained from www.plugincafe.com
        	return RegisterCommandPlugin(1000956,GeLoadString(IDS_MENUTEST),0,AutoBitmap("icon.tif"),String("C++ SDK Menu Test Plugin"),gNew MenuTest);
        }
      
    
      
        
        
        ///triangulate.h
        #include "c4d.h"
         
        #include <memory> //shared_ptr and more
         
        class MyData;
        class MenuTest;
         
        //=====================================================================================================================
        class TriangulateData : public ObjectData
        {
        	friend MenuTest;
        private:
        	TriangulateData() : m_object_string("TriangulateData") {    }
         
        	LineObject *PrepareSingleSpline(BaseObject *generator, BaseObject *op, Matrix *ml, HierarchyHelp *hh, Bool *dirty);
        	void Transform(PointObject *op, const Matrix &m);
         
        public:
        	virtual BaseObject* GetVirtualObjects(BaseObject *op, HierarchyHelp *hh);
         
        	const String& GetMyString() const { return m_object_string; }
         
        	static NodeData *Alloc(void) { return gNew TriangulateData; }
        private:
        	std::shared_ptr<MyData> mdata_ref;
         
        	String	m_object_string;
        };
        //=====================================================================================================================
        class MyData
        {
        public:
        	MyData() {}
        	MyData(const String& str) :  m_str(str) {}
         
        	~MyData() { GePrint(" MyData destructor "); }
         
        	String m_str;
        };
      
    


  • On 17/02/2013 at 15:55, xxxxxxxx wrote:

    Interesting. There's a little bit of everything going on in that example.

    I just want to share a class's members upwards to the previous class.
    But this thing is also using friend, and shared pointers. And I'm not sure how much part those things are playing in that end goal.
    I'll have to study this thing for a while and try to see if I can simplify it with some more simplistic LONG type class member variables and see if I can get the MyData class to share upwards to the previous class.

    Thanks,
    -ScottA



  • On 17/02/2013 at 21:05, xxxxxxxx wrote:

    Thanks for the conversation folks.
    Over at my end however, it's still not recieving the right String value. Whenever I copy another string over to the functions string, it returns an empty value. See below for what I'm trying to do.
    This one works:

      
    class MyClass1  
    {  
    public:  
     String MyString(String temp)  
      {  
          temp = String("Print successful!");  
          return temp;  
      }  
    };  
    

    But this one doesn't:

      
    class MyClass1  
    {  
    String AnotherString;    // class level String variable.  
    public:  
    Init(void)  
      {  
          AnotherString = String("This way won't work.");  
      }  
     String MyString(String temp)  
      {  
          // AnotherString (and 'temp') always returns empty when done this way  
          // AnotherString = String("Doesn't work either.");    // uncommenting this line doesn't work either.  
          temp = AnotherString;  
          return temp;  
      }  
    };  
    

    What's the differene here? Why won't the second way work?
    WP.



  • On 18/02/2013 at 01:24, xxxxxxxx wrote:

    Hi WickedP,

    what's the sense of passing a string that is not used at all? Are you calling Init() before MyString() ?
    You're also missing the return-type for the Init() method. Also note that a void parameter is
    deprecated for C++ and is used in C only.

    http://ideone.com/GZeyoY

    #include <string>
    #include <iostream>
     
    class MyClass {
        
        std::string my_string;
        
    public:
     
        void Init() {
            my_string = "Hello!";
        }
        
        std::string GetMyString() {
            return my_string;
        }
        
    };
     
    int main() {
        MyClass obj;
        obj.Init();
        std::cout << obj.GetMyString();
    }
    

    ScottA :

    The following does not work: http://ideone.com/ASo7LI
    The function MyFoo::DoStuff() is not declared since MyFoo is an incomplete type at the point
    MyFoo::DoStuff() is used.

    #include <string>
    #include <iostream>
     
    #define PFUNC() do { std::cout << \__FUNCTION\_\_ << "\n"; } while (0)
     
    class MyFoo;
     
    class MyBar {
        
        MyFoo\* foo;
     
    public:
     
        MyBar(MyFoo\* foo) : foo(foo) {
        }
     
        void DoStuff() {
            PFUNC();
    **         foo->DoStuff(); // MyFoo is still an incomplete type!**
        }
    };
     
    class MyFoo {
        
    public:
     
        void DoStuff() {
            PFUNC();
        }
    };
     
    int main() {
        MyFoo foo;
        MyBar bar(&foo);
        
        bar.DoStuff();
    }  
    

    Implementing MyBar::DoStuff() after MyFoo became a complete type, it
    works: http://ideone.com/3G2Xub

    #include <string>
    #include <iostream>
     
    #define PFUNC() do { std::cout << \__FUNCTION\_\_ << "\n"; } while (0)
     
    class MyFoo;
     
    class MyBar {
        
        MyFoo\* foo;
     
    public:
     
        MyBar(MyFoo\* foo) : foo(foo) {
        }
     
        ** void DoStuff();**
    };
     
    class MyFoo {
        
    public:
     
        void DoStuff() {
            PFUNC();
        }
    };
     
    **void MyBar ::DoStuff() {
        PFUNC();
        foo->DoStuff(); // MyFoo is now a complete type!
    }**
     
    int main() {
        MyFoo foo;
        MyBar bar(&foo);
        
        bar.DoStuff();
    }
    

    If that's not what you mean, and does not solve the problem, could you please post a full
    example that you think should compile but doesn't because of the undefined class error?

    -Niklas



  • On 18/02/2013 at 01:56, xxxxxxxx wrote:

    Hi Nik,
    just to clarify a few points for you:

    Originally posted by xxxxxxxx

    what's the sense of passing a string that is not used at all?

    the string is used in MyClass1 (which is actually a GeUserArea) but the userarea code is hundreds of lines long so I was a bit reluctant to paste all of that. The example I've been providing above is just a basic translated version of what I have.

    Originally posted by xxxxxxxx

    Are you calling Init() before MyString() ?

    As it's a user area, Init is called first. However the string is being updated/defined in a few other functions so it is in use elsewhere, and is for other purposes.

    Originally posted by xxxxxxxx

    You're also missing the return-type for the Init() method. Also note that a void parameter is
    deprecated for C++ and is used in C only.

    My mistake in translating things over. Needless to say the string (and the GeUserArea) all runs as expected, and the string variable is filled well before any call to the "MyString()" function is run. The issue seems to be if you get the function's string value from another variable - the string value (both the function's and the class level variable) are when passed to another class.
     
    Cheers,
     
    WP.



  • On 18/02/2013 at 02:07, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Sure.Here's a small example:

    class MyTag;                <-- In raw C++ this is how you tell the compiler to go get the MyTag class   
                                    Problem: This works fine in raw C++. But not when doing it inside of a C4D plugin.   
     
    class MyDialog : public GeDialog{   
        public:        MyTag tag;           <--- Complier error: Undefined class!!?   
            GeDialog Overrides   
            Blah, blah, etc..   
      };   
     
    class MyTag : public TagData{   
        public:        MyDialog dlg;         <--- MyDialog has been seen by the compiler and works fine   
                                       Note: This works fine in both raw C++ and in C4D plugins   
            TagData Overrides   
            Blah, blah, etc..   
     };   
    

    Originally posted by xxxxxxxx

    this shouldn't work in a "raw C++" environment, either.

    That's a normal forward declaration, and it works. I use it all the time.
    To clarify, there is nothing like "CINEMA 4D C++". It's all the same. Standard compiler, standard language, just some custom classes and structs.
    No difference in language behavior whatsoever.



  • On 18/02/2013 at 03:31, xxxxxxxx wrote:

    Hi Jack,
     
    the behaviour with the forward declaring seems to be different, as I get the same compiler error as Scott when forward declaring like that in a C4D plugin.
     
    WP.



  • On 18/02/2013 at 04:07, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Hi Jack,
     
    the behaviour with the forward declaring seems to be different, as I get the same compiler error as Scott when forward declaring like that in a C4D plugin.
     
    WP.

    "In C++, classes can be forward-declared if you only need to use the pointer-to-that-class type (since all object pointers are the same size, and this is what the compiler cares about)."

    Can't use forwards declation with

    MyTag tag;
    

    It's just C++, guys. Nothing funny going on here at all.


Log in to reply