PDA

View Full Version : Transform a QVector / QStringList in a va_list



fanoI
18th April 2013, 20:32
Hy to all I have a GUI application that at work we use to test the creations of Qt Widget itself, for example I have a dialog to create a QMessageBox with favourited properties icon, title, message and button of Qt or custom (you write the text on a list).

All this data are send using a json and the buttons are memorized in QStringList, I have a C function that creates the json, that normally it would called in C but now I want to call in C++, but how convert the QStringList in the “…” (va_list de facto?).

This is the prototype of the function that I want use:



void messagebox(json_object **json, char *iconType, char *title, char *msg, int showTime, ...
/* char *btn1, char *btn2, ... */ );


I cannot change the signature of the function to use a QStringList as it have to be used by a plain C application…

I have found a solution that in Windows XP works, but, obviously is not really portable:



void foo(int n, va_list args)
{
int i = 0;

printf("[foo] n is %d and i is %d\n", n, i);

for (; i != n; ++i)
printf("'%s'\n", va_arg(args, char *));

printf("I'm here... safe!\n");
}

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

QStringList args = a.arguments();

QVector<char *> v;

// It works but I need to allocate memory as QString::toAscii().data() is not saved in 'v' I don' know why!
foreach (QString a, args) {
//char str[strlen(a.toAscii().data()) + 1];
char *str = strdup(a.toAscii().data());

//strcpy(str, a.toAscii().data());
printf("adding to v: '%s'\n", a.toAscii().data());
v << str;
}

foo(args.count(), reinterpret_cast<va_list>((v.data())));

// Now I need to free() str in v or not? If it was a real function and not main() I has a memory leak here or not?
return 0;
}


As you could see it is a Kludge it incredibly works as, at least in Windows and Linux/X86, va_list is, in reality a char *. So I pass all the strings one after another to the “foo” function…

At least I’d like to use a vector of QString to avoid the allocation of memory but I cannot find a way, data() in this case would be what a QString? Or QString *? Or a QString[]? The toAscii().data() seems to not create a valid QByteArray...

By the way if my PM see that code probably he will kill me so I’m asking to you there’s a more portable solution in Qt to this problem?

For example supposing that could be happy with max 32 buttons MOC could automate the scripture of code as this:



QStringList buttons;

switch(buttons) {
case 0: // We have no buttons so I could call messagebox() w/o the last arg
messagebox(json_object **json, char *iconType, char *title, char *msg, int showTime);
break;

case 1: // We have only one buttons simple is the first element of the list...
messagebox(json_object **json, char *iconType, char *title, char *msg, int showTime, buttons[0]);
break;

case 2: /* 2 buttons they're in index 0 and 1 */
messagebox(json_object **json, char *iconType, char *title, char *msg, int showTime, buttons[0], buttons[1]);
break;
.... /* 3-4-5-6-7-... buttons */
case 31:
messagebox(json_object **json, char *iconType, char *title, char *msg, int showTime, buttons[0], buttons[1], buttons[2], buttons[3], buttons[4], buttons[5], [...], buttons[31]);
break;
}


What do you think? Is this possible?

Thanks for your attention…

P.S. For the de-allocation of the char * I've resolved calling QdeleteAll() on the QVector and then his method clean()... I've seen another useful class call QScopedPointer but the compiler doesn't liked the declaration of QVector < QScopedPointer > sadly :crying:

ChrisW67
18th April 2013, 23:52
Anything you do with va_list arguments is going to be a kludge and prone to breakage.

If you cannot alter the interface of the existing function why not add another with a sane (less insane) interface:


void messagebox(json_object **json, char *iconType, char *title, char *msg, int showTime, char **buttonNames);

To avoid repeating yourself you can change the implementation of the the va_list version to build a suitable list of strings for the new implementation. Your legacy C code keeps calling the va_list version and your new code uses the new interface.

Incidentally, how does the existing C code call this function? Surely it faces exactly the same problem.

fanoI
19th April 2013, 23:08
Anything you do with va_list arguments is going to be a kludge and prone to breakage.


Sure the "solution" is a Kludge, yes, but strangely it seems partially portable the same code worked on Windows XP and Linux w/o problems... I cannot garantue it could work in other platform as Mac OSX or totally different CPU as ARM, Power PC...



If you cannot alter the interface of the existing function why not add another with a sane (less insane) interface:


void messagebox(json_object **json, char *iconType, char *title, char *msg, int showTime, char **buttonNames);



The function is indeed safe it seems to don't have a way to found when args end but in reality it has; it is declared as an always inline function using the GCC extensions __builtin_va_arg_pack() and more importantly __builtin_va_arg_len().

The real function (that no one should call it directly!) is this:



void messagebox_aux(json_object **json, char *iconType, char *title, char *msg, int showTime, int n_buttons, ...
/* char *btn1, char *btn2, ... */ );


where n_buttons are the number of va_args calculated using the macro __builtin_va_arg_len().
So I obtain a partial overloaded function in C as I could call it as:



messagebox(&json, "alert", title, msg, showTime); // This an alert with it has no buttons


that translates to:


messagebox_aux(&json, "alert", title, msg, showTime, 0); // no args and n_buttons is 0!


or as:



messagebox(&json, "alert", title, msg, showTime, "OK"); // This an alert with only a button called "OK"


that translates to:


messagebox_aux(&json, "alert", title, msg, showTime, 1, "OK"); // an arg so n_buttons is 1 and is called "OK"


