DevMaster.net Forums
[[ Home | Forums | 3D Engines Database | Wiki | Articles/Tutorials | Game Dev Jobs | IRC Chat Network | Contact Us ]]

Go Back   DevMaster.net Forums > Site Discussions > Code & Snapshot Discussion
User Name
Password
Register FAQ Members List Search Today's Posts Mark Forums Read

Reply
 
Thread Tools Search this Thread Display Modes
Old 11-25-2006, 08:00 AM   #1
bladder
DevMaster Staff
 
bladder's Avatar
 
Join Date: Sep 2003
Location: Hell
Posts: 1,109
Default

Hello again,

Here's the situation. Imagine you had a callback function that takes 4 arguments. x,y position of your mouse pointer, a bool indicating if a button is pressed, and then an int indicating the button that is pressed.

Now imagine you already have a function that takes 2 ints representing the mouse coordinates and then draws a sprite at those coordinates. You can't assign this function to the callback because the sigs don't match.

I had this situation a while back and I didn't feel like writing a whole new function to take into account the extra bool and int that I really had no car for at the time. So I wrote up an argument caster. What it does, it takes a function object and casts it to another function object that must have the same return type, but can have a different number of parameters.

something like:

Code:
bool drawfunc(int, int); callback = args_cast< 2, bool(int,int,bool,int) >(&drawfunc); callback( x, y, true, 3 ); // calls drawfun(x,y)

I've made args_cast so that you can also rewire the arguments. So if you wanted the 4th parameter to go to the 1st parameter of the actual function, you could do:

callback = args_cast< 2, bool(int,int,bool,int), 4 >

or if you wanted the 3rd and 4th of the synthesized function to go to the 1st and 2nd of the actual function:

callback = args_cast< 2, bool(int,int,bool,int), 3, 4 >

The first template parameter is a number indicating how many arguments the actual function takes.

So here's the args_Cast implementation. My current implementation allows a maximum of a 5 argument synthesized function. (synthsized function being the one that takes dummy arguments)

NOTE: you need to have boost installed as I make use of the function_traits template that allows for syntax such as some_func<bool(int,int)> as opposed to some_func<bool, int, int>. There's also a lot of "mechanical code" involved in this implementation, a job for boost.preprocessor I'm sure.

Also, I'm thinking of submitting this to boost, what do you guys think?

