How to define a String with one character



  • On 16/07/2013 at 13:00, xxxxxxxx wrote:

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

    ---------
    Hi,
    I use this in my code:

            String foo = "" + (char)8;
    

    So as you see, I want a String with one character, and this character is not on my keyboard, so I do it this way. And it works just fine.
    However, I use this String in several methods, and it has to be the same all over, so I would like to define it in global space somewhere. How do I do that?

       const LONG FOO_LONG = 12345;
       const String FOO_STRING = "" + (char)8;
    

    This compiles with no errors, but is doesn't work at runtime. Well, FOO_LONG works ok, but not FOO_STRING.



  • On 16/07/2013 at 14:18, xxxxxxxx wrote:

    Uhm a string with one character, eg. "c" is created as

    const String str = "c";
    const String str("c");
    const String str = String("c");
    

    If you want that character to be of the value 8, you can use octal or hexadecimal notation

    const String str("\10"); // octal
    const String str("\x08"); // hexadecimal
    

    If you are talking about a string with one character, meaning no null terminator, just declare
    a char:

    char x = 8;
    


  • On 16/07/2013 at 16:32, xxxxxxxx wrote:

    Hi Niklas,
    it won't work for some reason I do not understand.
    Look here, when just declaring the string (not using it at all) the plugins won't load.
    A

    #include "ge_autoptr.h"
    #include <vector>
    using namespace std; 
    const LONG DA_DEGREES = 1000001;
    //const String foo("\x08");
    

    My plugins load fine.

    B

    #include "ge_autoptr.h"
    #include <vector>
    using namespace std; 
    const LONG DA_DEGREES = 1000001;
    const String foo("\x08");
    

    When starting C4D I get this:

    So by just adding the string constant like that, something goes wrong. I get no compiler errors, but the plugins won't load.
    It is not  a show-stopper, because I can declare the strings in each method, but I would like to have a string declared globally, and also understand why this won't work.



  • On 16/07/2013 at 22:11, xxxxxxxx wrote:

    Do not declare the String globally. The String class is based on the callbacks passed from Cinema 4D
    when the DLL is loaded, but since these are not available at the time the String is constructed, the
    loading fails. You can either use a native C string or declare a pointer to the String

    const String* foo = NULL;
    // ...
    Bool RegisterMyPlugin() {
        if (!foo) {
            foo = gNew String("\x08");
            if (!foo) return FALSE;
        }
        // ...
    }
      
    void FreeMyPlugin() {
        if (foo) {
            gDelete(foo);
            foo = NULL;
        }
    }
    
    const char* foo = "\x08";
    

    -Nik



  • On 17/07/2013 at 03:37, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Do not declare the String globally.

    There has to be a way to do it!!!!!!
    I can declare a LONG globally, and there MUST be a way to declare a string too, at least a string containing one single character.
    I tried the suggestions you submitted, and I am thankful for that! Unfortunately I got a ton of compiler error messages, and unfortunately I do not have the time to study C++ at this level right now. Maybe using the term "String" leads to the confusion, I do not even know what a "native C string" is.

    Here is what I want to do

    //I want to define foo = "\x08" but do not know how to do it
    
    //Then I want to use it in **several** classes, in **multiple** methods
    #include "foo_def.h"
    class A
    {
    	String MethodX()
    	{
    		return "Have a " + foo + " nice day!";
    	}
    	String MethodY()
    	{
    		return "Hello " + foo + " World !";
    	}
    };
      
    class B
    {
    	String MethodZ()
    	{
    		return "Something " + foo + " wrong!";
    	}
    	String MethodW()
    	{
    		return "All is " + foo + " fine!";
    	}
    };
    

    There will be several "foo" strings. I need to declare them globally because they represent delimiters in a system I am building up, and I want to be able to change this one-character string in just one place. In other words, I want to place "foo" in a header file, and link in this file whenever needed.
    This is both for convenience, but also for security, to avoid tying errors during development etc.



  • On 17/07/2013 at 03:55, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    There has to be a way to do it!!!!!!
    I can declare a LONG globally, and there MUST be a way to declare a string too, at least a string containing one single character.

    There simply isn't. The constructors of the String class are:

    	String(void)
    	{
    		C4DOS.St->Init(this);
    	}
      
    	String(const String& cs)
    	{
    		C4DOS.St->Init(this);
    		C4DOS.St->CopyTo(&cs, this);
    	}
      
    	String(const UInt16* s)
    	{
    		C4DOS.St->Init(this);
    		StCall(SetUcBlock) (s, -1);
    	}
      
    	String(const Char* cstr, STRINGENCODING type = STRINGENCODING_XBIT)
    	{
    		C4DOS.St->Init(this);
    		C4DOS.St->InitCString(this, cstr, -1, type);
    	}
      
    	String(Int count, UInt16 fillch)
    	{
    		C4DOS.St->Init(this);
    		C4DOS.St->InitArray(this, Int32(count), fillch);
    	}
    

    C4DOS is a table of function pointers that are passed from Cinema 4D to the plugin on loading.
    But the String is constructed before the C4DOS function table is passed to the plugin. And no, you
    can not tell the string to construct at a later time without using a pointer and explicitly create an
    object on the heap.

    Originally posted by xxxxxxxx

    I tried the suggestions you submitted, and I am thankful for that! Unfortunately I got a ton of compiler error messages

    If you were using the first method I showed you, a pointer to a String, you have to dereference it
    in the concatenation:

    const String* foo = NULL; // Initialized at a later time
    String MethodX() {
        return "Have a " + *foo + " nice day!";
    }
    

    If you were using the second method, you have to convert it to a C4D String first. A native C string
    is just an array of characters ending with a 0 character (so called null-termination). A char is not a
    class, it is a POD (Plain old data) type. There are no methods, you can only use its raw value. You
    can not add up to pointers, that is why you get a compiler error.

    const char* foo = NULL; // Initialized at a later time
    String MethodX() {
        //      char*    \+ char* + char*     -> INVALID
        return "Have a " + foo   \+ " nice day!";
    }
    
    const char* foo = NULL; // Initialized at a later time
    String MethodX() {
        //     char*     \+ String      \+ char*   -> CORRECT
        return "Have a " + String(foo) + " nice day!";
    }
    

    The reason this works is the "operator overloading".

    Which one you chose depends on your needs. My suggestion is to use a class when you need
    a fixed number of strings and more than one. This makes memory management easier.

    class StringTable {
    public:
        String foo;
        String bar;
        String bat;
        String zed;
      
        StringTable() {
            foo = "Peter";
            bar = "William";
            bat = "Daisey";
            zed = "Pinocchio";
        }
      
    };
      
    const StringTable* strs = NULL;
      
    String MethodX() {
        // Either check if `strs` is NULL or make sure this method is never called
        // when the globals string table didn't allocate.
        return "Hello " + strs->zed;
    }
      
    Bool RegisterMyPlugin(); {
        if (!strs) {
            strs = gNew StringTable;
            if (!strs) {
                return FALSE; // memory error
            }
        }
      
        // ...
    }
      
    void FreeMyPlugin() {
        if (strs) {
            gDelete(strs);  
            strs = NULL;
        }
    }
    

    Edit: Corrected StringTable class (added public qualifier)



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

    Thanks! 👏
    A GREAT help!
    What I am tempted to do is this:

    StringTable stringTable;
    MessageDialog("Hello " + stringTable.foo);
    

    It works indeed, I have tested it.
    At this stage, I am mostly occupied with making my plugins work, memory leaks is something I will test and eventually deal with later. Will this lead to memory leaks? I think the solution is just beautiful.



  • On 25/07/2013 at 07:30, xxxxxxxx wrote:

    Howdy,

    Just out of curiosity, isn't \x08 the backspace key?:
    http://www.asciitable.com/

    I don't think that is a printable character, is it?

    Anyway, another easy way around the issue of a global string would be using a global function:

    String MySpecialCharacter(void)  
    {  
      String str = "B";  
      return str;  
    }
    

    The "str" variable is local in scope to the MySpecialCharacter() function, but returns the string's value to the calling function, so there is no memory leak.

    Adios,
    Cactus Dan


Log in to reply