BaseTag and TagData - typecast?



  • On 01/07/2013 at 14:52, xxxxxxxx wrote:

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

    ---------
    I create my tags, and they descend from TagData.

    When I call this:

    BaseTag* tagFound =	someObject->GetFirstTag();
    

    What I get, is the tag I have created, because it sits on someObject. Or is it?
    It, namely, seems impossible to typecast it so that I have access to its methods, the custom methods I have created myself, in MyTag. I only have access to BaseTag's methods.
    I can get the parameters out, yes, using  tag->GetParameter().
    But I would rather use the built in Methods I wrote for MyTag.

    It seems there is something fundamental here that I do not get, regarding C4D.



  • On 01/07/2013 at 15:25, xxxxxxxx wrote:

    tag->GetNodeData() sounds like what you're looking for.

    -ScottA



  • On 01/07/2013 at 15:41, xxxxxxxx wrote:

    NodeData plugin data is represented by a GeListNode (BaseObject, BaseTag and so on) in c4d. ScottA pointed you to the GeListNode method that does returns the plugin data, which is an instance of the class you did derive from NodeData.

    Plugins itself are also not represented by the plugin class, but by also by a GeListNode (BasePlugin).



  • On 01/07/2013 at 23:33, xxxxxxxx wrote:

    You can call your own methods by just doing so or using the this pointer.



  • On 02/07/2013 at 00:22, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    tag->GetNodeData() sounds like what you're looking for.

    It sounds like that, but I can't get it to work.

    BaseTag* tagFound = someObject->GetNext();
    MySpecialTag* msp = (MySpecialTag)tagFound->GetNodeData();
    

    Line two gives error: C2440: 'type cast' : cannot convert from 'NodeData *' to 'MySpecialTag'

    There are some fundamental aspects of the C4D way of doing things that I still have not understood. I program all day, because that is my profession, but so far C4D and C++ is still a hobby.
    What I am used to, is full object oriented programming. So in my head, tags in C4D sit on their object, and are fully living classes with properties and methods. So when someObject->GetNext() returns a BaseTag, I expect to have access to all the tag's props and methods, in this case stuff I wrote myself.
    I also expect to be able to typecast, and furthermore to check availability by using keywords like is or as.

    What I have to do now, is to write special methods (functions) and call them using the 'tag' as parameter, like this:
    Bool MySpecialTag::DoSomeStuff(BaseTag* tag)
    and these functions can as well be static , since they "do not know of" the tag itself in any case.
    And then I have to use tag->GetParameter() or tag->SetParameter(), whereas I elsewhere (in C#) only have to call a Getter or Setter I wrote myself,  in MySpecialTag.



  • On 02/07/2013 at 00:49, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Originally posted by xxxxxxxx

    tag->GetNodeData() sounds like what you're looking for.

    It sounds like that, but I can't get it to work.

    BaseTag* tagFound = someObject->GetNext();
    MySpecialTag* msp = (MySpecialTag)tagFound->GetNodeData();
    

    Line two gives error: C2440: 'type cast' : cannot convert from 'NodeData *' to 'MySpecialTag'

    There are some fundamental aspects of the C4D way of doing things that I still have not understood. I program all day, because that is my profession, but so far C4D and C++ is still a hobby.
    What I am used to, is full object oriented programming. So in my head, tags in C4D sit on their object, and are fully living classes with properties and methods. So when someObject->GetNext() returns a BaseTag, I expect to have access to all the tag's props and methods, in this case stuff I wrote myself.
    I also expect to be able to typecast, and furthermore to check availability by using keywords like is or as.

    What I have to do now, is to write special methods (functions) and call them using the 'tag' as parameter, like this:
    Bool MySpecialTag::DoSomeStuff(BaseTag* tag)
    and these functions can as well be static , since they "do not know of" the tag itself in any case.
    And then I have to use tag->GetParameter() or tag->SetParameter(), whereas I elsewhere (in C#) only have to call a Getter or Setter I wrote myself,  in MySpecialTag.

    You are missing an asterisk in the cast.

    From what context do you want to call a method on the tag? If you are in a method of the TagData
    itself, you can call it they way you are used to it..

    > class MyTagData : public TagData {
    >
    >
    >
    >
    >     void FooBar() {
    >
    >         // ...
    >
    >     }
    >
    >
    >
    >
    > public:
    >
    >
    >
    >
    >     Bool Execute(/* .. */) {
    >
    >         FooBar();
    >
    >         // ...
    >
    >     }
    >
    >
    >
    >
    > };

    Remember you are in C++, not C#. It's different. Languages like C#, Java, Python, Ruby, COFFEE,
    PHP, JavaScript, they all *know* what type an object is because they store this information in the
    object itself. C++ doesn't do that.

    >  So when someObject->GetNext() returns a BaseTag, I expect to have access to all the tag's props and methods, in this case stuff I wrote myself.

    Get rid of this illusion. It is not this way. I think to understand this, you must dive deep into the
    Cinema API, but to give you the two the number one reasons why NodeData plugins are wrapped
    by the GeListNode class: Abstraction and Memory. The undo-system for example makes use of the
    fact that each GeListNode is just a proxy for a plugin implementation. When an object is moved on
    the undo-stack, it's GeListNode is copied, but it is still connected to the exactly same NodeData
    implementation.

    Btw, it is easier to make use of the Message() system instead of your own boilerplated methods,
    because it is quite a work to invoke them: See the "Stability and Testing" section in the docs, under
    "Virtual Function Calls".

    FYI, you can write wrapper classes (that could also serve as an API to your plugin) by subclassing
    BaseTag. Just make sure you are only using methods, no attributes! If you want to use it
    cross-over plugins (eg. as an API for extensions for your plugin), you even need to save a pointer
    to the methods.

    class MyTag : public BaseTag {
      
        MyTag();
        ~MyTag();
      
    public:
      
        Bool IsEnabled() {
            BaseContainer* data = GetDataInstance();
            if (data) {
                return data->GetBool(MYTAG_ENABLED);
            }
            return FALSE;
        }
      
        const Vector* GetSomeInternalData() {
            MyTagData* data = (MyTagData* ) GetNodeData();
            if (!data) return NULL;
            return data->GetSomeInternalData();
        }
      
        const Vector* AnotherWayOfGettingInternalData() {
            const Vector* ptr = NULL;
            if (!Message(MSG_MY_PRIVATE_MESSAGE_ID_FROM_THE_PLUGINCAFE, &ptr)) {
                return FALSE;
            }
            return ptr;
        }
      
    };
    

    "IsEnabled()" and "AnotherWayOfGettingInternalData()" could be used from another plugin too
    because they are doing safe operations on the virtual method table, unlike the
    "GetSomeInternalData()" method.



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

    Hi Niklas,
    I derive my tags from TagData, not from BaseTag. I used the templates from the SDK. So GetDatainstance() won't work. 
    By the way, Mr. Maxon wants us to use GetParameter and not use the GetDatainstance(), but that is a minor issue in this context.

    ----

    When I call a static method in my tag, using a BaseTag* as a parameter, I can access the various data for my tag. But this method could as well have been implemented in any object, I just store this in my tag, because it somehow belongs there.

    If tags written had been derived from BaseTag, and not TagData, all would have been fine, I believe. But from inside the TagData, there is no way (as far as I know) to access the data, using "this" i.e. the object itself. And the examples in the SDK use TagData as the ancestor. And that is the correct way, I believe?



  • On 02/07/2013 at 05:00, xxxxxxxx wrote:

    I know you do. You can't derive your plugin from BaseTag. I'm working with the Cinema 4D API for
    almost 4 years now bud.

    However, by subclassing BaseTag, you can make common operations on your plugin-tag contained
    in one class, and you can access them by simply making a cast.

    Bool MyTagData::Execute(BaseTag* bTag, /* ... */) {
            MyTag* tag = (MyTag* ) bTag;
            if (!tag->IsEnabled()) return TRUE;

    // ...
        }

    You can safely use GetDataInstance() for your own objects (and also others). I only use
    Get/SetParameter() on parameters that I know of (or have made the experience) that they are not
    stored in the container.

    > >> If tags written had been derived from BaseTag, and not TagData, all would have been fine, I
    believe. But from inside the TagData, there is no way (as far as I know) to access the data, using "this"
    i.e. the object itself. And the examples in the SDK use TagData as the ancestor. And that is the correct
    way, I believe?

    I'm not sure if I got this straight. You can always retrieve a NodeData's (so, also TagData's)
    GeListNode* by calling NodeData::Get().



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

    Originally posted by xxxxxxxx

    You can always retrieve a NodeData's (so, also TagData's)
    GeListNode* by calling NodeData::Get().

    Aha, now I know this, thanks. I wish there was a this->GetListNode() instead. Because this is almost scary.. But I will use it, errors will appear in any case.



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

    Ok, what would you prefer, you experienced guys here

    A

     _// Caller_
    BaseTag* tagFound =	tagOwner->GetFirstTag();
    LONG result = MySpecialTag::GetMySpecialValue(tag) 
      
    // Inside the tag, this can be **static**
    LONG MySpecialTag::GetMySpecialValue(BaseTag* tag)
    {
        GeData t_data;
        tag->GetParameter(SPECIAL_VALUE, t_data, DESCFLAGS_GET_0);
        return t_data.GetLong();
    }
    

    B

     _// Caller_
    BaseTag* tagFound =	tagOwner->GetFirstTag();
    // Type casting
    MySpecialTag* mst = (MySpecialTag* )tagFound->GetNodeData();
    LONG result = mst->GetMySpecialValue(); 
      
    // Inside the tag
    LONG MySpecialTag::GetMySpecialValue()
    {
        GeListNode* node = NodeData::Get();
        GeData t_data;
        node->GetParameter(SPECIAL_VALUE, t_data, DESCFLAGS_GET_0);
        return t_data.GetLong();
    }
    


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

    B



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

    One small point, you are assuming that the first tag returned is your tag, presumably because it's the only visible tag on the object. Bear in mind that there may well be other, invisible tags, one of which could be the first tag.

    So you should always check the type of tag, e.g.:

      
    BaseTag *tagFound = tagOwner->GetFirstTag();   
    while(tagFound != nullptr)   
    {   
         if(tagFound->GetType() == MY_TAGS_PLUGIN_ID)   
         {   
              // do whatever here...   
         }   
         tagFound = tagFound->GetNext();   
    }   
    


  • On 02/07/2013 at 12:10, xxxxxxxx wrote:

    Or.. just look for your specific tag, if that's all you want to do...

      
      BaseTag* tagFound = someObject->GetTag(MY_TAGS_PLUGIN_ID);  
      if( tagFound )  
      {  
          // do whatever with your tag here  
       }  
    


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

    Originally posted by xxxxxxxx

    Or.. just look for your specific tag, if that's all you want to do...

     
      BaseTag* tagFound = someObject->GetTag(MY_TAGS_PLUGIN_ID);  
      if( tagFound )  
      {  
          // do whatever with your tag here  
       }  
    

    Yes, most likely there is just one of them. 
    Anyhow, the question regarding A or B is to what extent I should use a static function passing the BaseTag as a parameter, or use a dynamic function, having typecast the BaseTag to the actual TagData descendant.



  • On 02/07/2013 at 23:42, xxxxxxxx wrote:

    This article might help answering some of the general questions:
    http://c4dprogramming.wordpress.com/2013/03/12/nodedata-what-is-it-good-for/



  • On 03/07/2013 at 19:12, xxxxxxxx wrote:

    Howdy,

    Well, from the looks of your code couldn't you do the same thing like this:

    LONG result = 0;  
    BaseTag *tag = object->GetTag(ID_MY_TAG_PLUGIN);  
    if(tag)  
    {  
      BaseContainer *tData = tag->GetDataInstance();  
      if(tData) result = tData->GetLong(ID_SPECIAL_LONG_VALUE);  
    }  
    

    I mean if the data is stored in the tag's container there's no need to cast a BaseTag to your tag data class.😉

    Adios,
    Cactus Dan


Log in to reply