(script) virtual machine to c++ binding
Started by Alex, Feb 15 2006 02:13 PM
11 replies to this topic
#1
Posted 15 February 2006 - 02:13 PM
I've pretty much reached the point where I want to integrate scripting into my app. I've got this old script system of mine which I'm perfectly happy with except the binding (calling native functions from the virtual machine).
As the old script stuff was win32 only I just used asm to push the arguments on the stack and called the function address. All native functions accessable by script had a defined calling convention etc so that it worked.
This time I'd like to get away from the dirty asm and do it properly.
So the basic problem is:
I've got an array of values (the function arguments) and the function address.
Now I'd like to call the function with the arguments stored in the array.
What I don NOT want to do is to declare another version of each function that takes an array instead of the separate values. It would be ok to do that if that could be automated (some sleek looking macro/template that does it).
It certainly is possible to write a separate template for functions with different
argument count. That's still ugly.
Is there any (more or less commonly supported, automated way) to map function arguments to an array?
In other words: I'm missing a syntax that allows me to generate a list of values that I can pass to a function.
I want some decent metaprograming/code generation without having to abuse templates. Also I'd like to query the compiler (at compile time)
for the type/name of a template argument so I can auto generate the link information necessary for the vm.
Alex
As the old script stuff was win32 only I just used asm to push the arguments on the stack and called the function address. All native functions accessable by script had a defined calling convention etc so that it worked.
This time I'd like to get away from the dirty asm and do it properly.
So the basic problem is:
I've got an array of values (the function arguments) and the function address.
Now I'd like to call the function with the arguments stored in the array.
What I don NOT want to do is to declare another version of each function that takes an array instead of the separate values. It would be ok to do that if that could be automated (some sleek looking macro/template that does it).
It certainly is possible to write a separate template for functions with different
argument count. That's still ugly.
Is there any (more or less commonly supported, automated way) to map function arguments to an array?
In other words: I'm missing a syntax that allows me to generate a list of values that I can pass to a function.
I want some decent metaprograming/code generation without having to abuse templates. Also I'd like to query the compiler (at compile time)
for the type/name of a template argument so I can auto generate the link information necessary for the vm.
Alex
#2
Posted 15 February 2006 - 03:49 PM
Interesting problem, I once was thinking of this myself when writing my own script language.
How would you store different argument types in a single array? Something like boost::any comes to mind, but then you have to pass arguments at runtime, which isn't possible with clean C++ code.
The only thing I could think of is creating a templated adapter function that casts the elements of the array to the correct type and passes it to your function. Of course, you still need to define multiple implementations for each number of arguments, as there's no way you can automate that in C++.
(If you don't have boost installed, here's a bare minimum boost::any implementation that will make the above code compile and run
)
How would you store different argument types in a single array? Something like boost::any comes to mind, but then you have to pass arguments at runtime, which isn't possible with clean C++ code.
The only thing I could think of is creating a templated adapter function that casts the elements of the array to the correct type and passes it to your function. Of course, you still need to define multiple implementations for each number of arguments, as there's no way you can automate that in C++.
typedef std::vector<boost::any> ParamArray;
// base interface for calling the functions
struct adapter_base
{
virtual void call(const ParamArray &) const = 0;
};
template<class T> struct adapter;
// for functions with 1 parameter
template<class R, class P1>
struct adapter<R (P1)> : public adapter_base
{
typedef R (*funcptr)(P1);
adapter(funcptr f) : m_func(f) { }
void call(const ParamArray & p) const { m_func(p[0].as<P1>()); }
private:
funcptr m_func;
};
// for functions with 2 parameters
template<class R, class P1, class P2>
struct adapter<R (P1, P2)> : public adapter_base
{
typedef R (*funcptr)(P1, P2);
adapter(funcptr f) : m_func(f) { }
void call(const ParamArray & p) const { m_func(p[0].as<P1>(), p[1].as<P2>()); }
private:
funcptr m_func;
};
// and with 3
template<class R, class P1, class P2, class P3>
struct adapter<R (P1, P2, P3)> : public adapter_base
{
typedef R (*funcptr)(P1, P2, P3);
adapter(funcptr f) : m_func(f) { }
void call(const ParamArray & p) const
{
m_func(p[0].as<P1>(), p[1].as<P2>(), p[2].as<P3>());
}
private:
funcptr m_func;
};
// 4
template<class R, class P1, class P2, class P3, class P4>
struct adapter<R (P1, P2, P3, P4)> : public adapter_base
{
typedef R (*funcptr)(P1, P2, P3, P4);
adapter(funcptr f) : m_func(f) { }
void call(const ParamArray & p) const
{
m_func(p[0].as<P1>(), p[1].as<P2>(), p[2].as<P3>(), p[3].as<P4>());
}
private:
funcptr m_func;
};
// etc.
// handy helper function
template<class T>
adapter<T> make_adapter(T * func)
{
return adapter<T>(func);
}
// putting it all to the test
void myfunc(int i, float f, const char * str)
{
std::cout << i << f << str << std::endl;
}
int main()
{
ParamArray params;
params.push_back(34);
params.push_back(23.34f);
params.push_back("hi");
const adapter_base & f = make_adapter(&myfunc);
f.call(params);
}
(If you don't have boost installed, here's a bare minimum boost::any implementation that will make the above code compile and run
namespace boost
{
struct any
{
any() { }
template<class T> any(T t) { as<T>() = t; }
template<class T> T & as() { return reinterpret_cast<T&>(data); }
template<class T> const T & as() const { return reinterpret_cast<const T&>(data); }
union { int i; float f; const char * str; } data;
};
}
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.
-
Currently working on: the 3D engine for Tomb Raider.
#3
Posted 15 February 2006 - 08:25 PM
Thx .oisyn.
The actual array is not a problem in my case. It's just a window into the vm's stack (where the parameters are). The script's compiler/linker guarantee that the types are ok.
Alex
The actual array is not a problem in my case. It's just a window into the vm's stack (where the parameters are). The script's compiler/linker guarantee that the types are ok.
Alex
#4
Posted 16 February 2006 - 05:17 AM
Here is a C way of doing it.. kinda.. you'd have to clean up this psuedocode though.
I kept trying to think of a way to generate the function call at runtime (your asm approach) but I think every way is compiler and architecture dependant.
typedef int generic; // replace this with a type factory
void CallFunctionFromVM(void* function, generic* argArray, uint argCount)
{
switch (argCount) {
case 0:
( ( (void*)() ) function ) ( );
return;
case 1:
( ( (void*)(generic*) ) function ) ( argArray[0] );
return;
case 2:
// todo: replace this with a type factory that you determine the type of each
// argument; many ways to do it easiest is just to store the types
// in your array
( ( (void*)(generic*, generic*) ) function ) ( argArray[0], argArray[1] );
return;
// add more if needed
default:
assert_message(false, "unsupported argument count");
return;
}
// for kicks if you dont actually have a built array for some reason
void Call(void* function, uint count, ...)
{
#ifdef DEPENDANT_UPON_YOUR_COMPILERS_VAARG_IMPLEMENTATION
CallFunctionFromVM(function, (generic*)(&count + sizeof(count)), count)
#else
generic x[MAX_FUNCTION_ARGS];
va_list a;
va_start(a, count);
for (int i=0; i<count; i++) x[i] = va_arg(a, generic);
va_end(a);
CallFunctionFromVM(function, x, count)
#endif
}
I kept trying to think of a way to generate the function call at runtime (your asm approach) but I think every way is compiler and architecture dependant.
#5
Posted 16 February 2006 - 05:23 PM
#6
Posted 17 February 2006 - 11:26 AM
hm...after some pondering about this I pretty much came to the conclusion that I'll stick to some conditionally compiled (depending on the target platform/compiler) asm generating the function call. It appears to be impossible to pass the arguments in any sane way that doesn't require another virtual function call (to abstract the different call implementations).
Eventually I'd write tons of code, make things slower with a virtual function call ...and all that just to avoid a simple block of asm that pushes the arguments backwards onto the stack , the last argument being the this pointer (cause all script callable funcs are __cdecl).
Alex
p.s. I demand the language to be enhanced to enable me to do this without asm!!!
Eventually I'd write tons of code, make things slower with a virtual function call ...and all that just to avoid a simple block of asm that pushes the arguments backwards onto the stack , the last argument being the this pointer (cause all script callable funcs are __cdecl).
Alex
p.s. I demand the language to be enhanced to enable me to do this without asm!!!
#7
Posted 17 February 2006 - 12:37 PM
I don't see the problem of the extra virtual call, But you could also use a function pointer if you don't use the interface and make the call method static. And you can also remove the function pointer you pass to the register function by making the function pointer a template argument. But again, I don't see the point.
Now that's just nonsense, the language already does this perfectly as I showed.
Quote
p.s. I demand the language to be enhanced to enable me to do this without asm!!!
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.
-
Currently working on: the 3D engine for Tomb Raider.
#8
Posted 17 February 2006 - 01:23 PM
The extra virtual call makes things slower than necessary. Replacing the virtual call with any other kind of indirection will not improve the performance (compared to the virtual call). The vm needs a generic interface to call the different member functions of different base clases.
Now your code is fine (it's pretty much what I described in my first post, a different template for each possible number of arguments...so by posting that you were missing the point)..but it's far from elegant. That's not your fault, but the language doesn't allow to do it in a more elegant manner ("Of course, you still need to define multiple implementations for each number of arguments, as there's no way you can automate that in C++.").
So what I meant to say was: I'd like an enhancement so that I can create code that is as efficient as the asm version and doesn't require me to generate tons of redundant code by hand that could be auto generated (I thought that's obvious).
Finally I have to say that I'm thankfull for your responses but a slighty less arrogant/smart ass way of answering would be greatly apreciated. I'm rather confident that you're not the only one who can code/has experience on this board.
Alex
Now your code is fine (it's pretty much what I described in my first post, a different template for each possible number of arguments...so by posting that you were missing the point)..but it's far from elegant. That's not your fault, but the language doesn't allow to do it in a more elegant manner ("Of course, you still need to define multiple implementations for each number of arguments, as there's no way you can automate that in C++.").
So what I meant to say was: I'd like an enhancement so that I can create code that is as efficient as the asm version and doesn't require me to generate tons of redundant code by hand that could be auto generated (I thought that's obvious).
Finally I have to say that I'm thankfull for your responses but a slighty less arrogant/smart ass way of answering would be greatly apreciated. I'm rather confident that you're not the only one who can code/has experience on this board.
Alex
#9
Posted 17 February 2006 - 01:45 PM
virtual calls? what virtual calls?
edit: oh that one =)
now, be honest, if you're writing some code in a SCRIPT language, does one virtual call really matter?
and for that 'multiple implementations for each number of arguments' thing, that code can easily be generated ... That's how I did it anyway ...
edit: oh that one =)
now, be honest, if you're writing some code in a SCRIPT language, does one virtual call really matter?
and for that 'multiple implementations for each number of arguments' thing, that code can easily be generated ... That's how I did it anyway ...
hi, i'm a signature viruz, plz set me as your signature and help me spread :)
Bramz' warehouse | LiAR isn't a raytracer
Bramz' warehouse | LiAR isn't a raytracer
#10
Posted 17 February 2006 - 02:49 PM
Alex said:
Now your code is fine (it's pretty much what I described in my first post, a different template for each possible number of arguments...so by posting that you were missing the point)..but it's far from elegant.
Quote
That's not your fault
Quote
So what I meant to say was: I'd like an enhancement so that I can create code that is as efficient as the asm version and doesn't require me to generate tons of redundant code by hand that could be auto generated (I thought that's obvious).
Quote
Finally I have to say that I'm thankfull for your responses but a slighty less arrogant/smart ass way of answering would be greatly apreciated.
So, friends?
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.
-
Currently working on: the 3D engine for Tomb Raider.
#11
Posted 17 February 2006 - 03:26 PM
That's funny..when I wrote my last post I wondered what exactly made u interpret my post in the way you did and why exactly it was nonsense especially as you had agreed that auto generating the code (using c++) isn't possible.
It appeared you tried very hard to not see my point but instead to show that I'm talking nonsense. Apparently it wasn't meant that way , I'm sorry.
Obviously I'm not in the position to demand anything done to c++. So my demand was about as serious as your "nonsense" :)
Let's make a deal..next time you think I wrote total nonsense pls read my post again to see if I might have meant something different ;)
sure..friends
Alex
It appeared you tried very hard to not see my point but instead to show that I'm talking nonsense. Apparently it wasn't meant that way , I'm sorry.
Obviously I'm not in the position to demand anything done to c++. So my demand was about as serious as your "nonsense" :)
Let's make a deal..next time you think I wrote total nonsense pls read my post again to see if I might have meant something different ;)
sure..friends
Alex
#12
Posted 17 February 2006 - 03:58 PM
Right, I seem to have misread the aforementioned post. Sorry about that
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.
-
Currently working on: the 3D engine for Tomb Raider.
1 user(s) are reading this topic
0 members, 1 guests, 0 anonymous users