Code:
#include <boost/type_traits/function_traits.hpp> // // arg choosers // template< class A0, class A1, int N > struct arg_chooser2; template< class A0, class A1 > struct arg_chooser2<A0, A1, 1> { A0& arg; arg_chooser2(A0& a0, A1& a1) : arg(a0) {} }; template< class A0, class A1 > struct arg_chooser2<A0, A1, 2> { A1& arg; arg_chooser2(A0& a0, A1& a1) : arg(a1) {} }; template< class A0, class A1, class A2, int N > struct arg_chooser3; template< class A0, class A1, class A2 > struct arg_chooser3<A0, A1, A2, 1> { A0& arg; arg_chooser3(A0& a0, A1& a1, A2& a2) : arg(a0) {} }; template< class A0, class A1, class A2 > struct arg_chooser3<A0, A1, A2, 2> { A1& arg; arg_chooser3(A0& a0, A1& a1, A2& a2) : arg(a1) {} }; template< class A0, class A1, class A2 > struct arg_chooser3<A0, A1, A2, 3> { A2& arg; arg_chooser3(A0& a0, A1& a1, A2& a2) : arg(a2) {} }; template< class A0, class A1, class A2, class A3, int N > struct arg_chooser4; template< class A0, class A1, class A2, class A3 > struct arg_chooser4<A0, A1, A2, A3, 1> { A0& arg; arg_chooser4(A0& a0, A1& a1, A2& a2, A3& a3) : arg(a0) {} }; template< class A0, class A1, class A2, class A3 > struct arg_chooser4<A0, A1, A2, A3, 2> { A1& arg; arg_chooser4(A0& a0, A1& a1, A2& a2, A3& a3) : arg(a1) {} }; template< class A0, class A1, class A2, class A3 > struct arg_chooser4<A0, A1, A2, A3, 3> { A2& arg; arg_chooser4(A0& a0, A1& a1, A2& a2, A3& a3) : arg(a2) {} }; template< class A0, class A1, class A2, class A3 > struct arg_chooser4<A0, A1, A2, A3, 4> { A3& arg; arg_chooser4(A0& a0, A1& a1, A2& a2, A3& a3) : arg(a3) {} }; template< class A0, class A1, class A2, class A3, class A4, int N > struct arg_chooser5; template< class A0, class A1, class A2, class A3, class A4 > struct arg_chooser5<A0, A1, A2, A3, A4, 1> { A0& arg; arg_chooser5(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) : arg(a0) {} }; template< class A0, class A1, class A2, class A3, class A4 > struct arg_chooser5<A0, A1, A2, A3, A4, 2> { A1& arg; arg_chooser5(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) : arg(a1) {} }; template< class A0, class A1, class A2, class A3, class A4 > struct arg_chooser5<A0, A1, A2, A3, A4, 3> { A2& arg; arg_chooser5(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) : arg(a2) {} }; template< class A0, class A1, class A2, class A3, class A4 > struct arg_chooser5<A0, A1, A2, A3, A4, 4> { A3& arg; arg_chooser5(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) : arg(a3) {} }; template< class A0, class A1, class A2, class A3, class A4 > struct arg_chooser5<A0, A1, A2, A3, A4, 5> { A4& arg; arg_chooser5(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) : arg(a4) {} }; // // 1 arg functions // template< int N, class R, class A0, class F > class add_args1 { public: add_args1( F f ) : f_(f) {} R operator () ( A0 a0 ) { return f_(); } F f_; }; // // 2 arg functions // template< int N, class R, class A0, class A1, class F, int P1 > class add_args2; template< class R, class A0, class A1, class F, int P1 > class add_args2<0, R, A0, A1, F, P1 > { public: add_args2( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1 ) { return f_(); } F f_; }; template< class R, class A0, class A1, class F, int P1 > class add_args2<1, R, A0, A1, F, P1 > { public: add_args2( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1 ) { arg_chooser2<A0, A1, P1> r0(a0,a1); return f_(r0.arg); } F f_; }; // // 3 arg functions // template< int N, class R, class A0, class A1, class A2, class F, int P1, int P2 > class add_args3; template< class R, class A0, class A1, class A2, class F, int P1, int P2 > class add_args3<0, R, A0, A1, A2, F, P1, P2 > { public: add_args3( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1, A2 a2 ) { return f_(); } F f_; }; template< class R, class A0, class A1, class A2, class F, int P1, int P2 > class add_args3<1, R, A0, A1, A2, F, P1, P2 > { public: add_args3( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1, A2 a2 ) { arg_chooser3<A0, A1, A2, P1> r0(a0,a1,a2); return f_(r0.arg); } F f_; }; template< class R, class A0, class A1, class A2, class F, int P1, int P2 > class add_args3<2, R, A0, A1, A2, F, P1, P2 > { public: add_args3( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1, A2 a2 ) { arg_chooser3<A0, A1, A2, P1> r0(a0,a1,a2); arg_chooser3<A0, A1, A2, P2> r1(a0,a1,a2); return f_(r0.arg, r1.arg); } F f_; }; // // 4 arg functions // template< int N, class R, class A0, class A1, class A2, class A3, class F, int P1, int P2, int P3 > class add_args4; template< class R, class A0, class A1, class A2, class A3, class F, int P1, int P2, int P3 > class add_args4<0, R, A0, A1, A2, A3, F, P1, P2, P3 > { public: add_args4( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1, A2 a2, A3 a3 ) { return f_(); } F f_; }; template< class R, class A0, class A1, class A2, class A3, class F, int P1, int P2, int P3 > class add_args4<1, R, A0, A1, A2, A3, F, P1, P2, P3 > { public: add_args4( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1, A2 a2, A3 a3 ) { arg_chooser4<A0, A1, A2, A3, P1> r0(a0,a1,a2,a3); return f_(r0.arg); } F f_; }; template< class R, class A0, class A1, class A2, class A3, class F, int P1, int P2, int P3 > class add_args4<2, R, A0, A1, A2, A3, F, P1, P2, P3 > { public: add_args4( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1, A2 a2, A3 a3 ) { arg_chooser4<A0, A1, A2, A3, P1> r0(a0,a1,a2,a3); arg_chooser4<A0, A1, A2, A3, P2> r1(a0,a1,a2,a3); return f_(r0.arg, r1.arg); } F f_; }; template< class R, class A0, class A1, class A2, class A3, class F, int P1, int P2, int P3 > class add_args4<3, R, A0, A1, A2, A3, F, P1, P2, P3 > { public: add_args4( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1, A2 a2, A3 a3 ) { arg_chooser4<A0, A1, A2, A3, P1> r0(a0,a1,a2,a3); arg_chooser4<A0, A1, A2, A3, P2> r1(a0,a1,a2,a3); arg_chooser4<A0, A1, A2, A3, P3> r2(a0,a1,a2,a3); return f_(r0.arg, r1.arg, r2.arg); } F f_; }; // // 5 arg functions // template< int N, class R, class A0, class A1, class A2, class A3, class A4, class F, int P1, int P2, int P3, int P4 > class add_args5; template< class R, class A0, class A1, class A2, class A3, class A4, class F, int P1, int P2, int P3, int P4 > class add_args5<0, R, A0, A1, A2, A3, A4, F, P1, P2, P3, P4 > { public: add_args5( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1, A2 a2, A3 a3, A4 a4 ) { return f_(); } F f_; }; template< class R, class A0, class A1, class A2, class A3, class A4, class F, int P1, int P2, int P3, int P4 > class add_args5<1, R, A0, A1, A2, A3, A4, F, P1, P2, P3, P4 > { public: add_args5( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1, A2 a2, A3 a3, A4 a4 ) { arg_chooser5<A0, A1, A2, A3, A4, P1> r0(a0,a1,a2,a3,a4); return f_(r0.arg); } F f_; }; template< class R, class A0, class A1, class A2, class A3, class A4, class F, int P1, int P2, int P3, int P4 > class add_args5<2, R, A0, A1, A2, A3, A4, F, P1, P2, P3, P4 > { public: add_args5( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1, A2 a2, A3 a3, A4 a4 ) { arg_chooser5<A0, A1, A2, A3, A4, P1> r0(a0,a1,a2,a3,a4); arg_chooser5<A0, A1, A2, A3, A4, P2> r1(a0,a1,a2,a3,a4); return f_(r0.arg, r1.arg); } F f_; }; template< class R, class A0, class A1, class A2, class A3, class A4, class F, int P1, int P2, int P3, int P4 > class add_args5<3, R, A0, A1, A2, A3, A4, F, P1, P2, P3, P4 > { public: add_args5( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1, A2 a2, A3 a3, A4 a4 ) { arg_chooser5<A0, A1, A2, A3, A4, P1> r0(a0,a1,a2,a3,a4); arg_chooser5<A0, A1, A2, A3, A4, P2> r1(a0,a1,a2,a3,a4); arg_chooser5<A0, A1, A2, A3, A4, P3> r2(a0,a1,a2,a3,a4); return f_(r0.arg, r1.arg, r2.arg); } F f_; }; template< class R, class A0, class A1, class A2, class A3, class A4, class F, int P1, int P2, int P3, int P4 > class add_args5<4, R, A0, A1, A2, A3, A4, F, P1, P2, P3, P4 > { public: add_args5( F f ) : f_(f) {} R operator () ( A0 a0, A1 a1, A2 a2, A3 a3, A4 a4 ) { arg_chooser5<A0, A1, A2, A3, A4, P1> r0(a0,a1,a2,a3,a4); arg_chooser5<A0, A1, A2, A3, A4, P2> r1(a0,a1,a2,a3,a4); arg_chooser5<A0, A1, A2, A3, A4, P3> r2(a0,a1,a2,a3,a4); arg_chooser5<A0, A1, A2, A3, A4, P4> r3(a0,a1,a2,a3,a4); return f_(r0.arg, r1.arg, r2.arg, r3.arg); } F f_; }; // // arg expansion impls // template< int Arity, class Sig, class F, int N, int P1, int P2, int P3, int P4 > class add_args_impl; template< class Sig, class F, int N, int P1, int P2, int P3, int P4 > class add_args_impl<1, Sig, F, N, P1, P2, P3, P4> { typedef boost::function_traits<Sig> traits; public: typedef add_args1< N, typename traits::result_type, typename traits::arg1_type, F > type; }; template< class Sig, class F, int N, int P1, int P2, int P3, int P4 > class add_args_impl<2, Sig, F, N, P1, P2, P3, P4> { typedef boost::function_traits<Sig> traits; public: typedef add_args2< N, typename traits::result_type, typename traits::arg1_type, typename traits::arg2_type, F, P1 > type; }; template< class Sig, class F, int N, int P1, int P2, int P3, int P4 > class add_args_impl<3, Sig, F, N, P1, P2, P3, P4> { typedef boost::function_traits<Sig> traits; public: typedef add_args3< N, typename traits::result_type, typename traits::arg1_type, typename traits::arg2_type, typename traits::arg3_type, F, P1, P2 > type; }; template< class Sig, class F, int N, int P1, int P2, int P3, int P4 > class add_args_impl<4, Sig, F, N, P1, P2, P3, P4> { typedef boost::function_traits<Sig> traits; public: typedef add_args4< N, typename traits::result_type, typename traits::arg1_type, typename traits::arg2_type, typename traits::arg3_type, typename traits::arg4_type, F, P1, P2, P3 > type; }; template< class Sig, class F, int N, int P1, int P2, int P3, int P4 > class add_args_impl<5, Sig, F, N, P1, P2, P3, P4> { typedef boost::function_traits<Sig> traits; public: typedef add_args5< N, typename traits::result_type, typename traits::arg1_type, typename traits::arg2_type, typename traits::arg3_type, typename traits::arg4_type, typename traits::arg5_type, F, P1, P2, P3, P4 > type; }; // // main wrapper // template< class Sig, class F, int N, int P1 = 1, int P2 = 2, int P3 = 3, int P4 = 4 > class add_args : public add_args_impl< boost::function_traits<Sig>::arity, Sig, F, N, P1, P2, P3, P4 >::type { typedef typename add_args_impl< boost::function_traits<Sig>::arity, Sig, F, N, P1, P2, P3, P4 >::type args_type; public: add_args( F f ) : args_type(f) {} }; // // casts // template< int N, class SigTo, class From > add_args<SigTo, From, N> args_cast( From f ) { return add_args<SigTo, From, N>( f ); } template< int N, class SigTo, int P1, class From > add_args<SigTo, From, N, P1> args_cast( From f ) { return add_args<SigTo, From, N, P1>( f ); } template< int N, class SigTo, int P1, int P2, class From > add_args<SigTo, From, N, P1, P2> args_cast( From f ) { return add_args<SigTo, From, N, P1, P2>( f ); } template< int N, class SigTo, int P1, int P2, int P3, class From > add_args<SigTo, From, N, P1, P2, P3> args_cast( From f ) { return add_args<SigTo, From, N, P1, P2, P3>( f ); } template< int N, class SigTo, int P1, int P2, int P3, int P4, class From > add_args<SigTo, From, N, P1, P2, P3, P4> args_cast( From f ) { return add_args<SigTo, From, N, P1, P2, P3, P4>( f ); }
bladder is offline   Reply With Quote
Old 11-25-2006, 08:17 AM   #2
dave_
Senior Member
 
