Is it possible to buffer/delay function calls?

Started by
47 comments, last by ZealousEngine 16 years, 11 months ago
Quick followup question - There arent any real 'negatives' to functors/boost::bind are there? I mean, I know I can expect to see a LITTLE extra overhead, but should I be concerned about anything inparticular?

I plan on using these functors mainly to indirectly modify my render engines 'scene state'. For example, in my main/logic thread, I will be doing a lot of 'commandList.push_back( &SceneMgrType::setPosition, &mySceneMgr, objectId, Position )', then passing that commandList to my render thread, where the scene state (object positions, ect...) will be modified (when its safe).

That should be ok, right?
Advertisement
You may experience longer compile / linker times, because boost massively relies on metaprogramming / templates, especially the boost function implementation.
Depending on how extensively you are using boost and what the size of your team / project is, this possible drawback might be of great or no importance for you.
Quote:Quick followup question - There arent any real 'negatives' to functors/boost::bind are there? I mean, I know I can expect to see a LITTLE extra overhead, but should I be concerned about anything inparticular?

I plan on using these functors mainly to indirectly modify my render engines 'scene state'. For example, in my main/logic thread, I will be doing a lot of 'commandList.push_back( &SceneMgrType::setPosition, &mySceneMgr, objectId, Position )', then passing that commandList to my render thread, where the scene state (object positions, ect...) will be modified (when its safe).

That should be ok, right?


Theres a page in the documentation for boost.function that discuses the performance, to sum it up in the worst case you'll end up with the equivelent of a single virtual function call.

One thing to watch out for with boost.bind is passing values by reference or binding values that are expensive to copy, boost.bind takes its arguments by value so if you bind to a function that takes arguments by reference then a reference to the copy made by boost.bind will be passed rather then a reference to the origonal object, to actualy pass by reference/const reference you need to wrap the argument with boost.ref/boost.cref. Because of this passing something thats expensive to copy without wrapping it might result in performance issues due to the extra copies that are made.
Quote:Original post by ZealousEngine
Quick followup question - There arent any real 'negatives' to functors/boost::bind are there? I mean, I know I can expect to see a LITTLE extra overhead, but should I be concerned about anything inparticular?


The three costs I can call to mind are:
1) Possible extra indirection hit (minor cost, boost::function only)
2) Possible extra virtual call hit (minor cost, boost::function only)
3) Minor compile time hit

You can guard yourself against all of these by providing yourself with the option of replacing them with your own, specialized equivilants if seriously needed, which is as simple as wrapping up the various push_back()s in helper functions if you find yourself scattering them all over the place. Doing it per frame for individual vertex data would be problematic, doing it once per frame per object in a modern RTS probably wouldn't, and you're definately in the clear if you're dealing with batched updates (e.g. setPositions).
Ok cool.

Well I got 'non return' functions to work, but now im wondering how to get the return value.

For example, if 'foo()' returns a int, what am I doing wrong here...

std::vector< boost::function<void()> > cmdList;
cmdList.push_back( boost::bind( &ObjType::foo, &myObj ) );

//why doesnt this work?
int val = cmdList[0]();
std::vector< boost::function<int()> > cmdList;cmdList.push_back( boost::bind( &ObjType::foo, &myObj ) );//why doesnt this work? works now.int val = cmdList[0]();


Similarly, if you want to add parameters which you specify when you call the cmdList, you can use a combination of signature changes and placeholders:

std::vector< boost::function<int(int)> > cmdList;cmdList.push_back( boost::bind( &ObjType::foo, &myObj , 12 , _1 ) ); //assuming foo takes two int parametersint val = cmdList[0](24); //calls myObj.foo(12,24); -- the 24 is placed where the _1 was in the bind() statement.


Note that the function signature: int(int) -- matches how we're calling it here (passing 1 int and returning another).
Hmm so I have to hard code the return type into the command list? Dang that means I can only have one 'type' of function PER command list right?

Is there no way to have a command list that can call foo() (which returns a int), AND goo() ( which returns void )?
Quote:Hmm so I have to hard code the return type into the command list? Dang that means I can only have one 'type' of function PER command list right?

Is there no way to have a command list that can call foo() (which returns a int), AND goo() ( which returns void )?


It depends, if you want acess to the return types you could try boost.any or boost.variant but once you store them in the list you have no way of knowing what the function will return so I would try to avoid it. Why exactly do you want to get the return value and be able to store functions with different unknown return value in one list, theres probally a better alternative.

(okay I admit you can find out the return type with boost::any or use visitors with boost::variant but I'd still say its a bad idea).
Well I guess youre right, it would be messy.

Im just thinking it would be nice if, when adding a function call to the command list, I could also speicfy a pointer back to where I want the return value stored. So, later, when the function is called, it automatically writes the return value to wherever I specified. I guess thats a pretty specific request, so I wouldnt be surprised if its not built into bind.

I guess if thats not possible, I can just create a command list for 'void returns', another command list for 'int returns', ect...

*About the command lists - Is it ok to use a WIDE range of functions in a single command list? Like for my 'void return' command list, is it ok (perofrmance wise) to have functions that set the cameras position, along side functions that hide/show objects, ect... They are all the same in essence (the dont return anything) but they all take a varrying amount of arguments.
Quote:*About the command lists - Is it ok to use a WIDE range of functions in a single command list? Like for my 'void return' command list, is it ok (perofrmance wise) to have functions that set the cameras position, along side functions that hide/show objects, ect... They are all the same in essence (the dont return anything) but they all take a varrying amount of arguments.


There shouldnt be any performance problems as far as I know, the only thing you really need to wory about is that you have enough space to store all of the arguments to the functions untill there called and removed from the command list.

Quote:Im just thinking it would be nice if, when adding a function call to the command list, I could also speicfy a pointer back to where I want the return value stored. So, later, when the function is called, it automatically writes the return value to wherever I specified. I guess thats a pretty specific request, so I wouldnt be surprised if its not built into bind.

I guess if thats not possible, I can just create a command list for 'void returns', another command list for 'int returns', ect...


Now that is alot easier to do :).

int main(){    // boost.lambda lets you compose functions on the fly    // just wrap all variables you want to use with boost::lambda::var    // and boost.lambda overloads all the operators to let you make the     // functions    using namespace boost::lambda;    std::vector< boost::function<void ()> > cmd_list;    my_class result;    // my_class my_function(int);    // creates a function that calls my_funtion(1) and stores the result in result    cmd_list.push_back(var(result) = boost::bind(my_function, 1));    // ...    cmd_list[0]();    // result now holds the result of my_function    // more complicated example of what boost.lambda can do    // creates and stores a functor that will print the value of result    // then call result.do_stuff then print the new value.    cmd_list.push_back(    (        std::cout << constant("Value of result before calling result.do_stuff(): ") << var(result),         boost::bind(my_class::do_stuff, &result),        std::cout << constant("Value after calling result.do_stuff()") << var(result)    ));    // does all the std::cout'ing etc above    cmd_list[1]();}


You might want to profile and potentially right a wrapper functor if your doing that lots thought because boost.lambda can result in ~10% speed hit over a hand written functor. (although I think that applies mainly to more complex stuff)

Edit:
boost.lambda
boost.lambda performance tests

[Edited by - Julian90 on May 8, 2007 7:30:30 AM]

This topic is closed to new replies.

Advertisement