This is neat, I had been hankering to lambda-ize various things, but hadn't worked through what the allocation would looked like in practice. Do you know if there's a reason the standard is defined so as to not let us override the reserved size of a std::function? Are we taking any kind of hit by doing this? John P.S. I have just gone on Amazon to order a C++11 book because I need to start using more than just lambda, auto, and pretty for loops ;-) On Wed, Sep 16, 2015 at 8:06 PM, Adam C. Emerson <aemerson@xxxxxxxxxx> wrote: > On 09/16/2015 02:31 PM, Gregory Farnum wrote: >> Can you provide a little background (links or text) for those of us >> who aren't up on C++11x/14x? I looked at them briefly but having only >> the vaguest idea about some of them quickly got lost. :) > > Surely, sir. > > So, in summary, Context* is the means we have been using to handle > callbacks. It has two big problems: > > (1) Every Context is allocated at the point of use and freed when it's > called. So by their nature they make heavy use of the heap. (If you > overload complete there's a few exceptions, but by and large Context is > very heapy.) > > (2) Context accepts an int. That's it. You can get around this by > storing other things in it and passing pointers to it and so forth. But > it would be nice to have more variety of type in our callbacks. > > C++11 (and following) have a whole pile of things that can be used as > functions. There are function objects (objects with an operator() > defined on them), lambdas with variable capture, function member > pointers, reference wrappers pointing to function objects and the like. > This gives you a good bit of flexibility, allows things like variable > capture, let's you allocate one object once and just pass references to > it, and so forth. > > Now, these objects all have different sizes and different types. So you > can't just shove them in an object naively. Because a class one of whose > members is a function pointer is going to be a different type than a > class holding a function object. (Which itself is different to a type > holding a reference to a function object.) > > std::function exists as a solution to this problem. It provides a > uniform type that can hold any object satisfying the requirement of > being callable with given argument types and a given return value. So, > for example, if you have some 'stat'-like call, you could specify it as > taking a function taking an error code, a size, and a date. Anything > that can be called with such would be accepted, anything that can't > (wrong argument types, say) would be rejected at compile time. And it > could be uniformly stored in a list of Operations. > > The downside is that if the thing you provide is too big, std::function > will allocate. 'too big' depends on your library vendor and there's no > way to find it out what. Thus the purpose of the ceph::function class. > If we have a pretty good idea how large most of the callback function > objects we expect to hold are, we can tell it to statically allocate > that much space. This gives us a tool to get allocations out of our fast > path. (For example, if we preallocate a bunch of classes with a > ceph::function that preallocates enough space to hold likely callbacks, > we can just pick them off and have no allocations, in the usual case.) > > If we know /everything/ we'll ever get, we can disable allocations > entirely. This is mostly so we can catch situations where we're trying > to shove something unexpectedly huge somewhere it ought not go. An > internal sanity check. > > Now, ceph::function, like std::function, is still an abstraction with a > virtual call in it and because it copies things around it reduces > opportunities for inlining. Thus, if you aren't storing a callback on an > object that's supposed to be in a queue, you shouldn't use it. You > should do something like: > > template<typename Fun> > void do_stuff(Fun&& f) { > ...; > f(some, values); > }; > > This allows inlining, and (based on my experiments trying multiple > implementations and looking at the generated assembly) if f is called in > a loop, the code is just as good as if you'd open-coded the loop and > written the body of 'f' in it. Functions like this can have the > potential to use closures (lambda expression) effectively for free. > > So, the summary is: > > Context* is very heapy. We have less heapy alternatives. This implements > a foundation for one of them. We also get a bit more flexibility. > > Thank you. > -- > To unsubscribe from this list: send the line "unsubscribe ceph-devel" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe ceph-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html