// member functions
boost::function<int (a* obj_ptr, float f)> f2;
f2 = &a::afunc;
a a_obj;
printf("\nf2 return: %d\n", f2(&a_obj, 10.3546f) );
// trying member functions with a void*
boost::function<int (void* obj_ptr, float f)> f3;
f3 = &a::afunc;
a another_obj;
printf("\nf3 return: %d\n", f3(&another_obj, 10.3546f) );
🔒 Locked
🎲 GameMaker
boost::function with anonymous object types
Advertisement
Original Post
I'm using boost::function for the in-engine debug console that I have had so many design problems with over the past couple of weeks. I need to be able to register functions and call them, and I have decided upon boost::function because it seems like the best thing for it. The only problem is that it won't work when you try and put the object pointer in as a void*. The first works, the second gives me a nice 110 line error which as far as I can tell, means "it doesn't work." This is going to turn out to be a problem as I'm going to need to be able to add member functions for different types of objects. I'd consider using a variant, except for the fact that whenever you want to add a function from a new type of object, you have to add that type to the variant template list and re-compile everything. Any ideas?
Boost.Bind?
boost::function< int (float) > function;
a a_obj;
function = boost::bind(&a::afunc, boost::ref(a_obj), _1);
std::cout << "\nfunction return: " << function(10.3546f);
b b_obj;
function = boost::bind(&b::bfunc, boost::ref(b_obj), _1);
std::cout << "\nfunction return: " << function(10.3546f);
Σnigma
boost::function< int (float) > function;
a a_obj;
function = boost::bind(&a::afunc, boost::ref(a_obj), _1);
std::cout << "\nfunction return: " << function(10.3546f);
b b_obj;
function = boost::bind(&b::bfunc, boost::ref(b_obj), _1);
std::cout << "\nfunction return: " << function(10.3546f);
Σnigma
Okay, hang on a second.
I'm having one of those "what the hell am I doing" moments (probably copyrighted by Mythbusters).
if boost::function and boost::function are different types of objects, how will I go about storing them all in a single array or list without doing something like attempting to keep them all as boost::variant?
I'm having one of those "what the hell am I doing" moments (probably copyrighted by Mythbusters).
if boost::function
Enigma: The function assigment operator already conveniently uses bind when appropriate, no problem there.
Endar: It's probably just too type-unsafe for the developers to consider supporting void pointers like that. Ugly things might happen if you accidentally called such a function with an object of a wrong type.
EDIT: You don't. You'd encapsulate the arguments and return value(s) into some generic scaffolding (say, a vector of variants), or, as I have done, pass them as text streams. My own console implementation has something like:
boost::function
where Input and Output are template arguments that are required to support stream extraction and insertion the way iostreams do. They default to argstream and ostream, the former being a light wrapper for istream with some convenience features.
Endar: It's probably just too type-unsafe for the developers to consider supporting void pointers like that. Ugly things might happen if you accidentally called such a function with an object of a wrong type.
EDIT: You don't. You'd encapsulate the arguments and return value(s) into some generic scaffolding (say, a vector of variants), or, as I have done, pass them as text streams. My own console implementation has something like:
boost::function
where Input and Output are template arguments that are required to support stream extraction and insertion the way iostreams do. They default to argstream and ostream, the former being a light wrapper for istream with some convenience features.
Quote:
Original post by Sharlin
Enigma: The function assigment operator already conveniently uses bind when appropriate, no problem there.
I think you missed the point of my post. To my knowledge you cannot bind a variable to a function, and thus produce a function object whose type does not depend on the type of that variable, without an explicit call to bind (or another binding function like std::bind1st). I was using bind solely to remove the problematic unknown type (void *) parameter from the function signature.
Endar: What exactly is it that you are trying to do?
Sharlin: Well, having the return and the arguments as boost::variants, since the rest of my console is using variants, that is the best way, but I was thinking more along the lines of how do I keep a bunch of function objects that I can use for calling member functions? How am I going to deal with the pointer to the object?
Or am I not quite getting what you were talking about?
...
Are you saying that Output will contain the return value as a variant and Input is a vector/array of variants that contain the function arguments? The first of which being a pointer to an object?
If so, whenever you want to add a new object type, won't you have to add another type to the variant template list and recompile everything?
Engima: I want to be able to call an arbitrary function from my in-engine console. Obviously the function is going to have to be registered before-hand. But in regards to member functions, I don't want to have to physically add a pointer to a new object type the first time I want to call a function for a new object type. I just want to have a single line of code such as:
Something like that, or perhaps I'll have to create a boost::function object before I call 'registerFunction', but I don't want to have to find a boost::variant declaration in a file and add 'Engine*' to its template param list.
Or am I not quite getting what you were talking about?
...
Are you saying that Output will contain the return value as a variant and Input is a vector/array of variants that contain the function arguments? The first of which being a pointer to an object?
If so, whenever you want to add a new object type, won't you have to add another type to the variant template list and recompile everything?
Engima: I want to be able to call an arbitrary function from my in-engine console. Obviously the function is going to have to be registered before-hand. But in regards to member functions, I don't want to have to physically add a pointer to a new object type the first time I want to call a function for a new object type. I just want to have a single line of code such as:
console->registerFunction<Engine*>(pointer_to_engine_object, params);Something like that, or perhaps I'll have to create a boost::function object before I call 'registerFunction', but I don't want to have to find a boost::variant declaration in a file and add 'Engine*' to its template param list.
Ah, then you can do just like Enigma said in his first post. If you have the this object at hand when you register a new function, you can just bind it right there.
result foo(params) { } // a free functionstruct bar1 { result f(params) { } }; // a non-static member functionstruct bar2 { result f(params) { } }; // a non-static member functionstruct baz { result operator ()(params) { } }; // a function object...vector<function<result (params)> > functions;functions.push_back(foo);bar1* b1;bar2* b2;functions.push_back(bind(&bar::f, b1, _1));functions.push_back(bind(&bar::f, b2, _1));functions.push_back(baz());
But they can certainly contain a base type, or a constant type which contains a base type for the arbitrarily templatized functions.
In my C++ version of this sort of thing it actually goes [psuedo class code]:
A bit messy, and I'm pretty certain with more black magic it could be streamlined quite a bit. My C# version is far cleaner and useful.
Hrm. Looking back that might not be very helpful at all. Meh, questions?
In my C++ version of this sort of thing it actually goes [psuedo class code]:
struct TriggerInterface{ virtual void trigger()=0; // note, mine doesn't need return values // just one more indirection if you do. virtual ~TriggerInterface(){}}template <typename F>struct Trigger: public TriggerInterface{ // Store function F, and do de-serialization into args // Something like this [using external Template Type Info class]: // shared_ptr<typename F::arg1_type> a1(def<typename F::arg1_type>().unserialize(anyargs[0].c_str())); // The shared_ptr ensures destruction after trigger, and since F // is a boost::function object, the type info is held in // F::argX_type}// 1 trigger for each arity.template <typename F, int ARGS>struct TriggerGenerationInterface{ TriggerInterface *trigger; ctor(F function, vector<string> args){ trigger=new... // Create proper class based on template argument. }}template <typename F>struct TriggerGenerator<F,1>{ ...}template <typename F>struct TriggerGenerator<F,2>{ ... and so on.}template <typename F> // where F is a boost::function<...> objectstruct ArgAccumulator: public TriggerInterface{ F function; vector<string> args; // functions to manipulate variables. void Trigger(){ // F is a boost::function object, so it contains arity info: TriggerGenerator<F,F::arity> built_function(function,args); built_function.trigger(); }}class BaseCmd{ string Name; virtual void Parse(string)=0; // Helper functions} template <typename F>class Cmd: public BaseCmd{ ArgAccumulator<F> Scratch; void Parse(string CmdLine){ // // Common code to clear the arg accumulator, // pick out arguments and accumulate them. // // If parsing is successful, trigger the accumulated // function/arg group. // }}class CmdSet{ hash_map<string, BaseCmd> Commands; // functions to manipulate hash_map, and to parse // the first word for CmdLine delegation to the // BaseCmd.}// Usage:CmdSet ConsoleHandler=new CmdSet();ConsoleHandler.Add("quit", boost::function<void()>(QuitGame));ConsoleHandler.Add("volume", boost:function<void(float)>(boost::bind( &sound_manager::SetVolume, SoundManager,_2)));ConsoleHandler.Parse("volume .75"); // would set the volume to 75%ConsoleHandler.Parse("foo"); // nothing, not in the hash_mapA bit messy, and I'm pretty certain with more black magic it could be streamlined quite a bit. My C# version is far cleaner and useful.
Hrm. Looking back that might not be very helpful at all. Meh, questions?
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement