Link to home
Start Free TrialLog in
Avatar of kmustang1
kmustang1

asked on

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?
Avatar of George Tokas
George Tokas
Flag of Greece image

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);
//TRY:
      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
Avatar of kmustang1
kmustang1

ASKER

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.
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!
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...
Since:
"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.
ASKER CERTIFIED SOLUTION
Avatar of kmustang1
kmustang1

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>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"
So:
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.
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.
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.
>> 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.