Returning another class' function value
-
On 18/02/2013 at 13:54, xxxxxxxx wrote:
Moving the tag's class above the GeDialog class defeats the whole purpose of what I'm trying to do.
Sharing downwards is fairly simple.
But sharing upwards is proving to be very, very difficult.-ScottA
-
On 18/02/2013 at 14:05, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Moving the tag's class above the GeDialog class defeats the whole purpose of what I'm trying to do.Sharing downwards is fairly simple.But sharing upwards is proving to be very, very difficult.-ScottA
edit: actually you *could* move MyDialog::MyDialog() below the declaration of StorageTag. Declare both classes first, then provide the implementations. But that would then sum up about all your available options.
No it's not, what you are trying is simply impossible with c++
Solution: One class, one .h and one .cpp file:
MyDialog.h:
#pragma once #include <c4d.h> #define DIALOG_PLUGIN_ID 1000001 //The GeDialog plugin's ID //-------- The GeDialog plugin --------------------// //-------------------------------------------------// class MyDialog : public GeDialog { public: MyDialog(); BaseTag* tag; void doStuff() { LONG type = tag->GetType(); GePrint(LongToString(type)); } //GeDialog Overrides Bool CreateLayout(); Bool InitValues(); Bool Command(LONG id, const BaseContainer& data); Bool CoreMessage(LONG id, const BaseContainer& data); };
MyDialog.cpp:
#include "MyDialog.h" #include "StorageTag.h" // Storage tag used, need to include MyDialog::MyDialog() //The GeDialog's constructor { GeDialog(); tag = BaseTag::Alloc(TAG_PLUGIN_ID); StorageTag* MyTag = NULL; if(tag != NULL && tag->GetType() == TAG_PLUGIN_ID) { MyTag = static_cast<StorageTag*>(tag->GetNodeData()); } } //Register function here. ... etc.
StorageTag.h:
#pragma once #include <c4d.h> #define TAG_PLUGIN_ID 1000002 //The Tag plugin's ID //-------- The tag plugin -------------------------// //-------------------------------------------------// class StorageTag : public TagData { public: static NodeData* Alloc(); LONG imagecount; //<---I want to share this class member so I can use it inside the MyDialog class //Overrides StorageTag(); Bool Init(GeListNode* node); void Free(GeListNode* node); Bool Read(GeListNode* node, HyperFile* file, LONG level); Bool Write(GeListNode* node, HyperFile* file); Bool Message(GeListNode* node, LONG id, void* msgData); };
StorageTag.cpp:
#include "StorageTag.h" NodeData* StorageTag::Alloc() { return gNew StorageTag; } //Register function here. ...etc.
-
On 18/02/2013 at 14:31, xxxxxxxx wrote:
WickedP.
Do you still have questions about your code?
I've completely stolen your thread and I want to give it back to you.-ScottA
-
On 18/02/2013 at 15:24, xxxxxxxx wrote:
lol
-
On 18/02/2013 at 17:59, xxxxxxxx wrote:
Originally posted by xxxxxxxx
WickedP.
Do you still have questions about your code?
I've completely stolen your thread and I want to give it back to you.-ScottA
Ha That's no troubles at all Scott! I'm enjoying the conversation. I know next to nothing about C++ so it's all a learning experience from my perspective - it'll all help somewhere along the line I'm sure!
I'm actually away on holiday at the moment so it can be a bit difficult at times to log on. I'm currently in Cambodia's south, heading to the north in half an hour or so. So I'm trying to do what I can in the spare moments I have. I'm quite excited about my plugin, hence why I bombared the forums a little at times. Just excited to try and push further with it and get it out there! Would love to show you guys what I have at some stage. It's not a world stopper but... I think it'll have some use. Certainly for myself it will.
RE: sharing a functions return downwards - I'm still having troubles getting anything other than an empty return. I had wondered about using multiple .h files - perhaps that's a better way for me at this stage? But haven't had a chance to try that yet. Will keep playing around.
Cheers all,
WP. -
On 19/02/2013 at 10:10, xxxxxxxx wrote:
Going back to the examples you posted earlier.
I noticed that you are in the habit of using the type specifier on your class member variables when assigning values to them.
That will re-declare the variable in a different place. And make it a local variable that isn't globally available to your other methods like a class variable.
That's probably why you keep getting no return values.Once you've declared a class member variable: String myString;
Don't use the "String" type specifier on it when you go and assign a value to it later on.
Just use the variable's name: myString = "HelloWorld".-ScottA
-
On 20/02/2013 at 03:38, xxxxxxxx wrote:
Hi Scott,
I've just had a peek at my code and this particular string is declared without the type. It GePrints fine from the function if I run it from within the class, but when the bottom class runs it the prints (even when within the function itself) are empty.Not sure what else I can do.
WP. -
On 20/02/2013 at 03:49, xxxxxxxx wrote:
Not sure what else I can do.
Post compilable code that does not work for you, and some one here may be will find the solution -
On 20/02/2013 at 05:06, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Going back to the examples you posted earlier.
I noticed that you are in the habit of using the type specifier on your class member variables when assigning values to them.
That will re-declare the variable in a different place. And make it a local variable that isn't globally available to your other methods like a class variable.
That's probably why you keep getting no return values.Once you've declared a class member variable: String myString;
Don't use the "String" type specifier on it when you go and assign a value to it later on.
Just use the variable's name: myString = "HelloWorld".Wrong, it's simply class-construction.
String str = "outter"; /* new scope */ { str = String("inner"); } GePrint(str);
String str = "outter"; /* new scope */ { String str = String("inner"); } GePrint(str);
-Niklas
-
On 20/02/2013 at 05:32, xxxxxxxx wrote:
Firs example is a bit confusion.
Why not use the same way to assign?
But this is a more code style question.String str = String("outter"); /* new scope */ { str = String("inner"); } GePrint(str);
Second is error-prone and should not be used!
-
On 20/02/2013 at 05:45, xxxxxxxx wrote:
_ >>> Why not use the same way to assign?_
> I wanted to demonstrate that there is no difference because of the implementation of the String
>
> class. I don't know if the String class explicitly implements the assignment operator for char*, but
>
> even if it does not, the constructor will be called and then assigned to the instance because the
>
> constructor accepts a char*.> >> Second is error-prone and should not be used!
>
>
>
> Agreed, it's very bad style.-N
-
On 20/02/2013 at 09:50, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Wrong, it's simply class-construction.
-NiklasNo. I'm not wrong.
When you create a class member variable. You do not then re-assign a new type specifier to it when you go to use it.@WickedP.
I've also found problems with getting zero returns. Even when doing top down sharing.
Apparently even top down class sharing in these plugins is not as simple as I thought.
I've figured out how to solve the zero return problem by making one class a friend of the other.
But so far I'm only getting the class member's initial constructor value.
If I change the value of the top class member. I don't get the new changed in the bottom class.
I need to work on it some more.@Remo:
Using the Alloc() method proved to be a dead end for me because it only returns BaseList2D elements. It does not inclue any of the custom class members in the class.
Using "friend class" seems to be the best method I've tried so far. The one that's produced the best results for me. But I'm still trying to flesh it all out and get updated values to work.-ScottA
-
On 20/02/2013 at 10:22, xxxxxxxx wrote:
> >> No. I'm not wrong.
When you create a class member variable. You do not then re-assign a new type specifier to it when you go to use it.
_
_
Probably a misunderstanding. What I figured from your post, is that WP should use> String myString;
>
> /* ... */
>
> myString = "Hello";Instead of
> String myString;
>
> /* ... */
>
> myString = String("Hello");.. because that would redeclare the variable. Is that what you said?
-
On 20/02/2013 at 11:38, xxxxxxxx wrote:
Yeah.
I noticed he seemed to be in the habit of re-declaring a class variable when he shouldn't be.-ScottA
-
On 20/02/2013 at 12:15, xxxxxxxx wrote:
But
myString = String("Hello");
does not redeclare the variable. It creates a String object using the String constructor and assigns
it to the local variable myString. If it would redeclare the variable, this would print 5, but it prints 10:>
> int main() {
> int a = 5;
> a = int(10);
>
> std::cout << a;
> }PS: More extended example: http://codepad.org/rGUgwY0j
-Niklas
-
On 20/02/2013 at 12:28, xxxxxxxx wrote:
Using the Alloc() method proved to be a dead end for me because it only returns BaseList2D elements.
You may need to make proper cast.
// be sure to use a unique ID obtained from www.plugincafe.com #define ID_LOOKATCAMERATAG 1001165 class LookAtCameraData : public TagData { //... public: String my_string; }; void Test() { AutoFree<BaseTag> tag = BaseTag::Alloc(ID_LOOKATCAMERATAG); //or //BaseTag *tag = doc->GetActiveTag(); if(tag){ LookAtCameraData *data_ptr = static_cast<LookAtCameraData*>(tag->GetNodeData()); GePrint(data_ptr->my_string); } }
> Apparently even top down class sharing in these plugins is not as simple as I thought.
Really do not udestand this problem.
It work in both directions.Remo
-
On 20/02/2013 at 13:05, xxxxxxxx wrote:
^I don't understand how creating a test() method that points back to the same plugin (the tag) it's residing in. Helps me to use that string class member within my GeDialog?
When I tried it. I tried to do this kind of thing from within the GeDialog. So that the dialog could use the tag's class members.
I tried to allocate a tag instance using the same code you're using. But I did it from inside of the GeDialog. Not from inside the Tag.
That might be why it didn't work. I'm really terrible at casting BTW.Your example works for me if I use it inside my tag class.
But that leaves me wondering...OK...so now what?
How does doing this allow my GeDialog to see and use the tag's "my_string" class member's value?-ScottA
-
On 20/02/2013 at 14:32, xxxxxxxx wrote:
You'll have to pass the tag pointer to the GeDialog either to store as a member variable or as just a function argument. As long as my_string is public. If not, then you need to create an access method (like:
String LookAtCameraData::GetMyString() { return my_string; }
// be sure to use a unique ID obtained from www.plugincafe.com #define ID_LOOKATCAMERATAG 1001165 class LookAtCameraData : public TagData { //... public: String my_string; }; void GeDialog::Test(BaseTag* tag) { if(tag) { LookAtCameraData *data_ptr = static_cast<LookAtCameraData*>(tag->GetNodeData()); GePrint(data_ptr->my_string); } }
-
On 20/02/2013 at 14:53, xxxxxxxx wrote:
Thanks Robert.
I just realized my silly mistake.
The Test() method Remo posted was never meant to be included inside of the tag's class structure.
It's meant to be posted after it. Below the Tag's registration code.Example:
void Test(); //Forward declare the method so it can be used in the GeDialog class class MyDialog : public GeDialog { public: //Overrides Bool CreateLayout(); Bool InitValues(); Bool Command(LONG id, const BaseContainer& data); Bool CoreMessage(LONG id, const BaseContainer& data); }; ...The rest of the dialog's stuff //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// class StorageTag : public TagData { public: static NodeData* Alloc() { return gNew StorageTag; } String my_string; //Overrides StorageTag(); Bool Init(GeListNode* node); void Free(GeListNode* node); Bool Read(GeListNode* node, HyperFile* file, LONG level); Bool Write(GeListNode* node, HyperFile* file); Bool Message(GeListNode* node, LONG id, void* msgData); }; ...The rest of the Tag's stuff //////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// void Test() { AutoFree<BaseTag> tag = BaseTag::Alloc(TAG_PLUGIN_ID); //or use: BaseTag *tag = doc->GetActiveTag(); if(tag) { StorageTag *data_ptr = static_cast<StorageTag*>(tag->GetNodeData()); GePrint(data_ptr->my_string); } }
I knew I was missing something stupid. I just had to find it.!
Embarrassed
[URL-REMOVED]
I'll give this new method a try for a while and see if I run into any problems.Thanks Remo.
-ScottA
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.
-
On 20/02/2013 at 20:28, xxxxxxxx wrote:
Other examples have already been given, but I'm going to post another to try and tie everything together, using complete examples. We begin with code that won't compile (note that in C++, structs are the same as classes, but with default public access, so they can be nice for making uncluttered examples), and show where, and why, it is failing:
struct A { int foo(B& b) { // <- error: 'B' has not been declared return b.i; } }; struct B { int i; }; int main() { A a = A(); B b = B(); return a.foo(b); }
Okay, the compiler says 'B' has not been declared, so let's fix this by forward declaring B before attempting to use it from within A:
struct B; // <- forward declare B struct A { int foo(B& b) { return b.i; // <- error: invalid use of undefined type 'struct B' } }; struct B { int i; }; int main() { A a = A(); B b = B(); return a.foo(b); }
What did the forward declaration do? It got us one line further. But note the difference in the errors: the first complained about declaration, the second about use of undefined type 'B'. This shows that the compiler now knows of a label called 'B', thanks to our forward declaration, but it still has no idea what, or where B::i is. Just as we made the compiler aware of the existence of B with a declaration, we will solve this problem by turning our definition of A::foo(B&) into a declaration, and deferring its definition until after B has been fully defined:
struct B; // <- forward declare B struct A { int foo(B& b); // <- declare(only!) A::foo(B&) }; struct B { int i; }; int A::foo(B& b) { // <- define A::foo(B&) return b.i; // <- ok: B is fully defined } int main() { A a = A(); B b = B(); return a.foo(b); // <- ok: A & B are fully defined }
Now we're golden. Something to note is that there is no special place (e.g. the very bottom of the file) that we have to do the definition -- the sole requirement is that any code it uses already be defined. In fact, what we have done here, in a single code region, is exactly the same as what we'd have done had we separated the code into h and cpp files. One reason (of many) for separating into h and cpp is that it helps us to remember to avoid mixing declarations and definitions. Just remember that if you #include a header, this is done by the preprocessor, and is equivalent to copy/pasting the entire contents of the header, verbatim, at the #include point.
A good mental check, if you run into a compilation issue, is to put yourself in the place of the compiler. Look at the usage, and ask yourself: would you need to read the rest of the code before you could identify a particular construct? Take the second example above; erase all code below the error, and look at the statement return b.i; You'll find yourself in agreement with the compiler, that there is not yet enough information to do the job.
To finish up this part of the example, though we already know, logically, since A is able to use B, that it follows A and B are able to use each other, for the sake of completeness, let's just prove it:
struct B; // <- forward declare B struct A { int i; int foo(B& b); // <- declare A::foo(B&) }; struct B { int i; int bar(A& a); // <- declare B::bar(A&) }; int A::foo(B& b) { // <- define A::foo(B&) return b.i; // <- ok: B is fully defined } int B::bar(A& a) { // <- define B::bar(A&) return a.i; // <- ok: A is fully defined } int main() { A a = A(); B b = B(); int i = a.foo(b); // <- ok: A & B are fully defined int j = b.bar(a); // <- ok: A & B are fully defined return 0; }
Now, with this out of the way, some further comments regarding the prior mention of difficulties with "top-down sharing." As we have seen above, it is entirely possible to do this, provided we allow the compiler to understand the code we are using, at the point we use it. There is, however, one thing we will find quite impossible: to include an instance of B as a data member of A. Why? Witness the objects of infinite static size:
struct A { B b; // <- error: field 'b' has incomplete type }; struct B { A a; }; int main() { A a; a.b.a.b.a.b.a.b.a.b.a.b.a.b.a.b.a.b.a.b..... return 0; }
This, could you compile it, would be equivalent to recursively including an instance of A as a data member of itself -- it just happens here indirectly, through the inclusion of an instance of B. Imagine though, that we were dealing not with two tiny classes, and rather with a large and complicated object model. How long would it be before you inadvertently tried to do exactly this? By the time you realized it, much design work might already have been done, and you could be looking at a costly re-design.
By definition, the C++ compilation rules prevent this from being possible. In languages with more flexible compilation rules though, C# for example, if you define a struct which includes an instance of itself, the compiler must explicitly check for, and catch this; you will receive an error stating "Struct member 'foo' of type 'Foo' causes a cycle in the struct layout."
It is, of course, perfectly fine to go the other way round and include an instance of A in B, because by that time we do that, A has been fully defined, and there is no longer any possibility of generating a recursive definition. So the final piece to the puzzle is: what if we really do want A and B to contain instances of each other, or even of themselves? It must be done using pointers:
struct B; struct A { B *b; A *foo(); }; struct B { A *a; B *bar(); }; A *A::foo() { return b->a; } B *B::bar() { return a->b; } int main() { A a = A(); B b = B(); a.b = &b; b.a = &a; A *pA = a.foo(); B *pB = b.bar(); return pA - b.a + pB - a.b; }
Note that it is still necessary for classes to be fully defined before they are actually used. The primary function of the C++ compiler is to read your code and generate the blueprints for turning it into memory layouts at runtime; to do that, it needs to figure out how large they are, and at what relative memory offsets their data and methods are located. It follows, then, that in order to generate the layout for an object, any object used inside that object must already have been fully defined, and so on.
Hopefully this is helpful in furthering your understanding of C++, and in dispelling any notions you may have that it could somehow work differently in Cinema plugins than it does elsewhere -- because it doesn't.