# Variable parameters retaining type information

19 replies to this topic

### #1eddie

Senior Member

• Members
• 751 posts

Posted 23 March 2006 - 08:02 AM

So, I want to have my cake and eat it too.

I'm trying to create a clean binding between my Lua and C++ code, and thanks to tolua++ (and some great help from Ariel Manzur), I think I've just about got it.

The one thing that's irking me, however, is C++ related.

I'd like to have a syntax from C++ that looks like this:

myLuaInterpreterClass.CallLuaFunction("luafunctionname", ...);

Where ... is zero or more parameters, of possibly varying types.

Now, the inside of that function is going to call to various lua functions to set the state and push the actual variable *into* Lua, but it needs to use it's type to determine what to do.

I think I'm hosed, or at least I'll have to do some crazy nasty shit with templates to get it to work. Does anyone have any other ideas? Currently all I'm thinking is something like this:


class LuaInterpreter

{

public:

void CallLuaFunction(const std::string& funcName);

template<typename T>

void CallLuaFunction(const std::string& funcName, const T& param1);

template<typename T, typename T1>

void CallLuaFunction(const std::string& funcName, const T& param1, const T1& param2);

template<typename T, typename T1, typename T2>

void CallLuaFunction(const std::string& funcName, const T& param1, const T1& param2, const T2& param3);

// ... etc, etc.

};



I've done this before using Boost::PreProcessor, and it's yukky, but it works...

Anyways, perhaps I'm just rambling. Let me know if this makes sense. :D

### #2.oisyn

DevMaster Staff

• Moderators
• 1842 posts

Posted 23 March 2006 - 10:21 AM

That is in fact the only way to do it
-
Currently working on: the 3D engine for Tomb Raider.

### #3Mattias Gustavsson

Senior Member

• Members
• 413 posts

Posted 23 March 2006 - 10:43 AM

You could create a "Variant" type, which can store a variable of any type, and then when you call your function, you don't do:

myLuaInterpreterClass.CallLuaFunction("luafunction name", 1.0f, 10, "test");

but rather

myLuaInterpreterClass.CallLuaFunction("luafunction name", Variant(1.0f), Variant(10), Variant("test"));

and then you can use a GetType method in the variant to get which type each parameter is.

The drawback is that if you forget to put Variant() around a parameter somewhere, you'll have a bug (probably even a crash).

I'd still take this before the template version any day :-)

### #4.oisyn

DevMaster Staff

• Moderators
• 1842 posts

Posted 23 March 2006 - 11:01 AM

Mattias Gustavsson said:

You could create a "Variant" type, which can store a variable of any type, and then when you call your function, you don't do:

myLuaInterpreterClass.CallLuaFunction("luafunction name", 1.0f, 10, "test");

but rather

myLuaInterpreterClass.CallLuaFunction("luafunction name", Variant(1.0f), Variant(10), Variant("test"));
That's possible, but keep in mind that copy-ctors won't run for the Variant type, and is therefore very dangerous (if you pass in a std::string for example, and you modify it's contents, it's heap corruption gallore). Also, you don't know how many variables are passed to the function so you'll need an end marker as well (although this information can be retrieved from the lua function that is to be called).

Quote

I'd still take this before the template version any day :-)
Why? The usage is a lot more hassle and it's potentially very harmful if you don't use it in the right way. With templates, you only have the burdon of defining a few functions, but this is a one-time process. And it's type-safe, plus your app won't crash if you forget something.
-
Currently working on: the 3D engine for Tomb Raider.

### #5Mattias Gustavsson

Senior Member

• Members
• 413 posts

Posted 23 March 2006 - 02:13 PM

.oisyn said:

Why?

Personal preference :)

### #6.oisyn

DevMaster Staff

• Moderators
• 1842 posts

Posted 23 March 2006 - 03:13 PM

So you prefer typing more error-prone code over safe and clean code? Well it's your choice and I'll respect that
-
Currently working on: the 3D engine for Tomb Raider.

### #7eddie

Senior Member

• Members
• 751 posts

Posted 23 March 2006 - 03:18 PM

Wouldn't the variant stuff still require me to have code like I mentioned above?

Except it would look like this:

class LuaInterpreter
{
public:
void CallLuaFunction(const std::string& name);
void CallLuaFunction(const std::string& name, const VariantType&);
void CallLuaFunction(const std::string& name, const VariantType&, const VariantType&);
// etc

};


