1

sing Borland C++ Builder 2009.

In a custom class that is comparable to a String class (for the sake of this Q at least) I have a function, like so:

(edited a bit for this Q)

MyString &MyString::Sprintf(const wchar_t *Text, ...)
{
    wchar_t ResultStr[1000] ;
    va_list List ;
    va_start  (List, Text) ;
    int cnt = vswprintf (ResultStr, Text, List ) ;
    #ifdef _DEBUG
    if (cnt >= (int)sizeof(ResultStr)) {throw (sizeof(ResultStr)) ;}
    #endif
    va_end (List) ;
    my_internally_handled_string.assign(ResultStr) ;
    return *this;
}

I would however like to adjust this function (or make another one) that only takes the variables as input, and uses the class' internally managed string as input Text for the vswprintf() function.

Something like this:

MyString &MyString::Sprintf(...)
{
    const wchar_t *Text = my_internally_handled_string.c_str() ;
    // ... Rest see above

This doesn't work however. vswprintf throws an exception. (tested with a perfectly OK text in my_internally_handled_string)

I wonder if it is at all possible and/or what I need to do to make it work the way I envision it ?

Peter
  • 1,334
  • 12
  • 28
  • `{throw (sizeof(ResultStr))` ?? Also you should be comparing the number of characters, not `sizeof`. – M.M Jan 25 '16 at 05:10
  • It would help to replace "Rest see above" with your actual code. I'm having trouble guessing what you replaced `va_start(List, Text)` with. – M.M Jan 25 '16 at 05:12
  • Make sure you don't try and overwrite the same memory your format string is stored in while you are still reading the format string... for your stated use case you'd need to copy the internal string to a separate buffer before you start overwriting it. – M.M Jan 25 '16 at 05:14
  • Finally, I don't think this is a very good idea in the first place as it will increase the chance of the argument list not matching the format string. – M.M Jan 25 '16 at 05:16
  • @M.M - The rest of the code is identical to the function code above. - I'm sorry but I'm not seeing where I overwrite memory ? - It would be used in controlled circumstances where the object already contains the data in which I need to replace the % parameters. It makes sense to skip the step to pass it again to the function. – Peter Jan 25 '16 at 05:19
  • 1
    If the rest of the code is identical, then `va_start(List, Text)` is an error because `Text` is a local variable. The second argument of `va_start` has to be a parameter in the parameter list. – M.M Jan 25 '16 at 05:22
  • AH ok ! That makes sense. So there is no way around this then ? I wanted to avoid having to put the input `Text` in the parameter list because the object already contains that information. – Peter Jan 25 '16 at 05:35
  • Yeah. I suggest sticking with your original though, in the interests of usability – M.M Jan 25 '16 at 07:39

1 Answers1

2

The variable (or variadic) arguments of va_start() (stdarg.h/cstdarg) are macros inherited from C. The second argument is the last non-variable parameter (in your case const wchar_t *Text). This is needed as a reference point to calculate the starting address of the following variable arguments. If there is no such last parameter before the ... then it has no reference point to find the variable parameters. So you need something to give it, such as a dummy parameter.

You can keep the existing function signature

    MyString &MyString::Sprintf(const wchar_t *Text, ...)

and just not use the parameter Text other than to call va_start(List, Text). Remember that you can pass whatever you want to vswprintf(), such as

    vswprintf(ResultStr, my_internally_handled_string.c_str(), List);

Also, do not shadow the parameter Text with a local variable of the same name, especially before calling va_start(), because it needs the parameter as a starting point.

Another thing worth mentioning about variable arguments in C++ is don't use a reference variable as the last parameter before the ....

Community
  • 1
  • 1
e0k
  • 6,961
  • 2
  • 23
  • 30
  • Thanks for clarifying. I actually already tried something similar (with success) but my aim was to get rid of having to pass anything other than the actual variables, so only use: `(...)`. I see now that that is not possible. – Peter Jan 25 '16 at 05:50