Join Date: Sep 2005
Location: Brighton, UK
Posts: 584
Default Re: function argument caster

I'm not sure. I personally wouldnt use it because it has a confusing syntax.
The reason I think its confusing is because its not obivous what parameters get mapped/discarded.

If you want an example of a move obivious way of mapping look at boost bind. Perhaps you could extend bind to have a way of discarding arguments.

However in that sort of case, I'd prefer adding callback function which explicitly maps the the callback. Theres no extra dependencies and it compiles tonnes faster.

Whats a more natural c++ syntax this:
args_cast< 2, bool(int,int,bool,int), 3, 4 >(&drawfunc)
or this:
calbackdrawfunc(int x, int y, bool u, int z) { return drawfunc(u,z); }
?

Last edited by dave_ : 11-25-2006 at 08:26 AM.
dave_ is offline   Reply With Quote
Old 11-25-2006, 06:58 PM   #3
stodge
Valued Member
 
Join Date: May 2003
Location: Canada
Posts: 111
Default Re: function argument caster

I just use boost::any:

typedef std::vector<boost::any>ParameterListType;
typedef boost::any ParameterType;

typedef void (ICallable::*FunctionPtrType)(ParameterListType& params);

Like so:

Code:
#include "Reactor.h" #include "ICallable.h" #include "CoreMacros.h" #include "LinuxSignalHandler.h" #include <iostream> class MyTimer: public ICallable { public: void Hello(ParameterListType& params) { HB_TRACE_PUSH; std::cout << "Hello" << std::endl; Reactor::GetSingleton().Stop(); HB_TRACE_POP; }; void Hello2(ParameterListType& params) { HB_TRACE_PUSH; std::cout << "Hello2" << std::endl; Timer* timer= HB_GET_PARAMETER(params, Timer*, 0); timer->dump(); Reactor::GetSingleton().Cancel(timer); Reactor::GetSingleton().Stop(); HB_TRACE_POP; }; void Hello3(ParameterListType& params) { HB_TRACE_PUSH; std::cout << "Hello3" << std::endl; Reactor::GetSingleton().Stop(); HB_TRACE_POP; }; void Test(ParameterListType& params) { HB_TRACE_PUSH; std::cout << "Test" << std::endl; HB_TRACE_POP; }; }; void onExit(void) { std::cout << "onExit - exiting application cleanly" << std::endl; Reactor::GetSingleton().Dump(); } int main() { HB_TRACE_PUSH; Reactor* reactor = &Reactor::GetSingleton(); reactor->Initialise(); Reactor::SetTickTime(0.32f); MyTimer* my = new MyTimer(); reactor->SetDelay(10000); ParameterListType params1; ParameterListType params2; ParameterListType params3; ParameterListType params4; LinuxSignalHandler sig; sig.AddExitHandler(onExit); Timer* timer1 = reactor->Schedule(my, (FunctionPtrType)&MyTimer::Test, 2000, params1); if (timer1==NULL) { std::cout << "Timer 1 failed" << std::endl; } else { timer1->dump(); } Timer* timer2 = reactor->Schedule(my, (FunctionPtrType)&MyTimer::Test, 3000, params2); if (timer2==NULL) { std::cout << "Timer 2 failed" << std::endl; } else { timer2->dump(); } Timer* timer3 = reactor->Schedule(my, (FunctionPtrType)&MyTimer::Test, 4000, params3); if (timer3==NULL) { std::cout << "Timer 3 failed" << std::endl; } else { timer3->dump(); } Timer* stop = reactor->Schedule(my, (FunctionPtrType)&MyTimer::Hello, 8000, params4); if (stop==NULL) { std::cout << "Stop Timer failed" << std::endl; } else { stop->dump(); } reactor->Cancel(timer1); reactor->Cancel(timer2); reactor->Cancel(timer3); timer1 = timer2 = timer3 = NULL; reactor->Run(); std::cout << "Cancelling 1 " << timer1 << std::endl; reactor->Cancel(timer1); std::cout << "Cancelling 2 " << timer2 << std::endl; reactor->Cancel(timer2); std::cout << "Cancelling 3 " << timer3 << std::endl; reactor->Cancel(timer3); reactor->Shutdown(); HB_TRACE_POP; return 1; }
___________________________________________
http://stodge.blogspot.com
stodge is offline   Reply With Quote
Old 11-26-2006, 04:16 AM   #4
bladder
DevMaster Staff
 