Hrmm, now that I think about it, I suppose I could do varargs, and just *know* that it's going to be a variant, do the cast, and therefore introducing the potential bug that you're mentioning.

Decisions, decisions. I think I like the templated goop better, except for it's lack of debuggabilit once it comes out of somethign like boost::preprocessor. I suppose I'll try both and hope for the best.

Thanks for the insight.

### #8.oisyn

DevMaster Staff

• Moderators
• 1842 posts

Posted 23 March 2006 - 04:20 PM

If you want to avoid the template code in your LuaInterpreter class you could always make a function that stores all the passed arguments and their type information in an object, and use that object to extract the arguments.

So something like:
class LuaInterpreter
{
void CallLuaFunction(const std::string & name, const std::vector<boost::any> & args);
};

template<class P1>
std::vector<boost::any> arguments(const P1 & p1)
{
std::vector<boost::any> v;
v.push_back(p1);
return v;
}

template<class P1, class P2>
std::vector<boost::any> arguments(const P1 & p1, const P2 & p2)
{
std::vector<boost::any> v;
v.push_back(p1);
v.push_back(p2);
return v;
}

// etc

int main()
{
LuaInterpreter li;
li.CallLuaFunc("myFunc", arguments(1, 2, "hi there", 0.3435f));
}

But instead of boost::any you probably want your own type containing a reference to an interface that pushes an argument of a given type on the lua stack, so you aren't stuck with huge if-else-if structures to test the type in a boost::any to determine the appropriate actions to take.
-
Currently working on: the 3D engine for Tomb Raider.

### #9eddie

Senior Member

• Members
• 751 posts

Posted 23 March 2006 - 04:25 PM

Hrmm. That's not exactly what I want, but you gave me a *great* idea, .oisyn.

That reminded me of the named parameter idiom, and more specifically, boost's usage of operator % for boost::format.

I could potentially have something like this:



CallLuaFunction("foo").Param(param1).Param(param2).Param(param3) // etc, etc.



It might not be what I asked for in the beginning, but it's an idea! I'd only need one templated function at that point, and I can deal with the conversions to boost::any internally.

INTERESTING. :D

Thanks!

### #10.oisyn

DevMaster Staff

• Moderators
• 1842 posts

Posted 23 March 2006 - 04:58 PM

Or, you could actually overload the , operator to do that

CallLuaFunction("foo"), param1, param2, param3;

-
Currently working on: the 3D engine for Tomb Raider.

### #11dave_

Senior Member

• Members
• 584 posts

Posted 23 March 2006 - 11:08 PM

Mattias Gustavsson said:

Personal preference :)
I've got to agree with .oisyn here. Templates are the only way to go. It will be stupidly in inefficient to use dynamic types when the compiler can calculate them at runtime.

### #12Mattias Gustavsson

Senior Member

• Members
• 413 posts

Posted 24 March 2006 - 10:11 AM

Depends on what you want to achieve. Performance is not always the number one goal.

Lots of things in programming are down to your specific circumstances and personal preference.

I prefer my way to having dozens of template functions to be able to cope with variable number of arguments. Others prefer the template method. Both have their advantages and disadvantages. All I'm saying is, make the tradeoffs that works for your project.

### #13dave_

Senior Member

• Members
• 584 posts

Posted 24 March 2006 - 03:21 PM

Mattias Gustavsson said:

Depends on what you want to achieve. Performance is not always the number one goal.

Lots of things in programming are down to your specific circumstances and personal preference.

I prefer my way to having dozens of template functions to be able to cope with variable number of arguments. Others prefer the template method. Both have their advantages and disadvantages. All I'm saying is, make the tradeoffs that works for your project.
true, but I prefer to use a different language, perhaps something with proper dynamic types.

### #14monjardin

Senior Member

• Members
• 1033 posts

Posted 24 March 2006 - 07:54 PM

Overloading the comma operator makes me uneasy. I'd prefer returning a functor.

CallLuaFunction("foo")(param1)(param2)(param3);



monjardin's JwN Meter (1,2,3,4,5,6):
|----|----|----|----|----|----|----|----|----|----|
*

### #15eddie

Senior Member

• Members
• 751 posts

Posted 27 April 2006 - 07:11 PM

I had some time to play with this idea at home, and I came up with a neat solution. It's pretty much as perfect as I can get it.

This is just example code, but it should compile and run for you (does for me under gcc 3.4.5):


#include <iostream>

using namespace std;

class MyFunction

