More UnicodeString to char* trouble

I have a library that uses char* for passing string parameters.  I was trying to extend that interface to also support UnicodeStrings so I don't have to do so many conversions in the application code.

For example, I have a function:
int myClass::SqlFormat(Result* result, const char* statement, ...)
      char buffer[10000];
      va_list arguments;

      va_start(arguments, statement);
      vsprintf(buffer, statement, arguments);
      va_end(arguments);   // Breakpoint A

      return Sql(result, buffer);  // Another function in this same library

So I added the following function:
intmyClass::SqlFormat(Result* result, const String& statement, ...)
      char buffer[10000];
      AnsiString conv = statement;
      va_list arguments;

      va_start(arguments, statement);
      vsprintf(buffer, conv.c_str(), arguments);
      va_end(arguments);  // Breakpoint B

      return Sql(result, buffer);

The first (original) function works just fine.  The second one does not.  When I put a breakpoint on the va_end() statement, buffer doesn't contain my expanded argument(s).  My tests send the same text to both functions:

classinstance.SqlFormat(&myresult, "SELECT * FROM 'unittest' WHERE 'linenumber'=%d", 1234);
String str01 = L"SELECT * FROM 'unittest' WHERE 'linenumber'=%d";
classinstance.SqlFormat(&myresult, str01, 1234);

I put breakpoints after the vsprintf calls, and the value of buffer in each case is:
// breakpoint A:
SELECT * FROM 'unittest' WHERE 'linenumber'=1234

// breakpoint B:
SELECT * FROM 'unittest' WHERE 'linenumber'=

I've tried various ways of packaging the string in the second case, including strcpy'ing it into another buffer, all to no avail.  In no way can I get it to behave.

Oh, and the value of AnsiString.c_str() does have the %d correctly on the end, it's not getting mangled in the conversion or anything.

Any ideas why this isn't working?
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

George TokasCommented:
intmyClass::SqlFormat(Result* result, const String& statement, ...)
      char buffer[10000];
      AnsiString conv = statement;//conv has the statement contents with "SELECT * FROM 'unittest' WHERE 'linenumber'=%d"
      va_list arguments;//DECLARATION OF arguments and so its NULL at this point

      //va_start(arguments, statement);
      va_start(arguments, conv.c_str());
      vsprintf(buffer, conv.c_str(), arguments);
      va_end(arguments);  // Breakpoint B

      return Sql(result, buffer);
Try that and report again here....

George Tokas
kmustang1Author Commented:
This generates the error:

[BCC32 Error] cpNetClasses.cpp(1507): E2027 Must take address of a memory location

I was assuming that the second parameter to va_start() is to define where the list of arguments starts, so passing it a pointer to a local buffer probably won't work.
kmustang1Author Commented:
I think I've got it figured out.  At times, I've had trouble passing STL objects as parameters, and wondered if that reference was somehow messing up the variable arguments (and it wasn't some sort of coding error).

So I put a dummy integer after the String reference and before the varargs list.  On first blush, this seems to be working.  Any idea why?  Is this some sort of quirk with this compiler and object references?  Or is there some legitimate reason why one shouldn't pass an object reference followed by the list of variable arguments?

Thanks so much for your help!
Exploring ASP.NET Core: Fundamentals

Learn to build web apps and services, IoT apps, and mobile backends by covering the fundamentals of ASP.NET Core and  exploring the core foundations for app libraries.

George TokasCommented:
It is not that simple...
For me the idea of va_xxxx formating is wrong when we have to deal with strings - AnsiStrings or whatever...
"SELECT * FROM 'unittest' WHERE 'linenumber'=%d"
is a string and the next value who has to be added is a string too and has to be added then why don't you just play with AnsiStrings and not making it harder than it is....
When you have your final string just put it on the buffer if it is needed (c_str() is a pointer to char anyway)...

George Tokas.
kmustang1Author Commented:
The next value to be added is NOT a string, it's an integer.  The whole point of this entry point is to do all the formatting for the caller to simplify the calling code.  While the string I'm showing here is a simple example, the actual code is generally much more complex.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
George TokasCommented:
>>The next value to be added is NOT a string, it's an integer
YES, but the final buffer content HAS to be a series of characters that was the meaning of my post...
SQL communicates in that way....
AnsiString S = "SELECT * FROM 'unittest' WHERE 'linenumber'="
int a = 1234
Final string for the buffer ::"SELECT * FROM 'unittest' WHERE 'linenumber'=1234"
AnsiString FinalString = S + IntToStr(a);
The buffer has to be filled with the contents of FinalString...
I hope you understand what I mean...
Forgive me because English is not my maternal language and some aspects of handling the meaning in this language are unknown to me...

George Tokas.
George TokasCommented:
I have to ask if my final comment make sense...
I mean DID the author understood what I am proposing??
It is pure C++ Builder way with AnsiStrings...
IF the author thinks that I have no point in my proposal then I don't have ANY objection for this Q to close.

George Tokas.
kmustang1Author Commented:
I'm sorry, I should have closed the loop on this.

I got the answer from a response on the Embarcadero forum.  The problem is due to the va_start macro:

in <StdArg.h>
#define va_start(ap, parmN)
((void)((ap) = (std::va_list)((char*)(&parmN)+__size(parmN))))

By using a string reference, it's adding the size of the string object, not the size of the reference, to obtain the starting point of the optional arguments.  The difference between the size of a pointer, and the size of a String object is substantial, so it's just picking up random memory values.

I didn't respond to gtokas because either I don't understand him, or he's not understanding me.  If I am understanding him correctly, his recommendation doesn't help.  At some point, the formatted string has to be constructed, whether it's in the caller or in my wrapper function.  The *point* of the wrapper function is to eliminate the need for the caller to do it in the first place.  This makes the code much MUCH more readable.

In the end, the method above doesn't work, either.  In the list of arguments there can be (and often are) unicode strings, which are being passed through to the char* version of the function.  So I had to move the string formatting to the wrapper level, and pass that to the char* function to do the rest of the "real" work.
George TokasCommented:
>> So I had to move the string formatting to the wrapper level, and pass that to the char* function to do the rest of the "real" work.
That was the point of my post and my previous answer about the wrapper.
Unfortunatetly I'm Greek and can't exprees myself properly in English...
The point was and still is since you are working with strings and C++ Builder has ways - IMHO - easier to use than plain c++ or VC or whatever, use them when dealing with strings (unicode or whatever) and when ready pass them as char* using .c_str()...
But since you got your answer and NOT from here I don't have any objection for the Q to close...
As you can see I'm not in need for points...:-)
I'm interested on helping finding solutions here...

George Tokas.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Editors IDEs

From novice to tech pro — start learning today.