Returning another class' function value
-
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.
-
On 22/02/2013 at 03:26, xxxxxxxx wrote:
Thanks JDHill,
that provides some sense and order. I seem to have managed to get the function in the following class to receive and print the string value now. However there's still one small issue I can't seem to resolve. I can't attach the functions new string value to a local string. Example below of what I'm referring too (my apologies for any syntax errors etc, it's just for demo, hope you see what I mean!) :
This works fine:class MyClass2; class MyClass1 { MyClass2 *MC2; public: MyFunction(); }; class MyClass2 { MyClass1 *MC1; String LocalString; // local string variable - not used in this example public: Class2MyFunction(String PassingString) { return PassingString; } }; MyClass1::MyFunction() { String pass = "Blah"; MC2.Class2MyFunction(pass); return TRUE; }
This one however, crashes cinema (the difference is in trying to declare the local string in MyClass2's function) :
class MyClass2; class MyClass1 { MyClass2 *MC2; public: MyFunction(); }; class MyClass2 { String LocalString; // local string variable MyClass1 *MC1; public: Class2MyFunction(String& PassingString) { LocalString = PassingString; // crashes.. return PassingString; } }; MyClass1::MyFunction() { String pass = "Blah"; MC2.Class2MyFunction(pass); return TRUE; }
What's the trick I'm missing here?
WP. -
On 22/02/2013 at 07:44, xxxxxxxx wrote:
First of all, by standard, you should pass by const reference when you do not change the reference (goes back to C++ standard saying "non-const references cannot bind to temporary objects"...it's always good advice anyway!). Then you should not assign the string but initialise it directly in the constructor initialisation list like:
Class2MyFunction(const String& PassingString) : LocalString(PassingString) {}
There is also no return value for the constructor.
But I just see (silly me) that this is not your constructor so it should just be constant (and of course it lacks a return value definition in front of the function). But I leave the above text for completeness
-
On 22/02/2013 at 22:57, xxxxxxxx wrote:
WickedP, your second example should not compile (I didn't check the first), so I'm not sure how you know that it crashes. Please take a look at the following and see if it helps. It is just two tiny classes, Data and Proxy, where Data has a String, and Proxy has a Data*. So if you create a Data, and a Proxy that uses it, the GetString/SetString methods of either will get/set the Data's string.
//////////////////////////////////////////////////////// // We don't really need to forward Proxy, but it can't // hurt. If we put this in another file, declaring // everything here would give us a nice overview. class Proxy; class Data; //////////////////////////////////////////////////////// // The Proxy class represents an object which doesn't // have any real data of its own. Instead, it must ask // its private Data pointer to get and set data for it. class Proxy { Data *data; public: Proxy(Data *d); String GetString() const; void SetString(const String &s;); }; //////////////////////////////////////////////////////// // The Data class represents an object which contains // data, but which has no knowledge of other classes. class Data { String string; public: Data(String s); String GetString() const; void SetString(const String &s;); }; //////////////////////////////////////////////////////// // Definition of Proxy methods: Proxy::Proxy(Data *d) : data(d) { } String Proxy::GetString() const { return data->GetString(); } void Proxy::SetString(const String &s;) { data->SetString(s); } //////////////////////////////////////////////////////// // Definition of Data methods: Data::Data(String s) : string(s) { } String Data::GetString() const { return string; } void Data::SetString(const String &s;) { string = s; } //////////////////////////////////////////////////////// int main() { // Create a Data and a Proxy: Data data("data"); Proxy proxy(&data;); // Check that they both print "data" GePrint("data: " + data.GetString()); GePrint("proxy: " + proxy.GetString()); // Set the string through Data and check: data.SetString("Data::SetString"); GePrint("data: " + data.GetString()); GePrint("proxy: " + proxy.GetString()); // Set the string through Proxy and check: proxy.SetString("Proxy::SetString"); GePrint("data: " + data.GetString()); GePrint("proxy: " + proxy.GetString()); }
(and by the way, an OT question for anyone reading this: whenever I post on this forum, the code tags always use a double line height for my code -- how are you guys avoiding that? I've tried using firefox, chrome, and IE, and also win/unix/mac line-endings, so far with no luck.)
-
On 23/02/2013 at 08:19, xxxxxxxx wrote:
^In my experience. This forum can't handle any tabs in your code.
If you want the code to be nice and tight. You'll have to remove any tabs from it and only use spaces.-ScottA
-
On 23/02/2013 at 09:33, xxxxxxxx wrote:
Thanks, but I don't ever use tabs, it's something else. Looking at the html, there actually appear to be three different ways it is generated for different poster's code tags, but as this is OT, I'm going to make a different thread for it.
-
On 23/02/2013 at 11:42, xxxxxxxx wrote:
All people that post from mac have this problem.
-
On 24/02/2013 at 06:58, xxxxxxxx wrote:
Originally posted by xxxxxxxx
WickedP, your second example should not compile (I didn't check the first), so I'm not sure how you know that it crashes.
Ah - I'm an expert at getting these things to happen JD
Thanks again all. I won't bother the forum with this topic again (unless I lose all my hair!). I think you've all done enough for me on this one.
Kind regards,
WP.