{

public:

MyFunction(const char* pFunctionName)

{

cout << "Received function " << pFunctionName << endl;

}

MyFunction& operator,(const char* pParam)

{

cout << "Received param " << pParam << endl;

return *this;

}

};

class MyFunctionCaller

{

public:

void operator[](MyFunction)

{

cout << "Calling function!" << endl;

};

};

int main(void)

{

MyFunctionCaller myFunctionCaller;

myFunctionCaller[ MyFunction("FunkName"), "one", "two", "three" ];

return 0;

}



The output it gives looks like this:

Quote

Calling function!

A couple of points:

• I use the operator[] for the function caller, instead of operator()
• You have to use a MyFunction("Foo") call first.

The first point is important, because if I were to use operator(), the compiler starts looking for an overloaded member that accepts more parameters than what I've got. operator[] only accepts one parameter, so it looks for an "operator," which will return a MyFunction.

The second point goes hand in hand for a couple of reasons. First, I'm basically chaining via operator, the instance of the MyFunction, and it needs to know what type that is. A simple char * doesn't automatically converted to a MyFunction (one of the times I was hoping implicit constructors would rear it's otherwise-ugly head) in this case.

Anyhow, I think it's pretty neat, and thought I'd share. It's very similar to the other posts up here, but gives a more familiar function-calling feel (... sort of?). :)

Anyways. Sorry for reposting to an old thread. :)

### #16Jare

Valued Member

• Members
• 247 posts

Posted 27 April 2006 - 09:00 PM

I love that last solution! Although the indexing is kinda ugly, you could always make it like this:
class MyFunctionCaller
{
public:
MyFunctionCaller(MyFunction)
{
cout << "Calling function!" << endl;
}
};

int main(void)
{
MyFunctionCaller((MyFunction("FunkName"), "one", "two", "three" ));
return 0;
}
The (( )) protocol is a common way to achieve variable number of parameters to macros, so the resulting syntax is not too weird.

### #17eddie

Senior Member

• Members
• 751 posts

Posted 27 April 2006 - 09:07 PM

See my first point. That won't work, at least it didn't, under gcc.

The reason I use operator[] versus operator(), is because operator[] forces you to only have one parameter. operator() allows multiple, which then gets confusing to the compiler -- "Are you passing me multiple parameters to this function? Or do you want me to use the comma operator?". I'm sure there's a rule in the C++ spec that says why the compiler doesn't try to lookup the comma operator in these cases, but I'll leave it to the all-knowing .oisyn to point that one out. :)

Anyhow, it works well enough. I'm pretty damned happy with it. :) Feel free to prove me wrong if you can send me code that compiles on gcc with operator() -- I couldn't.

### #18eddie

Senior Member

• Members
• 751 posts

Posted 27 April 2006 - 09:08 PM

Oh, I'm a tool. Now I see that you're using double parenthesis on the function call.

Yes, that would work, you're right. That said, between using [] or (()), I'd prefer using less brackets, and less wonky errors if you forget to use the double brackets.

Thanks for the kudos, by the by. ;)

### #19monjardin

Senior Member

• Members
• 1033 posts

Posted 27 April 2006 - 09:12 PM

You could drop the function caller class all together like this:
#include <iostream>
using namespace std;

class MyFunction
{
public:
MyFunction(const char* pFunctionName)
{
cout << "Received function " << pFunctionName << endl;
}
~MyFunction()
{
cout << "Calling function!" << endl;
}

MyFunction& operator,(const char* pParam)
{
cout << "Received param " << pParam << endl;
return *this;
}
};

int main(void)
{
MyFunction("FunkName"), "one", "two", "three";

return 0;
}


EDIT: Don't do this if you need to throw exceptions from the function!
monjardin's JwN Meter (1,2,3,4,5,6):
|----|----|----|----|----|----|----|----|----|----|
*

### #20eddie

Senior Member

• Members
• 751 posts

Posted 27 April 2006 - 09:36 PM

Well, a couple of reasons why I myself can't.

First, my "FunctionCaller" is my LuaInterpreter. This is a real object that actually holds state that is used in calling the function.

Second, I like having the [] (ideally, a single set of ()), surround my parameters. Makes it feel all lovely, like a regular function. :)

Third, my operator[] will, in the end, actually end up executing the function. As it stands in what you've got, it'll collect all the parameters and such, but not do anything with them. (Easily fixed, I know. Just sayin'. :))

But, good points if someone's looking for something like that.

#### 1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users