Update: I've Boost-preprocessorized the library, so it now works for arbitraryish arity, where arbitrarish means "up to some preprocessor-defined limit which is currently 10." The process was... better than I imagined but still ugly. I'd like to set up generation of a header version with the Boost PP iteration already applied so that at least it doesn't suck to look at when you're not iterating, but who am I kidding, I haven't even written a build script yet. And also because g++ ... test.cc -E
currently takes over 10 seconds to run on my (admittedly low-powered) computer, which is pretty ridiculous.
For some lolz look at this file
from Boost's preprocessor library. I'll leave it to your imagination as to how they use that to define a BOOST_PP_SUB macro that subtracts two numbers. I am not sure, but I kind of think it can't be good.
Anyway, what I'm working on is a library that gives you a normal C function pointer that calls a std::function. I use libffi (said 3rd party API) to dynamically allocate a thunk that wraps around the functor (actually it wraps around another function that wraps around the functor) which then becomes your function pointer.
With lifetime managed by the object that owns the function. Cute.
Mildly interesting fact: Actually all of the objects stored as fields in the CCallableClosure need to live that long, including the argument type vector. At least that's what's suggested by, uh, experimental evidence.
I might suggest that you should refactor the "call a function on an array of arguments" away from the rest of binder?
I could probably do that, though I'm not exactly clear what you want.
Oh, and you probably do not need to use an actual std::function -- forcing that level of redirection when you are trying to bind a lambda seems inelegant.
You'll want to extract the arguments and return value of anything passed in that has an operator(), and store any such type in your CallableClosure. Then you'll avoid an extra indirection in many cases!
I'd like to support more than just std::functions, but I don't think I care enough about the extra indirection to do more than add a new constructor that implicitly converts other callables to a std::function. The assumption that the backing functor is a std::function is fairly ingrained, and in fact I just made it a little moreso, because I sort of pass it around as a handle on the function type. (Is there a way of going from std::function<r (args...)>
to r (args...)
, or would I have to write that or some traits classes? That would make things a bit easier for that conversion.)
It's not like there aren't already several layers interposed -- libffi and the binder each add two calls, so another two calls [what GCC's std::function will give you] from std::function isn't like it's adding a bunch of overhead where there wasn't before. And in fact if you optimize, one of the ones belonging to std::function goes away, which means that there are 5 middlemen on the call stack, only one of which is due to std::function. (Edit
: Oh, not to mention, the impression I have (which could totally be wrong) is that libffi's wrappers are rather thick ones -- a lot more than just pass through stuff. It has to walk over the array of argument types while it walks down the stack and puts the address of each argument into the args array.)
Ben-oni wrote:Supposedly the sizes are different in ARM. At the very least, you should assert that the size of the pointers are the same.
Good idea. I'll add that in in a future version.