• 15
• 15
• 11
• 9
• 10

# trouble with va_list, especially va_arg

This topic is 4190 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hello there! I´m facing a weird problem: I´m trying to implement a function with a variable argument list, but va_arg always returns 0, which makes it quite unusable. Here´s the code:
bool LUAInterpreter::RunEntityFunction(const char* funcName, Entity* e, const char* sig, ...)
{
va_list pArgs;
va_start(pArgs, sig);
int i = 0;
while(sig != '\0')
{
if(sig == 'f')
{
float arg = va_arg(pArgs,float);
lua_pushnumber(this->m_pLUAState, arg);

}
if(sig == 'i')
{
lua_pushnumber(this->m_pLUAState, va_arg(pArgs, int));
}
if(sig == 's')
{
lua_pushstring(this->m_pLUAState, va_arg(pArgs, const char*));
}
i++;
}
va_end(pArgs);
...


I´d rather use std::string instead of const char* but I´ve read that va_list can´t be used with std::string, so I changed to const char* just in case that was causing the problem I have. But it wasn´t. So I´m left clueless. When I call the function like this: RunEntityFunction("func", (*i), "fff", 0.f, 35.f, 238.f) all I get from va_arg is 0.000 for every argument most of the time, or some random number, but not the ones I passed in. Help anyone?

##### Share on other sites
FYI
u can use std::string::c_str() to get the pointer to the char buffer from a std::string.
I think there is also a std::string::data() something that lets you do similar stuff.

##### Share on other sites
RunEntityFunction("func", (*i), "%f%f%f", 0.f, 35.f, 238.f)

Also, make sure you're including stdarg.h

##### Share on other sites
"... " is evil in C++, reconsider the function.

##### Share on other sites
Quote:
 RunEntityFunction("func", (*i), "%f%f%f", 0.f, 35.f, 238.f)

That doesn't solve the problem at all. The parsing of "sig" string (or the "format" string for printf, et al) is up to the function itself; the va_* mechanism doesn't know, care, or provide functionality for it. "fff" is perfectly acceptable input for the string ("%f%f%f" will produce the same behavior, but the '%' tokens will be ignored).

'sig' is only passed to va_start to indicate where the last parameter on the stack is. That's all.

Using '...' is evil. The same thing can be accomplished in a type safe fashion using overloading or templates.

However.
Is that exactly how you are calling the function (if not, show me, because I'm speculating here; your va_* code looks correct)? Specifying a float but providing a double constant (e.g., no after the number) may result in mismatching of addresses due to varying sizes, thus producing garbage. Examine the bug using integers (0xAAAAAAAA, 0xBBBBBBBB, et cetera) and look at the results. If you are misaligning somewhere, you'll be able to tell because you'll get, say, 0xAABBBBBB back). You could also examine the stack and step into the va_* assembly (since va_* is generally a macro) if you are comfortable with that kind of thing.

##### Share on other sites
Quote:
 Original post by jpetrieUsing '...' is evil. The same thing can be accomplished in a type safe fashion using overloading or templates.

Quoted for emphasis. I will spell it out for you, even:

class LUAInvoker {  std::string funcName;  LUAInterpreter* context;  Entity* e;  public:  LUAInvoker(const std::string& funcName, LUAInterpreter* context, Entity* e) :    funcName(funcName), context(context), e(e) {}  template <typename T>  void operator()(T x) {    context->Push(x);  }  bool operator()() {    // placeholder for whatever was in the rest of your function before :)    return context->doSomethingWith(e, funcName);  }};void LUAInterpreter::EntityFunction(const std::string& funcName, Entity* e) {  return LUAInvoker(funcName, this, e);}void LUAInterpreter::Push(int x) {  lua_pushnumber(m_pLUAState, x);}void LUAInterpreter::Push(float x) {  lua_pushnumber(m_pLUAState, x);}// This assumes you really want to feed a const char* to lua_pushstring...void LUAInterpreter::Push(const char* x) {  lua_pushstring(m_pLUAState, x);}void LUAInterpreter::Push(const std::string& x) {  lua_pushstring(m_pLUAState, x.c_str());}// Replace:// RunEntityFunction("func", (*i), "fff", 0.f, 35.f, 238.f)// With:// EntityFunction("func", (*i))(0.f)(35.f)(238.f)()

##### Share on other sites
whoa, so many replies :) Thx to all for your help and tips.
@Zahlman:
Could you explain what that code does a bit more? I have trouble understanding it, especially what will happen if I call
EntityFunction("func", (*i))(0.f)(35.f)(238.f)()
and how that call will be processed.
A link to a tutorial or article or anything explaining it will be fine, too.
I don´t like copying code I don´t understand, so I´m asking that.

Thx :)

I think I get it now:
   EntityFunction("func", (*i))(0.f)(35.f)(238.f)()-> LUAInvoker("func", LUAInterpreter, (*i))(0.f)(35.f)(238.f)()

after that LUAInvoker processes the (...) operators by pushing the supplied values and finally the () operator by calling the function, right?
At least that makes sense to me now, if there´s anything to note about the behaviour I forgot, please tell me :)
[/edit]

[Edited by - matches81 on October 2, 2006 9:58:32 AM]

##### Share on other sites
The idea is as follows:
EntityFunction() constructs and returns a "proxy object" of sorts that has a "chainable" operator(). By "chainable," I mean in the sense that operator<< for streams is chainable, because it returns a reference to the invoking object (I think Zahlman made a few typos in his example; EntityFunction() should return LUAInvoker, not void, and LUAInvoker::operator() that takes a T should have a return type of LUAInvoker& and "return (*this)").

So yes, what happens is that EntityFunction() returns a LUAInvoker temporary, upon which you call successive operator()'s with the parameters. As you've surmised, those parameters are pushed onto a stack and the stack is dispatched by the parameterless operator().

You could replace the operator() overloads with operator<< if it helps you visualize how things work, except for the final parameterless one which you'd need to replace with a named function ("dispatch," perhaps, or "invoke"). I personally would prefer that method since the << syntax is a little more idiomatic, and the use of a named function cause the invocation to happen makes it a little clearer.

##### Share on other sites
Yes, I definitely didn't check that one very carefully, thanks. :)

I personally prefer to use operator() chaining when there is a sense of "currying" parameters for a function, versus operator<< creating a sense of "streaming" data. It maintains symmetry with the final empty ()s. (Otherwise I believe you will need to wrap all the rest of it in parentheses anyway - operator precedence, you know.) Unless maybe you want to invoke the function by streaming a special object (analogous to std::endl etc.)?