bladder's Avatar
 
Join Date: Sep 2003
Location: Hell
Posts: 1,109
Default Re: function argument caster

dave_:

The syntax would be documented of course, so I don't think it would be any more complicated then using something like bind. First parameter is the number of args that the real function takes. Second parameter is the signature of the synthesized function and what follows are rewiring parameters which are not necessary. By default the parameters are mapped according to how they appear in the sythesized function. So if the synthesized function has 4 args and the real function has 3 args, by default, the first 3 args to the synthesized function are passed to the real function.

The rewiring parameters that follow the sythesized function signature parameter are just like boost.bind, except thay're not placeholders but integers specifying which argument of the synthesied function will be directed to "this" location of the real function.

As for adding an explicit callback that calles the real funciton, well of course that's a solution but the args_cast was made to avoid cluttering with those types of functions unnecessarily, it should enhance readability as well.

What kind of syntax would you see with something like this.

About extending bind: I don't think that should be done because bind has a specific purpose: it binds arguments to functions. args_cast has a specific purpose: expand the number of arguments a function can take, while discarding the extra ones. As it is bind cannot be made to expand functions, and doing so would kid of give it another purpose which i think should be made seperate (like with the args_cast)

stodge:

while that way works of course, it involves too much boiler-plate code on the client side, which is something I try to avoid. I wouldn't want someone making a callback to follow too many protocols, give them more flexibility. I'd rather have clients follow less strict protocols. The function signature is the main protocol with a callback, args_cast makes following that less strict.
___________________________________________
- TripleBuffer
- Me blog
bladder is offline   Reply With Quote
Old 11-26-2006, 08:15 AM   #5
stodge
Valued Member
 
