trouble with va_list, especially va_arg

Started by
8 comments, last by matches81 17 years, 6 months ago
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?
Advertisement
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.
RunEntityFunction("func", (*i), "%f%f%f", 0.f, 35.f, 238.f)

Also, make sure you're including stdarg.h
We''re sorry, but you don''t have the clearance to read this post. Please exit your browser at this time. (Code 23)
"... " is evil in C++, reconsider the function.
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.
Quote:Original post by jpetrie
Using '...' 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)()
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 :)

[edit]
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]
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.
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.)?
great, thx to you two for clearing that up. On the naming of the operator and the invoke-function: I think I like the () operator for both operators, as it looks rather similar to just calling a normal function, except for more parentheses ;)

I wonder whether that stuff is common practice and there is a (special) name for it?

This topic is closed to new replies.

Advertisement