Your version could solve the problem in Qt maybe but I need to create the array before calling the function, right?
I think I could not do this:



messagebox(&json, "alert", title, msg, showTime, {"OK", "Discard", "Cancel", "..."});


But I would have to do this:


char *arr[] = {"OK", "Discard", "Cancel", "..."};
messagebox(&json, "alert", title, msg, showTime, arr);


... and I cannot call it w/o the last arg, right? It wouldn't compile...

In any case in C I'm pretty sure cannot declare an array in a function declaration, right?



To avoid repeating yourself you can change the implementation of the the va_list version to build a suitable list of strings for the new implementation. Your legacy C code keeps calling the va_list version and your new code uses the new interface.


In the end yes I was forced to create a version that takes a va_list as this Kludge creates a va_list from a QVector not a "..." and it seems they have "forgotten" a method to pass from a va_list to a "..." there's only the other way around... so in the end the list of calls is this:



always inline void messagebox(...);
always inline void message_box_aux(...); // creates the va_list using the va_start() macro
vmessagebox(json_object **json, char *iconType, char *title, char *msg, int showTime, int n_buttons, va_list buttons)




Incidentally, how does the existing C code call this function? Surely it faces exactly the same problem.


The C code calls the first version and Qt calls the last one after the QVector -> va_list ugly hack... by the way I seem to have found a bug in the __bultin_va_arg_pack() thing as, for convenience, I have created other proxy functions to create the various types of MessageBox and they are always inline and use __bultin_va_arg_pack() in any call until they arrive to vmessagebox but n_buttons is always 0... when args are presents in the first function call, too! It seems to not work well if called too much times...

I fear I'm forced to create MACROS but they are really unsafe as don't check the args types...

Oh well if only I could use Qt / C++ for all my applications... plain C in 2013 is so limiting really...

If I had to this in Qt/C++? I could create 3 methods using overloading:



void messagebox(json_object **json, char *iconType, char *title, char *msg, int showTime);
void messagebox(json_object **json, char *iconType, char *title, char *msg, int showTime, QString button);
void messagebox(json_object **json, char *iconType, char *title, char *msg, int showTime, QStringList button);


all would be more easy, intuitive, without hacks or unportable GCC extensions... but my PM say "C++ is ugly and slow (?), our society write ANSI C... K&R and better if C90 compliant"!

So annoying :crying:

amleto
20th April 2013, 12:52
lol, second time in two days I see a C lover call C++ ugly! C++ is much prettier than C imo. va_list is inherently unsafe as there is no type checking, so you should not worry about the same issue in your macros. Generally this is a problem throughout C! Your PM sounds like a douche. If you are using Qt, then there is already a C++ 'bottleneck', so why stick to C?

The best way is to expose


void messagebox(json_object **json, char *iconType, char *title, char *msg, int showTime, va_list ap)

so that you can make your own va_list and pass into this method. Without this available, you only have hack solutions, I think.

fanoI
20th April 2013, 17:48
lol, second time in two days I see a C lover call C++ ugly! C++ is much prettier than C imo. va_list is inherently unsafe as there is no type checking, so you should not worry about the same issue in your macros. Generally this is a problem throughout C! Your PM sounds like a douche. If you are using Qt, then there is already a C++ 'bottleneck', so why stick to C?


I think in the same way C++ is better than C overloading, inheritance, GUI toolkits (as our friend Qt), event driven programming... the only thing that C++ did wrong is, IMHO, that could be, for someone, one of its point of force the C compatibility why:


All the pointers things are unsafe!
The C compatibly is not complete, too... an essential C keyword called NULL is not present (it calls it "0"), and an empty variable doesn't evaluates to false
Code that C compiles perfectly or giving only a warning doesn't compile in C++ !


We maybe you could hate me but I, personally, prefer the Java approach all is object (well not completely to be honest int is primitive variable Int is the object), no pointer and no compatibility with the old, ugly (it is that beast that is ugly not Qt/C++) and unsafe C.

By the way yes va_args are unsafe __builtin_va_arg_pack() tries to mitigate the problem as at least permits to check the size of the args without the need to use a variable or a NULL in the end but yes if I pass an int instead of char * the code would compile and, probably will crash when it will try to convert to a char *!

In the end if I don't have done nothing wrong and there's a bug in __builtin_va_arg_pack() when called by a function that calls again __builtin_va_arg_pack() I go for a hibryd approach: I'll implement the info(), warning(), alert(), wait() as variadic macro that will call the messagebox function... but I hate the macro syntax in particular when I have to write functions like macros :(



The best way is to expose


void messagebox(json_object **json, char *iconType, char *title, char *msg, int showTime, va_list ap)

so that you can make your own va_list and pass into this method. Without this available, you only have hack solutions, I think.


By the way the hack is in test code so not problem but the curiosity remains is possible to use the Meta Object Compiler to create your own code?
I could use him to make a switch for example? Maybe in the future I could find a good usage of this technique...

amleto
20th April 2013, 18:37
what would be the point in using moc to generate code? your problem is still a run-time problem, so moc can't help you there...

fanoI
20th April 2013, 18:48
See my fist post I wanted MOC to help me to write at least the first 32 version of the switch()... is this possible?

It is in general possible to make MOC create custom code?

amleto
20th April 2013, 19:19
no, I don't think so. Just write it by hand - it's going to look ugly whether you do it or moc does it (if it could).