Join Date: May 2003
Location: Canada
Posts: 111
Default Re: function argument caster

Too much boiler plate code? I don't understand what you mean. Also, how is this not flexible? I give the developer the ability to store whatever they want into the parameter list.
___________________________________________
http://stodge.blogspot.com
stodge is offline   Reply With Quote
Old 11-26-2006, 09:45 AM   #6
.oisyn
DevMaster Staff
 
.oisyn's Avatar
 
Join Date: Sep 2005
Location: The Netherlands
Posts: 1,442
Default Re: function argument caster

Quote:
Originally Posted by stodge
Too much boiler plate code?
You'll have to code num param and type checking in every function (although for the latter you could just on boost::any_cast's exceptions). And automatic conversions won't work anymore like normally so you'll have to be very careful when filling the param array.

Code:
// this works void foo(int i, MyBase * a) { a->someFunc(i); } void bar() { foo(34.45f, new MyDerived()); } // this won't typedef std::vector<boost::any> param_array; void foo(const param_array& params) { if (params.size() < 2) throw some_exception(); boost::any_cast<MyBase*>(params[1])->boost::any_cast<int>(params[0]); } void bar() { param_array params; params.push_back(34.45f); params.push_back(new MyDerived()); foo (params); }
___________________________________________
C++ addict
-
Currently working on: the 3D engine for Tomb Raider: Underworld and Deus Ex 3.

Last edited by .oisyn : 11-26-2006 at 09:48 AM.
.oisyn is offline   Reply With Quote
Old 11-26-2006, 01:33 PM   #7
Nils Pipenbrinck
Senior Member
 
Nils Pipenbrinck's Avatar
 
Join Date: Sep 2005
Location: Hamburg / Germany
Posts: 597
Default Re: function argument caster

Hm. to me it looks like this code solves a problem that should not exist.
Nils Pipenbrinck is offline   Reply With Quote
Old 11-26-2006, 08:09 PM   #8
stodge
Valued Member
 
Join Date: May 2003
Location: Canada
Posts: 111
Default Re: function argument caster

Ah I see, that's why I do this:

Timer* timer= HB_GET_PARAMETER(params, Timer*, 0);

With a similar macro to push parameters.

I know it's not to everyone's taste but I quite like this.

___________________________________________
http://stodge.blogspot.com
stodge is offline   Reply With Quote
Old 11-27-2006, 04:04 AM   #9
.oisyn
DevMaster Staff
 
.oisyn's Avatar
 
Join Date: Sep 2005
Location: The Netherlands
Posts: 1,442
Default Re: function argument caster

How would that macro handle the conversions in my example? It can't, and that makes it a hassle to work with.
___________________________________________
C++ addict
-
Currently working on: the 3D engine for Tomb Raider: Underworld and Deus Ex 3.
.oisyn is offline   Reply With Quote
Old 11-27-2006, 05:07 AM   #10
bladder
DevMaster Staff
 
bladder's Avatar
 
Join Date: Sep 2003
Location: Hell
Posts: 1,109
Default Re: function argument caster

Quote:
Originally Posted by stodge
Too much boiler plate code? I don't understand what you mean. Also, how is this not flexible? I give the developer the ability to store whatever they want into the parameter list.

oh i didn't mean your code in not flexible, it just has too many "rules" you need to follow. I meant giving the client more flexibility with those rules. Cause that is what args_cast is supposed to do.

Quote:
Hm. to me it looks like this code solves a problem that should not exist.

Yeah I guess... but in todays work envionments where you're using code passed down through God only knows how many hands, you could come across a callback with a specific sig, and a function that can handle your situation that has a different signature. And suppose you change the signature to match, then you may have to change all invocations of said function. Or you can wrap the function in another object (as dave_ showed), but that produces clutter, or you can cast it.. but that adds to compile time.

At the end you have multiple solutions with their own advantages/disadvantages.
___________________________________________
- TripleBuffer
- Me blog
bladder is offline   Reply With Quote
Old 11-27-2006, 05:57 AM   #11
Jan
Member
 
Join Date: Sep 2005
Posts: 51
Default Re: function argument caster

Quote:
I had this situation a while back and I didn't feel like writing a whole new function to take into account the extra bool and int that I really had no car for at the time.

Writing a temporary function would have taken you a minute. Your function-caster looks so complicated, i bet you have had fun developing it more than a day.

I wouldn't wonna work with such code. Nobody, except you, knows what's going on. The most straight-forward solution is short, clear, typesafe and every c++ noob can understand it and see what's the problem in the first place.

