Making your own classes with the STL



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 27/06/2012 at 08:01, xxxxxxxx wrote:

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

    ---------
    Before I ask my question. I just want to apologize for asking so many questions lately.
    I don't know if I'm being annoying or not. So please tell if I'm posting too much.

    My question is this:
    I'm reading that you guys are often writing your own classes for things instead of relying on the SDK for some things. And I was wondering about how you are doing it.
    Are you guys using the C++ STL? Or are you writing everything using only the built in things that come with the C4D SDK?

    I've briefly attempted to use the STL in my plugins. But I've had problems with it because it's not working the same way as when writing raw C++ outside of C4D projects.

    For example: using namespace std;
    This does not seem to work inside of a C4D project.
    In fact I was having trouble getting any of the STL to work at all inside of a C4D project. But then I discovered that I have to manually type STD:: before everything to make them work.

    So I was just wondering how you guys were handling this?
    Are you using the STL in your C4D projects?
    If you are. Are you typing STD:: in your code?

    -ScottA



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 27/06/2012 at 08:41, xxxxxxxx wrote:

    There are some issues with the Cinema 4D SDK and the C++ STL but they can be worked as far as I know.  When writing your own classes it is more a matter of creating a class structure that implements the algorithm that you are trying to achieve and tying it to the SDK in a relative manner.  For instance, you could write your own classes to support some feature either not supported in Cinema 4D or inadequately supported without even resorting to the STL.  The C++ STL is a great step towards more pure objective language but it is not supported well by the SDK.  I typically use third party solutions which do not rely upon the STL so that I can implement them in the SDK context.  One of the problems of the STL is that it relies upon C++ exception handling which the SDK does not support.  Very frustrating since this will ultimately end in unhandled exception processing at the main level.  Best to implement your own pseudo exception handling or just use error handling to avoid the limitations.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 27/06/2012 at 09:33, xxxxxxxx wrote:

    Thanks Robert.
    I didn't know about the exception handling.
    I would still like to try out using the STL though. Just to get an idea what is/isn't possible with it inside a C4D project. But I'm hitting the wall with newbish stuff like casting.

    Example:

    #include <string>  
    ....  
    std::string mystring = "Hello World";  //A string created with the string namespace in the STL  
    GePrint(mystring);                     //<--Nope!! You can't do that because C4D uses it's own "String" class
    

    Obviously I have to convert the STL string type to a C4D string type in order for me to use GePrint() on it. But I can't figure out how to do that.

    As a beginner I feel like I'm living in two separate worlds:
    -Raw C++ where the STL is used heavily
    -C4D where the STL is basically never used.

    I was able to lean the STL method from the internet and Books.
    I was able to learn the C4D SDK method by reading this forum.
    But I'm having a devil of time trying to figure out how to use the STL inside of a C4D project.

    -ScottA



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 27/06/2012 at 23:38, xxxxxxxx wrote:

    You can't print directly a std::string but you can get its C-string calling c_str() :

    GePrint(mystring.c_str());
    


  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 28/06/2012 at 00:54, xxxxxxxx wrote:

    Hi Scott,

    Getting back to your original question (if I understand it correctly), creating your own classes (having nothing to do with STL) is fairly straight-forward...

    I recently needed to add some 'hash table' functionality to my plugins (optimized look-up capability for arrays/lists of items).  I found one that is really easy to implement and seems to work quite well - http://uthash.sourceforge.net/

    Basically, you just need to add a element/member to your class structure (used as a hash handle) and then include one header file.  You can then use some macros from the header file to add/delete entries and iterate over the entries, etc.

    I wanted to isolate these macros from the rest of my code - to condense them to a few files/implementations, making the actual implementation transparent to the rest of the code, so I created some classes to implement the hashing for various purposes.

    One of the basic/root-level classes I created was a "unique name hash", which looks like this:

      
    class nameNode  
    {  
    public:  
      char    *m_pName;  
      size_t   m_nameLen;  
      ULONG    m_ID;  
      UT_hash_handle hh;                            // makes this structure hashable  
      
      ULONG   GetID(void)                           { return m_ID; }  
      void    SetID(ULONG ID)                       { m_ID = ID; }  
      char    *GetName(void)                        { return m_pName; }  
      Bool    SetName(const char *szName);  
    //  void    SetName(const String &strName);  
      
      nameNode(void);  
      ~nameNode(void);  
    };  
      
    class nameHash  
    {  
    private:  
      nameNode    *m_pHashTable;  
      ULONG        m_count;  
      
      nameHash(void);  
      ~nameHash(void);  
      
    public:  
      ULONG       GetCount(void)                    { return m_count; }  
      Bool        IsCloneName(const char *szName);  
      nameNode    *AddName(const char *szName);  
      nameNode    *AddName(const String &strName);  
      nameNode    *FindName(const char *szName);  
      nameNode    *FindName(const String &strName);  
      void        Flush(void);  
      
      static nameHash    *Alloc(void);  
      static void        Free(nameHash *&pNameHash);  
    };  
    

    ...the nameNode class only has a few methods not implemented in the declaration above...

      
    // nameNode::nameNode() - Constructor  
    nameNode::nameNode(void)  
    {  
      m_pName = NULL;  
      m_nameLen = 0;  
      m_ID = 0;  
    }  
      
    // nameNode::~nameNode() - Destructor  
    nameNode::~nameNode(void)  
    {  
      if( m_pName )    GeFree(m_pName);  
    }  
      
    // nameNode::SetName()  
    Bool nameNode::SetName(const char *szName)  
    {  
      if( m_pName )    GeFree(m_pName);  
      
      m_nameLen = strlen(szName);  
      m_pName = (char * )GeAlloc(m_nameLen+2);  
      if( !m_pName )  
      {  
          m_nameLen = 0;  
          return false;  
      }  
      strcpy(m_pName, szName);  
      
      return true;  
    }  
    

    ...then the nameHash class is the actual list/container that the rest of my code mostly deals with to access the nameNode(s)...

      
    // nameHash::nameHash() - Constructor  
    nameHash::nameHash(void)  
    {  
      m_pHashTable = NULL;  
      m_count = 0;  
    }  
      
    // nameHash::~nameHash() - Destructor  
    nameHash::~nameHash(void)  
    {  
      this->Flush();  
    }  
      
    // nameHash::Flush() - free up the list  
    void nameHash::Flush(void)  
    {  
      nameNode *pName, *pTmp;  
      HASH_ITER(hh, m_pHashTable, pName, pTmp)  
      {  
        HASH_DEL(m_pHashTable, pName);  
        gDelete(pName);  
      }  
      m_pHashTable = NULL;  
      m_count = 0;  
    }  
      
    // nameHash::Alloc()  
    nameHash *nameHash::Alloc(void)  
    {  
      nameHash *pNameHash = gNew nameHash();  
      if( !pNameHash )    return NULL;  
      return pNameHash;  
    }  
      
    // nameHash::Free()  
    void nameHash::Free(nameHash *&pNameHash)  
    {  
      if( !pNameHash )    return;  
      
      gDelete(pNameHash); //  <-- calls the Destructor, which in turn calls Flush()  
    }  
      
    // nameHash::IsCloneName() - Similar to AddName(), but return value is whether or not  
    //                           the name already existed instead of the nameNode pointer  
    Bool nameHash::IsCloneName(const char *szName)  
    {  
      nameNode *pNode;  
      
      HASH_FIND_STR( m_pHashTable, szName, pNode);  
      if( pNode )    return true;  
      
      pNode = gNew nameNode();  
      if( !pNode )    return false;  
      
      pNode->SetName(szName);  
      pNode->SetID(m_count++);  
      
      HASH_ADD_KEYPTR( hh, m_pHashTable, pNode->m_pName, pNode->m_nameLen, pNode );  
      
      return false;  
    }  
      
    // nameHash::AddName() - char * version  
    nameNode *nameHash::AddName(const char *szName)  
    {  
      nameNode *pNode;  
      
      HASH_FIND_STR( m_pHashTable, szName, pNode);  
      if( pNode )    return pNode;  
      
      pNode = gNew nameNode();  
      if( !pNode )    return NULL;  
      
      pNode->SetName(szName);  
      pNode->SetID(m_count++);  
      
      HASH_ADD_KEYPTR( hh, m_pHashTable, pNode->m_pName, pNode->m_nameLen, pNode );  
      return pNode;  
    }  
      
    // nameHash::AddName() - Cinema 4D String() version  
    nameNode *nameHash::AddName(const String &strName)  
    {  
      char chkName[MAX_MAPNAME_LEN+1];  
      
      cmpGetCString8(strName, chkName, MAX_MAPNAME_LEN);  
      chkName[MAX_MAPNAME_LEN] = 0;  
      
      return this->AddName(chkName);  
    }  
      
    // nameHash::FindName() - char * version  
    nameNode *nameHash::FindName(const char *chkName)  
    {  
      nameNode *pNode;  
      
      HASH_FIND_STR( m_pHashTable, chkName, pNode);  
      return pNode;  
      
    }  
      
    // nameHash::FindName() - Cinema 4D String() version  
    nameNode *nameHash::FindName(const String &strName)  
    {  
      char chkName[MAX_MAPNAME_LEN+1];  
      cmpGetCString8(strName, chkName, MAX_MAPNAME_LEN);  
      chkName[MAX_MAPNAME_LEN] = 0;  
      
      return this->FindName(chkName);  
    }  
    

    ...so the nameHash class is used for various purposes in my code, but I also have some additional classes that derive from the basic nameNode, such as...

      
    class tagNode    :    public nameNode  
    {  
      friend class tagHash;  
    private:  
      BaseTag    *m_pTag;  
      
      tagNode(void)                     { m_pTag = NULL; }  
      ~tagNode(void)                    { }  
    public:  
      void        SetTag(BaseTag *pTag) { m_pTag = pTag; }  
      BaseTag    *GetTag(void)          { return m_pTag; }  
    };  
      
    class tagHash  
    {  
    private:  
      tagNode    *m_pHashTable;  
      ULONG      m_count;  
      
      tagHash(void);  
      ~tagHash(void);  
    public:  
      ULONG       GetCount(void)        { return m_count; }  
      tagNode    *AddTag(const char *szName, BaseTag *pTag);  
      tagNode    *AddTag(const String &strName, BaseTag *pTag);  
      tagNode    *FindTag(const char *szName);  
      tagNode    *FindTag(const String &strName);  
      void        Flush(void);  
      
      static tagHash  *Alloc(void);  
      static void      Free(tagHash *&pTagHash);  
    };  
    

    ...the tagNode is derived from the nameNode, so it still has the SetName()/GetName()/GetID()/etc methods, but also adds a new member and method to store/retrieve a BaseTag pointer.

    The tagHash class is used in a similar manor to the nameHash class (but to track tagNode(s)), but it's not derived from it.

    Also note that both xxHash classes have methods for both C "char *" names as well as Cinema 4D String() names, so I just call them with whatever is handy.

    I won't bother to post the code for the tagHash class - I'll leave that as an exercise for the user :).

    I have dozens (hundreds?) of other classes that (for example) convert to/from .obj file mesh structure to Cinema 4D PolygonObject structure (I have my own internal ioMesh, ioGroup, ioMaterial, ioRegion and many other classes/sub-classes used for the conversion process).

    I also use that tagHash class to gather/track lists of Texture Tags, Material Tags, Polygon Selection tags (used for Texture Tag restrictions), Polygon Selection tags (used for Group definitions), Polygon Selection tags (used for Region definitions), etc.

    I hope this was helpful - Cheers.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 28/06/2012 at 01:38, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    [...] But I can't figure out how to do that.As a beginner I feel like I'm living in two separate worlds:-Raw C++ where the STL is used heavily-C4D where the STL is basically never used.I was able to lean the STL method from the internet and Books.I was able to learn the C4D SDK method by reading this forum.But I'm having a devil of time trying to figure out how to use the STL inside of a C4D project.-ScottA

    It should be pretty obvious: Unless you're forced (by a 3rd party lib) to use STL in a Cinema plugin, you should avoid STL.

    STL is exception based (which makes in crash-prone in the Cinema context, unless you take extra care for handling these exceptions), isn't unicode-based, doesn't use our fast memory system, circumvents debug utilities like our memory leak detection, etc., ...

    Best regards,

    Wilfried



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 28/06/2012 at 07:41, xxxxxxxx wrote:

    Thanks for the replies guys.

    Since I'm making Windows only plugins. I was toying with idea of maybe using the STL very sparingly when the SDK doesn't have something I needed. And thought it could possibly bail me out when I get stuck.
    I think I've read that other people have done that here. So I was curious about how to do it.
    But I couldn't figure out how to get around the types and casting issues to make them work together like the print code Yannick posted. Thank you Yannick.

    Nice example Giblet.
    Thanks for posting it.

    -ScottA


Log in to reply