But that's just my opinion.
Jan.
Jan is offline   Reply With Quote
Old 11-27-2006, 06:39 AM   #12
stodge
Valued Member
 
Join Date: May 2003
Location: Canada
Posts: 111
Default Re: function argument caster

Code:
#include "Reactor.h" #include "ICallable.h" #include "CoreMacros.h" #include "LinuxSignalHandler.h" #include <iostream> class First { public: First(int aa): age(aa) { std::cout << "First::First" << std::endl; }; virtual ~First() { std::cout << "First::~First" << std::endl; }; virtual void PrintName() { std::cout << "My name is first " << age << std::endl; }; protected: int age; }; class Second: public First { public: Second(int aa): First(aa) { std::cout << "Second::Second" << std::endl; }; virtual ~Second() { std::cout << "Second::~Second" << std::endl; }; virtual void PrintName() { std::cout << "My name is second" << age << std::endl; }; }; class MyTimer: public ICallable { public: void Hello(ParameterListType& params) { HB_TRACE_PUSH; std::cout << "Hello" << std::endl; First* first= HB_GET_PARAMETER(params, First*, 0); first->PrintName(); Second* second= HB_GET_PARAMETER(params, Second*, 1); second->PrintName(); Reactor::GetSingleton().Stop(); HB_TRACE_POP; }; void Hello2(ParameterListType& params) { HB_TRACE_PUSH; std::cout << "Hello2" << std::endl; Timer* timer= HB_GET_PARAMETER(params, Timer*, 0); timer->dump(); Reactor::GetSingleton().Cancel(timer); Reactor::GetSingleton().Stop(); HB_TRACE_POP; }; void Hello3(ParameterListType& params) { HB_TRACE_PUSH; std::cout << "Hello3" << std::endl; Reactor::GetSingleton().Stop(); HB_TRACE_POP; }; void Test(ParameterListType& params) { HB_TRACE_PUSH; std::cout << "Test" << std::endl; HB_TRACE_POP; }; }; void onExit(void) { std::cout << "onExit - exiting application cleanly" << std::endl; Reactor::GetSingleton().Dump(); } int main() { HB_TRACE_PUSH; Reactor* reactor = &Reactor::GetSingleton(); reactor->Initialise(); Reactor::SetTickTime(0.32f); MyTimer* my = new MyTimer(); reactor->SetDelay(10000); ParameterListType params1; ParameterListType params2; ParameterListType params3; ParameterListType params4; LinuxSignalHandler sig; sig.AddExitHandler(onExit); Timer* timer1 = reactor->Schedule(my, (FunctionPtrType)&MyTimer::Test, 2000, params1); if (timer1==NULL) { std::cout << "Timer 1 failed" << std::endl; } else { timer1->dump(); } Timer* timer2 = reactor->Schedule(my, (FunctionPtrType)&MyTimer::Test, 3000, params2); if (timer2==NULL) { std::cout << "Timer 2 failed" << std::endl; } else { timer2->dump(); } Timer* timer3 = reactor->Schedule(my, (FunctionPtrType)&MyTimer::Test, 4000, params3); if (timer3==NULL) { std::cout << "Timer 3 failed" << std::endl; } else { timer3->dump(); } First* first = new First(111); Second* second = new Second(222); HB_PUSH_PARAMETER(params4, first); HB_PUSH_PARAMETER(params4, second); Timer* stop = reactor->Schedule(my, (FunctionPtrType)&MyTimer::Hello, 8000, params4); if (stop==NULL) { std::cout << "Stop Timer failed" << std::endl; } else { stop->dump(); } reactor->Cancel(timer1); reactor->Cancel(timer2); reactor->Cancel(timer3); timer1 = timer2 = timer3 = NULL; reactor->Run(); std::cout << "Cancelling 1 " << timer1 << std::endl; reactor->Cancel(timer1); std::cout << "Cancelling 2 " << timer2 << std::endl; reactor->Cancel(timer2); std::cout << "Cancelling 3 " << timer3 << std::endl; reactor->Cancel(timer3); reactor->Shutdown(); HB_TRACE_POP; return 1;}

Gives:

Code:
**** Timer ID=0 Enabled=1 Expired=0 Timeout=2 Pointer=0x95e6560 **** Timer ID=1 Enabled=1 Expired=0 Timeout=3 Pointer=0x95e71e8 **** Timer ID=2 Enabled=1 Expired=0 Timeout=4 Pointer=0x95e7220 First::First First::First Second::Second **** Timer ID=3 Enabled=1 Expired=0 Timeout=8 Pointer=0x95e7298 Hello My name is first 111 My name is second222 Cancelling 1 0 Cancelling 2 0 Cancelling 3 0 onExit - exiting application cleanly

Which I *think* is what you're implying. Correct me if I'm not understanding your example fully.
___________________________________________
http://stodge.blogspot.com
stodge is offline   Reply With Quote
Old 11-27-2006, 07:09 AM   #13
.oisyn
DevMaster Staff
 
.oisyn's Avatar
 
Join Date: Sep 2005
Location: The Netherlands
Posts: 1,442
Default Re: function argument caster

Jesus why must you make your example so complex

I'll strip it down to the bare essentials:
Code:
void Hello(ParameterListType& params) { First* first= HB_GET_PARAMETER(params, First*, 0); first->PrintName(); Second* second= HB_GET_PARAMETER(params, Second*, 1); second->PrintName(); } int main() { First* first = new First(111); Second* second = new Second(222); ParameterListType params4; HB_PUSH_PARAMETER(params4, first); HB_PUSH_PARAMETER(params4, second); Hello(params4); }
Of course that is going to work. But what if you push 'second' the first time as well? Because in C++ it's perfectly legal to do this:
Code:
void Hello(First * first, Second * second) { // ... } int main() { First * first = new First; Second * second = new Second; Hello(second, second); }
But if you push 'second' the first time, the first entry in the param array will be of type Second* and not of type First*. Retrieving the pointer as a First* using boost::any_cast (I suspect that's what you're doing internally, but even if you're not, it isn't possible to detect it and do the conversion at runtime) will throw an exception, because the first entry isn't a First* but a Second*.
___________________________________________
C++ addict
-
Currently working on: the 3D engine for Tomb Raider: Underworld and Deus Ex 3.
.oisyn is offline   Reply With Quote
Old 11-27-2006, 08:20 PM   #14
juhnu
Valued Member
 
Join Date: Aug 2005
Location: Seoul
Posts: 272
Default Re: function argument caster

nice, but slightly over-engineered..
juhnu is offline   Reply With Quote
Old 11-28-2006, 03:50 AM   #15
bladder
DevMaster Staff
 
bladder's Avatar
 
Join Date: Sep 2003
Location: Hell
Posts: 1,109
Default Re: function argument caster

It actually didnt take more then a few hours to develop (and that also because of the repetative typing). It's just that it has a lot of mechanical code that's why it looks complex, but if you strip it down to it's basic structures it's not really complex at all. It uses some very simple template tricks, but because of the repetativeness it looks bloated. If I wrote the templates with boost.preprocessor it's probably be like 50 lines long or something max.

As for no-one but me knowing: most of the boost libraries are the same, everyone's using those though.

btw: args_cast also works wonders with lambda (because of it's placeholder limitation) , especially when writing lambda functions for callbacks.

But your opinion is your opinion
___________________________________________
- TripleBuffer
- Me blog
bladder is offline   Reply With Quote
Old 11-30-2006, 10:25 AM   #16
kusma
Valued Member
 
Join Date: Aug 2005
Posts: 162
Default Re: function argument caster

Quote:
Originally Posted by bladder
As for no-one but me knowing: most of the boost libraries are the same, everyone's using those though.

Actually, "everyone" is not using boost. I stay away as much as possible, and most of the people I work with does too.
kusma is offline   Reply With Quote
Old 12-01-2006, 02:56 AM   #17
dave_
Senior Member
 
Join Date: Sep 2005
Location: Brighton, UK
Posts: 584
Default Re: function argument caster

Quote:
Originally Posted by kusma
Actually, "everyone" is not using boost. I stay away as much as possible, and most of the people I work with does too.
Good thing the C++ standards board aren't like you!
dave_ is offline   Reply With Quote
Old 12-01-2006, 04:17 AM   #18
.oisyn
DevMaster Staff
 
.oisyn's Avatar
 
Join Date: Sep 2005
Location: The Netherlands
Posts: 1,442
Default Re: function argument caster

Quote:
Originally Posted by kusma
Actually, "everyone" is not using boost. I stay away as much as possible, and most of the people I work with does too.
Then args_cast is probably not for you, so you don't need to know. You probably avoid the iostreams as well, because FILEs handle the job perfectly, am I right?

As I said before, every self-respectable C++ programmer has boost installed. What that implies is up to you
___________________________________________
C++ addict
-
Currently working on: the 3D engine for Tomb Raider: Underworld and Deus Ex 3.

Last edited by .oisyn : 12-01-2006 at 04:23 AM.
.oisyn is offline   Reply With Quote
Old 12-01-2006, 10:27 PM   #19
juhnu
Valued Member
 
Join Date: Aug 2005
Location: Seoul
Posts: 272
Default Re: function argument caster

Quote:
Originally Posted by .oisyn
As I said before, every self-respectable C++ programmer has boost installed. What that implies is up to you

I don't.

Unless the Boost-libraries become part the official standard, I don't see how to justify their use in the real production environments.

I respect Boost-project and all the people involved, that they try to develop C++ further as a language by establishing new coding practises and technologies, but it's still a moving field and so there are some problems involved.

For a homegrown project it might be fun to try out new technologies and all cool trickeries, but the priorities are considerably different in professional projects, where you actually have to deliver quality results and be able to share your code with other team members. Locking down an imporant project to some certain version of Boost would be needed in order to avoid regression bugs, but in the long run that would generate more problems than it solves. It's still a research project and existing practises and code conventions are bound to change - also not all people are familiar with __third__-party template libraries and things like code simplicity and easy (long-term) maintenance are of utmost importance. Somehow I feel Boost in some parts, and the function argument caster in question, have answers for problems, which shouldn't even exists in the first place.

There are many who think themselves as hardcore C++ programmers and are using all kind of short-hand notations and "cool" tricks and thinking it's that what makes them good programmers - but are they really?

Since when programming became an obfuscation contest?
juhnu is offline   Reply With Quote
Old 12-02-2006, 06:07 AM   #20
bladder
DevMaster Staff
 
bladder's Avatar
 
Join Date: Sep 2003
Location: Hell
Posts: 1,109
Default Re: function argument caster

Quote:
Originally Posted by juhnu
Unless the Boost-libraries become part the official standard, I don't see how to justify their use in the real production environments.

If you restrict yourself to only the standard when it comes to c++ and are afraid of version lockdown then you're pretty screwed in a "real" production environment.


Quote:
Originally Posted by juhnu
Since when programming became an obfuscation contest?

well we are dealing with c++ here

c++ as a language has quite a few problems. boost is there because of those problems.

as for your other question, of course being a "hardcore" c++ user doesnt make you a good programmer... but what's your point? That efforst like boost are not significant?
___________________________________________
- TripleBuffer
- Me blog

Last edited by bladder : 12-02-2006 at 06:12 AM.
bladder is offline   Reply With Quote
Old 12-02-2006, 09:55 AM   #21
.oisyn
DevMaster Staff
 
.oisyn's Avatar
 
Join Date: Sep 2005
Location: The Netherlands
Posts: 1,442
Default Re: function argument caster

Quote:
Originally Posted by juhnu
I don't.

Unless the Boost-libraries become part the official standard, I don't see how to justify their use in the real production environments.
Actually, the boost sublibraries are first-class citizens when it comes to new standard c++ functionality. For example, boost::function and boost::bind are already in the C++'s technical report for library extensions (and will probably make it to C++0x)

Quote:
but the priorities are considerably different in professional projects, where you actually have to deliver quality results and be able to share your code with other team members.
I'm sorry but that's just bull. Boost is being used in lots of professional projects, and we use it as well. By saying it's just a hobbyist's thing you really don't give boost enough credit.

Quote:
There are many who think themselves as hardcore C++ programmers and are using all kind of short-hand notations and "cool" tricks and thinking it's that what makes them good programmers - but are they really?
I really have no clue what this has got to do with our current discussion . The point was that the code was unreadable, but I don't understand why you need to be reading the code in the first place - it's the interface documentation that counts. You (and I don't mean _you_ but merely people in general) probably won't be reading your vendor's implementation of std::map either, nor the streambuf implementation that's responsible for writing to stdout, yet you're using it anyway.
___________________________________________
C++ addict
-
Currently working on: the 3D engine for Tomb Raider: Underworld and Deus Ex 3.
.oisyn is offline   Reply With Quote
Old 12-02-2006, 10:04 AM   #22
Nils Pipenbrinck
Senior Member
 
Nils Pipenbrinck's Avatar
 
Join Date: Sep 2005
Location: Hamburg / Germany
Posts: 597
Default Re: function argument caster

Hm.. my thoughs about boost:

A nice and handy tool to do things fast, but there are problems involved when you write code for other people:

You force them into a library which they don't nessesarily know. And you can hardly do a code coverage analysis anymore. Or better said: You can of cause run the analysis, but you'll never get close to 80% coverage. That might be not a problem for games, but for other fields it is.
Nils Pipenbrinck is offline   Reply With Quote
Old 12-02-2006, 10:41 AM   #23
.oisyn
DevMaster Staff
 
.oisyn's Avatar
 
Join Date: Sep 2005
Location: The Netherlands
Posts: 1,442
Default Re: function argument caster

That counts for any library, even the standard C++ library.
___________________________________________
C++ addict
-
Currently working on: the 3D engine for Tomb Raider: Underworld and Deus Ex 3.
.oisyn is offline   Reply With Quote
Old 12-02-2006, 10:53 PM   #24
juhnu
Valued Member
 
Join Date: Aug 2005
Location: Seoul
Posts: 272
Default Re: function argument caster

Quote:
Originally Posted by .oisyn
Actually, the boost sublibraries are first-class citizens when it comes to new standard c++ functionality. For example, boost::function and boost::bind are already in the C++'s technical report for library extensions (and will probably make it to C++0x)

Yeah I'm aware of that, and hence said *until* they become part of the standard. Being part of the standard is important, as it greatly reduces problems associated with the version lockdown, compatibility issues and integration costs. People are more likely to get educated to use and know the standard library than third party solutions.

This doesn't mean I don't respect Boost or other template-based framework projects. I do appreciate them a lot and I think they're changing the way we are looking at C++ as a language. There are lots of useful things in Boost and admittedly if didn't need to share code with lots of people(with varying skill levels) I would be more positive about it.

That said, however, I would rather see some features of Boost incorporated in the language itself, such as anonymous functions(vs Boost Lambda).

Quote:
Originally Posted by Bladder
c++ as a language has quite a few problems. boost is there because of those problems.
Exactly.
juhnu is offline   Reply With Quote
Old 12-03-2006, 06:19 AM   #25
.oisyn
DevMaster Staff
 
.oisyn's Avatar
 
Join Date: Sep 2005
Location: The Netherlands
Posts: 1,442
Default Re: function argument caster

Actually I think we totally agree with eachother
___________________________________________
C++ addict
-
Currently working on: the 3D engine for Tomb Raider: Underworld and Deus Ex 3.
.oisyn is offline   Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Forum Jump


All times are GMT -7. The time now is 08:31 PM.


Powered by vBulletin
Copyright ©2000 - 2009, Jelsoft Enterprises Ltd.