Proposal - non allocating std::function

1225 views
Skip to first unread message

Carl Cook

unread,
Jul 13, 2016, 6:47:44 PM7/13/16
to ISO C++ Standard - Future Proposals
Hi all,

This is a cross posting from the SG14 reflector, regarding a draft proposal some of us are working on.

Very briefly, some of us are interested in having a standardized function which does not incur an upfront memory allocation to store the callable target, instead opting for a compile time specified buffer (stack allocated). This has been quite useful in several low latency domains such as gaming and electronic trading. In other words, a drop-in replacement for std::function, where all data is on the stack.



Please feel free to comment here or in the SG14 reflector. This is still very much in the draft stage, and all feedback is appreciated. The reference implementation is still a work in progress (https://github.com/WG21-SG14/SG14/blob/master/SG14/inplace_function.h).

Many thanks,
Carl

Nevin Liber

unread,
Jul 13, 2016, 6:55:07 PM7/13/16
to std-pr...@isocpp.org
On 13 July 2016 at 17:47, Carl Cook <carl...@gmail.com> wrote:
Very briefly, some of us are interested in having a standardized function which does not incur an upfront memory allocation to store the callable target, instead opting for a compile time specified buffer (stack allocated).

I don't think you mean stack allocated; I think you mean the space is embedded in the object (aka the small object optimization).  If the object is allocated on the heap, the space will also be in the heap, correct?
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com>  +1-847-691-1404

Carl Cook

unread,
Jul 13, 2016, 6:58:33 PM7/13/16
to std-pr...@isocpp.org
On 14 July 2016 at 00:54, Nevin Liber <ne...@eviloverlord.com> wrote:
I don't think you mean stack allocated; I think you mean the space is embedded in the object (aka the small object optimization).  If the object is allocated on the heap, the space will also be in the heap, correct?

100% correct, thanks. I'll fix this up now. 

Thiago Macieira

unread,
Jul 13, 2016, 7:22:44 PM7/13/16
to std-pr...@isocpp.org
Em quarta-feira, 13 de julho de 2016, às 15:47:44 PDT, Carl Cook escreveu:
> Hi all,
>
> This is a cross posting from the SG14 reflector, regarding a draft proposal
> some of us are working on.
>
> Very briefly, some of us are interested in having a standardized function
> which does not incur an upfront memory allocation to store the callable
> target, instead opting for a compile time specified buffer (stack
> allocated). This has been quite useful in several low latency domains such
> as gaming and electronic trading. In other words, a drop-in replacement for
> std::function, where all data is on the stack.
>
> The original post is
> here:
> https://groups.google.com/a/isocpp.org/d/topic/sg14/1Sw_qEdIYes/discussion
>
> The resultant draft proposal is
> here:
> https://github.com/WG21-SG14/SG14/blob/master/Docs/Proposals/NonAllocatingS
> tandardFunction.pdf

Hi Carl

Can you explain somewhere why std::function is unable to do that?

In other words, why must std::function allocate memory?

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Ville Voutilainen

unread,
Jul 13, 2016, 7:26:23 PM7/13/16
to ISO C++ Standard - Future Proposals
On 14 July 2016 at 02:22, Thiago Macieira <thi...@macieira.org> wrote:
> Em quarta-feira, 13 de julho de 2016, às 15:47:44 PDT, Carl Cook escreveu:
>> Hi all,
>>
>> This is a cross posting from the SG14 reflector, regarding a draft proposal
>> some of us are working on.
>>
>> Very briefly, some of us are interested in having a standardized function
>> which does not incur an upfront memory allocation to store the callable
>> target, instead opting for a compile time specified buffer (stack
>> allocated). This has been quite useful in several low latency domains such
>> as gaming and electronic trading. In other words, a drop-in replacement for
>> std::function, where all data is on the stack.
>>
>> The original post is
>> here:
>> https://groups.google.com/a/isocpp.org/d/topic/sg14/1Sw_qEdIYes/discussion
>>
>> The resultant draft proposal is
>> here:
>> https://github.com/WG21-SG14/SG14/blob/master/Docs/Proposals/NonAllocatingS
>> tandardFunction.pdf
>
> Hi Carl
>
> Can you explain somewhere why std::function is unable to do that?
>
> In other words, why must std::function allocate memory?


Because it does type erasure and can hold a functor of any size, and
can be assigned to from another functor
of any size. Presumably this new type would fail to initialize if a
functor larger than the internal buffer is used
for initialization or assignment.

D. B.

unread,
Jul 13, 2016, 7:26:33 PM7/13/16
to std-pr...@isocpp.org
On Thu, Jul 14, 2016 at 12:22 AM, Thiago Macieira <thi...@macieira.org> wrote:
Hi Carl

Can you explain somewhere why std::function is unable to do that?

In other words, why must std::function allocate memory?

I think the issue is not that it "must allocate", rather that it's not required to embed and provides no way for the user to ensure embedding, and typical implementations don't provide enough in-object space for Carl and co's cases.

Thiago Macieira

unread,
Jul 13, 2016, 8:20:37 PM7/13/16
to std-pr...@isocpp.org
Em quinta-feira, 14 de julho de 2016, às 02:26:17 PDT, Ville Voutilainen
escreveu:
> > Hi Carl
> >
> > Can you explain somewhere why std::function is unable to do that?
> >
> > In other words, why must std::function allocate memory?
>
> Because it does type erasure and can hold a functor of any size, and
> can be assigned to from another functor
> of any size. Presumably this new type would fail to initialize if a
> functor larger than the internal buffer is used
> for initialization or assignment.

Ah, I see: functors (incl. lambdas). I was thinking of function pointers and
PMFs. Functors can have an arbitrary size, so no std::function implementation
can be wide enough to hold all possibilities.

But as a matter of QoI, std::function should not allocate for regular function
pointers and PMFs, plus stateless functors & lambdas.

Moritz Klammler

unread,
Jul 13, 2016, 9:05:06 PM7/13/16
to std-pr...@isocpp.org
> Very briefly, some of us are interested in having a standardized
> function which does not incur an upfront memory allocation to store
> the callable target, instead opting for a compile time specified
> buffer (stack allocated). This has been quite useful in several low
> latency domains such as gaming and electronic trading. In other words,
> a drop-in replacement for std::function, where all data is on the
> stack.

Isn't this already possible by using today's `std::function` with a
custom allocator backed by a stack-based array? The only thing this
solution cannot easily do for you is checking statically that the buffer
is large enough but since the size of an object is a constant, a single
unit test that passes control flow through the creation of the
type-erased function object would give that assurance to you.

`std::function` type-erases the allocator as well but I'd be very
disappointed if this would mean a heap allocation for a simple stack
allocator with a state as small as two pointers. I'm not able to point
to a source that confirms this, though.

It's likely that if you care about `std::function` not calling `malloc`,
you probably care about small (but still too large for SSO) and
short-lived strings and similar stuff, too. Custom allocators provide a
uniform interface to this without having to re-implement each type as a
stack-based alternative.

Am I missing something essential that your proposed
`std::inplace_function` can do in addition?

Nicol Bolas

unread,
Jul 13, 2016, 10:15:41 PM7/13/16
to ISO C++ Standard - Future Proposals
On Wednesday, July 13, 2016 at 9:05:06 PM UTC-4, Moritz Klammler wrote:
> Very briefly, some of us are interested in having a standardized
> function which does not incur an upfront memory allocation to store
> the callable target, instead opting for a compile time specified
> buffer (stack allocated). This has been quite useful in several low
> latency domains such as gaming and electronic trading. In other words,
> a drop-in replacement for std::function, where all data is on the
> stack.

Isn't this already possible by using today's `std::function` with a
custom allocator backed by a stack-based array?

I'm not clear as to what you mean here. Consider this:

std::function<void()> a_func()
{
 
return []{};
}

What would the "stack-based array" equivalent look like? Would it look as reasonable as this? Would it be able to use that nifty guaranteed elision stuff we'll be getting?

Nicol Bolas

unread,
Jul 13, 2016, 10:25:41 PM7/13/16
to ISO C++ Standard - Future Proposals
First, would it be possible to make this proposal available via a PDF not hosted on GitHub? Like maybe on a github.io page or something, where I can view it in my browser instead of having to download it?

Next, I don't believe something like this belongs in the standard. I'm not saying that it isn't a useful class. But its utility is really special case. Much like a string with a user-specified SSO size and so forth. Not a bad idea, but just not good for the standard.

I really think it would be a good idea to have some form of "special case/low level/etc TS" for these kinds of types. Like the library fundamentals TS, but without the expectation that it would ever go into the standard wholesale.

As to the idea itself... I'm not really sure about it. My main concern is this:

std::inplace_function<int(), VALUE> func()
{
 
return [x = 12]() {return x;};
}

What can I set `VALUE` to which will guarantee that this code compiles? Because I don't think the standard gives us a way to do that. Oh sure, from a practical perspective, we can make some guesses about the size of the lambda type. But even so, it's not clear that any particular `VALUE` would permit compilation.

Jim Porter

unread,
Jul 14, 2016, 1:25:22 AM7/14/16
to std-pr...@isocpp.org
On 7/13/2016 9:25 PM, Nicol Bolas wrote:
> First, would it be possible to make this proposal available via a PDF
> /not/ hosted on GitHub? Like maybe on a github.io page or something,
> where I can view it in my browser instead of having to download it?

I see it in-line in my browser, but if that doesn't work for you, you
can use rawgit:
<https://cdn.rawgit.com/WG21-SG14/SG14/master/Docs/Proposals/NonAllocatingStandardFunction.pdf>.

- Jim


T. C.

unread,
Jul 14, 2016, 1:58:32 AM7/14/16
to ISO C++ Standard - Future Proposals


On Wednesday, July 13, 2016 at 9:05:06 PM UTC-4, Moritz Klammler wrote:
The allocator support in std::function has been zapped as of three weeks ago. 

Also, passing such a thing around is trickier since the array's lifetime is independent of the std::function.

Moritz Klammler

unread,
Jul 14, 2016, 2:11:05 PM7/14/16
to std-pr...@isocpp.org
> I'm not clear as to what you mean here. Consider this:
>
> std::function<void()> a_func()
> {
> return []{};
> }
>
> What would the "stack-based array" equivalent look like? Would it look
> as reasonable as this? Would it be able to use that nifty guaranteed
> elision stuff we'll be getting?

I think you'd have to pass the allocator as an argument to `a_func`. It
would be more involved than your snippet but probably still
"reasonable". Anyway, this...

> The allocator support in std::function has been zapped as of three
> weeks ago.

makes my entire point obsolete, of course. The only remaining feedback
I can provide then is to suggest you add a quick note to the motivation
of the proposal that explains this. I was somehow irritated that the
word "allocator" didn't appear a single time in a paper about memory
allocation. If allocator support was removed for good reasons and the
proposed approach is considered a superior replacement, mentioning this
explicitly might help the uninvolved reader.

Jeffrey Yasskin

unread,
Jul 14, 2016, 3:00:20 PM7/14/16
to std-pr...@isocpp.org
Could you have your paper discuss the difference vs LLVM's function_ref<> (http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/STLExtras.h?revision=273520&view=markup#l62) and explain your view (whatever it is) over which set of these possible function abstractions we should put in the standard?

Thanks,
Jeffrey

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/e00db911-40f5-4c11-a4b8-32bba77aa0a5%40isocpp.org.

Carl Cook

unread,
Jul 14, 2016, 6:56:33 PM7/14/16
to ISO C++ Standard - Future Proposals
Hi all,

Thanks very much for the feedback so far, this is exactly what I was looking for.

Below are my responses to the questions raised. Please let me know if I haven't answered concisely enough, or have misunderstood the comments.
  • Is this too specialized for the standard library? Should this be in a TR/boost/extension only?
    • This is the most important comment in my opinion, and the driver for this proposal - to determine the level of support
    • Possibly it should be kept out of the standard. Here we are talking about introducting a whole new type, plus optionally specifying buffer sizes/alignment, just to save an allocation
    • On the flip side, it's actually more than an allocation. It's cache locality, potential to prefetch, deterministic performance, etc
    • This leads on to a bigger question - should low latency/high performance be a concern of the standard library?
      • I would argue yes, as industries such as finance/games/image processing/aerospace represent a large set of C++ users, and low latency requirements are a reality
  • How to determine the correct buffer size, at all times?
    • You can't. For that, fall back to std::function
    • In reality, inplace_function will only be used in specialized cases, and for that you will have a good idea of the size of the callable object
      • If your size is too conservative, the static assert will tell you what size you should have used
    • Also, from experience, the implementation's default size is fine 90% of the time
  • Why not consider a custom allocator?
    • Primarily, for performance (keeping the allocated memory together with the function object itself is important, for example)
    • A secondary concern is simplicity (another template argument introduces more complexity in terms of copy construction, assignment, etc)
      • That was a good point about the allocator memory outliving the function as well
    • Another concern is giving the implementation freedom in terms of memory layout/alignment/type erasure/management function pointers, etc (custom allocators make this harder to acheive)
    • I'll add this discussion to the design considerations part of the spec
  • What's the difference between inplace_function and LLVM's function_ref?
    • Excellent point - I've not seen function_ref until today
      • One difference - an inplace_function can be stored/copied/moved, even for non-trivial callable objects (for function_ref you'd need a second step of converting this to a std::function before storing/copying)
      • Another difference - I am not convinced function_ref successfully captures closures correctly (at least in GCC on -O1 or above, it doesn't appear to capture out of scope variables by reference or copy)
    • Again, I'll expand on this and add this to the proposal (after taking a closer look at function_ref)
  • Why not publish this draft proposal on github.io?
    • I don't particularly like having to push updated pdfs into github either, but I think the best thing for me to do is rewrite the proposal in markdown
    • Incidentally, github renders pdfs fine for me (using chrome/chromeos)
Again, thanks very much for the comments so far. This helps improve the proposal, even if it never makes it out of draft stage.

Please keep in mind that the primary goal of this proposal (at present) is to measure the interest/response of such a mechanism. But I do note several related proposals such as small/inplace vectors, inplace strings, inplace any/variant - i.e. there seems to be a theme here.

Many thanks,
Carl

Ville Voutilainen

unread,
Jul 14, 2016, 7:11:08 PM7/14/16
to ISO C++ Standard - Future Proposals
Here's my 2 cents:

On 15 July 2016 at 01:56, Carl Cook <carl...@gmail.com> wrote:
> Hi all,
>
> Thanks very much for the feedback so far, this is exactly what I was looking
> for.
>
> Below are my responses to the questions raised. Please let me know if I
> haven't answered concisely enough, or have misunderstood the comments.
>
> Is this too specialized for the standard library? Should this be in a
> TR/boost/extension only?
>
> This is the most important comment in my opinion, and the driver for this
> proposal - to determine the level of support
> Possibly it should be kept out of the standard. Here we are talking about
> introducting a whole new type, plus optionally specifying buffer
> sizes/alignment, just to save an allocation
> On the flip side, it's actually more than an allocation. It's cache
> locality, potential to prefetch, deterministic performance, etc
> This leads on to a bigger question - should low latency/high performance be
> a concern of the standard library?
>
> I would argue yes, as industries such as finance/games/image
> processing/aerospace represent a large set of C++ users, and low latency
> requirements are a reality

It's also not just about latency. There are environments where
subsystems have fixed pools, and not
being able to exactly control what and how much gets allocated and
from where is unacceptable.
Controlling it with an in-place buffer works in quite many cases.

>
> How to determine the correct buffer size, at all times?
>
> You can't. For that, fall back to std::function
> In reality, inplace_function will only be used in specialized cases, and for
> that you will have a good idea of the size of the callable object
>
> If your size is too conservative, the static assert will tell you what size
> you should have used
>
> Also, from experience, the implementation's default size is fine 90% of the
> time

Fantastic, that solves quite many practical problems I've seen in this
area. Fail to compile rather
than fail to allocate at run-time.

>
> Why not consider a custom allocator?

Because that's a much more complex solution than a fixed in-place buffer. ;)

Richard Smith

unread,
Jul 14, 2016, 8:35:42 PM7/14/16
to std-pr...@isocpp.org
On Thu, Jul 14, 2016 at 3:56 PM, Carl Cook <carl...@gmail.com> wrote:
Hi all,

Thanks very much for the feedback so far, this is exactly what I was looking for.

Below are my responses to the questions raised. Please let me know if I haven't answered concisely enough, or have misunderstood the comments.
  • Is this too specialized for the standard library? Should this be in a TR/boost/extension only?
    • This is the most important comment in my opinion, and the driver for this proposal - to determine the level of support
    • Possibly it should be kept out of the standard. Here we are talking about introducting a whole new type, plus optionally specifying buffer sizes/alignment, just to save an allocation
    • On the flip side, it's actually more than an allocation. It's cache locality, potential to prefetch, deterministic performance, etc
    • This leads on to a bigger question - should low latency/high performance be a concern of the standard library?
      • I would argue yes, as industries such as finance/games/image processing/aerospace represent a large set of C++ users, and low latency requirements are a reality
  • How to determine the correct buffer size, at all times?
    • You can't. For that, fall back to std::function
    • In reality, inplace_function will only be used in specialized cases, and for that you will have a good idea of the size of the callable object
      • If your size is too conservative, the static assert will tell you what size you should have used
    • Also, from experience, the implementation's default size is fine 90% of the time
  • Why not consider a custom allocator?
    • Primarily, for performance (keeping the allocated memory together with the function object itself is important, for example)
    • A secondary concern is simplicity (another template argument introduces more complexity in terms of copy construction, assignment, etc)
      • That was a good point about the allocator memory outliving the function as well
    • Another concern is giving the implementation freedom in terms of memory layout/alignment/type erasure/management function pointers, etc (custom allocators make this harder to acheive)
    • I'll add this discussion to the design considerations part of the spec
  • What's the difference between inplace_function and LLVM's function_ref?
    • Excellent point - I've not seen function_ref until today
      • One difference - an inplace_function can be stored/copied/moved, even for non-trivial callable objects (for function_ref you'd need a second step of converting this to a std::function before storing/copying)
      • Another difference - I am not convinced function_ref successfully captures closures correctly (at least in GCC on -O1 or above, it doesn't appear to capture out of scope variables by reference or copy)
llvm::function_ref doesn't capture anything, and is not supposed to -- it's a *non-owning* handle to a callable that outlives the handle. (llvm::function_ref is to std::function as std::string_view is to std::string.) This is a feature and a design goal of llvm::function_ref, but I think it means it's addressing a fundamentally different problem than the one you're tackling here (which seems to be essentially, "let me control the size and alignment in std::function's small function optimization")

Nicol Bolas

unread,
Jul 14, 2016, 10:57:50 PM7/14/16
to ISO C++ Standard - Future Proposals
On Thursday, July 14, 2016 at 6:56:33 PM UTC-4, Carl Cook wrote:
Hi all,

Thanks very much for the feedback so far, this is exactly what I was looking for.

Below are my responses to the questions raised. Please let me know if I haven't answered concisely enough, or have misunderstood the comments.
  • Is this too specialized for the standard library? Should this be in a TR/boost/extension only?
    • This is the most important comment in my opinion, and the driver for this proposal - to determine the level of support
    • Possibly it should be kept out of the standard. Here we are talking about introducting a whole new type, plus optionally specifying buffer sizes/alignment, just to save an allocation
    • On the flip side, it's actually more than an allocation. It's cache locality, potential to prefetch, deterministic performance, etc
    • This leads on to a bigger question - should low latency/high performance be a concern of the standard library?
      • I would argue yes, as industries such as finance/games/image processing/aerospace represent a large set of C++ users, and low latency requirements are a reality
It's not a question of "should the standard library care about high-performance code". It's a question of "how many high-performance corner cases should be in the standard library?" You can keep adding stuff to cover corner cases ad infinitum. At some point, you have to draw a line.

Remember: we are talking about a type which, in the very next section, you admit "will only be used in specialized cases". To me, the standard library should be for tools that are of general use.

You need a shared ownership pointer that can work with anything? We've got that. But if the atomic nature of the reference count is a performance problem for you, or if you really don't like the added allocation overhead of the control block and want it to be intrusive, you'll have to look elsewhere for a tool that services that need. You need a string type with some basic-though-not-very-good string functionality? We've got that. But if you need a string that has a guaranteed small size, or a specific small size, or just a fixed maximum size period, that's something you need to make for yourself.

That's why I like the idea of having a dedicated TS specifically for these types of things. I'd love to see a TS full of `small_vector`, `small_basic_string`, `inplace_function`, and so forth (but not `flat_set/map`. Those need to be in the standard library). Please don't look on TS's as some kind of ghetto for things that are not really standard. Yes, they really are standard, they really are supplied by compiler vendors, and they really are available.

They're just not part of the C++ standard library.

Similarly, I like the idea of the 2D graphics proposal, and maybe the input proposals and so forth. But I don't think it is appropriate for the standard library.
  • How to determine the correct buffer size, at all times?
    • You can't. For that, fall back to std::function
This actually brings up a small technical deficiency with the current proposal: no interop with `std::function`. Well, besides the obvious fact that `inplace_function` is a callable and you could therefore shove one into a `function` and vice-versa.

We ought to be able to convert between the two without creating overhead. That is, this:

inplace_function<...> f(a_functor);
std
::function<...> g = f;

Should not take up additional storage or function call overhead compared to:

std::function<...> g(a_functor);

At the very least, we should be able to go from `inplace_function` to `std::function`. Preferably with move support, if possible. Obviously you can't move the storage in the `inplace_function`, but you can at least move the object being stored if it contains a `unique_ptr` or something.

Please keep in mind that the primary goal of this proposal (at present) is to measure the interest/response of such a mechanism. But I do note several related proposals such as small/inplace vectors, inplace strings, inplace any/variant - i.e. there seems to be a theme here.

Themes are TS material ;) Like networking or filesystems.

Nicol Bolas

unread,
Jul 14, 2016, 11:01:36 PM7/14/16
to ISO C++ Standard - Future Proposals


On Thursday, July 14, 2016 at 8:35:42 PM UTC-4, Richard Smith wrote:
On Thu, Jul 14, 2016 at 3:56 PM, Carl Cook <carl...@gmail.com> wrote:
  • What's the difference between inplace_function and LLVM's function_ref?
    • Excellent point - I've not seen function_ref until today
      • One difference - an inplace_function can be stored/copied/moved, even for non-trivial callable objects (for function_ref you'd need a second step of converting this to a std::function before storing/copying)
      • Another difference - I am not convinced function_ref successfully captures closures correctly (at least in GCC on -O1 or above, it doesn't appear to capture out of scope variables by reference or copy)
llvm::function_ref doesn't capture anything, and is not supposed to -- it's a *non-owning* handle to a callable that outlives the handle. (llvm::function_ref is to std::function as std::string_view is to std::string.) This is a feature and a design goal of llvm::function_ref, but I think it means it's addressing a fundamentally different problem than the one you're tackling here (which seems to be essentially, "let me control the size and alignment in std::function's small function optimization")

It's more than that, because `inplace_function` never allocates memory. If the type you pass can't fit into the buffer... you get a hard compile error.

It's more like the difference between having a fixed-length string class and having a class that lets you control the small buffer size. With the former, you say "this string will have 31 characters maximum, all stored within this object." With the latter, you say "if the string is less than 32 characters long, it will not allocate memory."

Nevin Liber

unread,
Jul 14, 2016, 11:19:24 PM7/14/16
to std-pr...@isocpp.org
On 14 July 2016 at 21:57, Nicol Bolas <jmck...@gmail.com> wrote:
It's not a question of "should the standard library care about high-performance code". It's a question of "how many high-performance corner cases should be in the standard library?" You can keep adding stuff to cover corner cases ad infinitum. At some point, you have to draw a line.

Why?

I remember back in '12 when the big complaint was that the C++ Standard Library was too small.  I must have been sleeping when that got solved.

My personal criterion are:
  • Things that people keep inventing in multiple places
  • Things that are hard to write correctly 
Remember: we are talking about a type which, in the very next section, you admit "will only be used in specialized cases". To me, the standard library should be for tools that are of general use.

That's you.  And no one is forcing you to use the other stuff.
 
That's why I like the idea of having a dedicated TS specifically for these types of things. I'd love to see a TS full of `small_vector`, `small_basic_string`, `inplace_function`, and so forth (but not `flat_set/map`. Those need to be in the standard library). Please don't look on TS's as some kind of ghetto for things that are not really standard. Yes, they really are standard, they really are supplied by compiler vendors, and they really are available.

And people who work on them expect the stuff in them to go into the IS at some point.  If you wish to change the focus of TSes, you need to come to meetings and present your case.
 
This actually brings up a small technical deficiency with the current proposal: no interop with `std::function`. Well, besides the obvious fact that `inplace_function` is a callable and you could therefore shove one into a `function` and vice-versa.

Why?  What are the actual use cases for that?

Nevin Liber

unread,
Jul 14, 2016, 11:21:35 PM7/14/16
to std-pr...@isocpp.org
On 14 July 2016 at 21:57, Nicol Bolas <jmck...@gmail.com> wrote:

It's not a question of "should the standard library care about high-performance code". It's a question of "how many high-performance corner cases should be in the standard library?" You can keep adding stuff to cover corner cases ad infinitum. At some point, you have to draw a line.
[...] 
This actually brings up a small technical deficiency with the current proposal: no interop with `std::function`. Well, besides the obvious fact that `inplace_function` is a callable and you could therefore shove one into a `function` and vice-versa.

We ought to be able to convert between the two without creating overhead.

Isn't that just a corner case we ought to be drawing the line at?

Nicol Bolas

unread,
Jul 15, 2016, 12:30:18 AM7/15/16
to ISO C++ Standard - Future Proposals
On Thursday, July 14, 2016 at 11:19:24 PM UTC-4, Nevin ":-)" Liber wrote:
On 14 July 2016 at 21:57, Nicol Bolas <jmck...@gmail.com> wrote:
It's not a question of "should the standard library care about high-performance code". It's a question of "how many high-performance corner cases should be in the standard library?" You can keep adding stuff to cover corner cases ad infinitum. At some point, you have to draw a line.

Why?

I remember back in '12 when the big complaint was that the C++ Standard Library was too small.  I must have been sleeping when that got solved.

I remember back in '12 when I completely disagreed with Herb Sutter's statement to that effect. So I'm not really sure what point you're trying to make. I've always felt that C++'s small standard library was a good thing, not a problem to be "solved".

My personal criterion are:
  • Things that people keep inventing in multiple places
  • Things that are hard to write correctly 
Remember: we are talking about a type which, in the very next section, you admit "will only be used in specialized cases". To me, the standard library should be for tools that are of general use.

That's you.  And no one is forcing you to use the other stuff.

Generally speaking, people do not code in a vacuum. How other people code affects your code, just as your code affects the code of others. Why?

Because you may need to share that code. And thus, your code needs to be able to interoperate with theirs. If someone elses code allocates memory, but your project doesn't allow unauthorized allocations, then you can't use their code. If their strings are UTF-16s, then you're going to have to convert your UTF-8s into UTF-16 if you want to use it. If you don't like `enum class`es, but someone used an `enum class` in the interface to their library, you're going to have to make your peace with that language feature in order to use that library. If a library uses filesystem `path`s, but you use C-style file IO (because you like performance), you're going to have to write non-standard code in order to work with their system.

And so forth.

Some people do code in a vacuum, where the only code they talk to lives in their own world. And that's fine. But let's not ignore the fact that a lot of other people share code. And therefore, what code someone uses very much affects others.

This is why standards are important. That's why `shared_ptr` is designed as the "do everything" smart pointer. It is the smart pointer that can work with anything. Despite its overhead compared to other possible implementations, it is the lingua franca smart pointer.

The nice thing about the C++ standard library as it currently stands is that it usually has only one answer to every general problem (that it covers). Need a dynamic array? `vector`. Need a callback that can call anything? `function`. Need a reference-counted shared ownership object? `shared_ptr`. Need a way to communicate a value/exception between threads? `promise/future`. Etc.

Every time the standard library tries to offer multiple ways to achieve the same result, they screw things up. Just look at iostreams vs. C-standard IO. C's IO is very fast and serviceable, but is horribly non-type-safe and doesn't interoperate with `filesystem`. Iostreams has filesystem interop (when the committee fixes that defect) and type-safety, but is very weak on performance when type safety isn't an issue. Neither one is good; there is only the one that is least painful.

We had C-standard string-to-number functions, which were unsafe. So we tried to replace them with `stoX` and `to_string`, all of which are broken by the fact that they sue `std::string` directly, not even allowing you to use a string type with a different allocator. That attempt to replace C-standard string conversions went so badly that C++17 is going to replace the replacements with more string conversion functions. Converters that seem so terrified of the mistakes of the C++11 converters that they don't even use `string_view`, eschewing it for explicit `char*` ranges.

Imagine how much cleaner the standard library could be if iostreams were just an adjacent TS, rather than a fully required part of the C++ standard. Imagine how much refactoring and improvement could have been done on that interface if backwards compatibility were not a requirement.

That's why I like the idea of having a dedicated TS specifically for these types of things. I'd love to see a TS full of `small_vector`, `small_basic_string`, `inplace_function`, and so forth (but not `flat_set/map`. Those need to be in the standard library). Please don't look on TS's as some kind of ghetto for things that are not really standard. Yes, they really are standard, they really are supplied by compiler vendors, and they really are available.

And people who work on them expect the stuff in them to go into the IS at some point.

So... there is a genuine intent to adopt a 2D graphics rendering system into the C++ standard library at some point? Really?

If you wish to change the focus of TSes, you need to come to meetings and present your case.
 
This actually brings up a small technical deficiency with the current proposal: no interop with `std::function`. Well, besides the obvious fact that `inplace_function` is a callable and you could therefore shove one into a `function` and vice-versa.

Why?  What are the actual use cases for that?

The use case of "someone used `std::function` in an interface, but I use `std::inplace_function` internally, and I need to talk to their code." Right now, `std::function` is how lots of C++ code takes callbacks, and for good reason. But if I've wrapped some function in an `inplace_function`, I still need to be able to talk to them.

It's basically the same thinking that allows a `unique_ptr` to be moved into a `shared_ptr`.

Nevin Liber

unread,
Jul 15, 2016, 3:13:58 AM7/15/16
to std-pr...@isocpp.org
On 14 July 2016 at 23:30, Nicol Bolas <jmck...@gmail.com> wrote:
The use case of "someone used `std::function` in an interface, but I use `std::inplace_function` internally, and I need to talk to their code." Right now, `std::function` is how lots of C++ code takes callbacks, and for good reason. But if I've wrapped some function in an `inplace_function`, I still need to be able to talk to them.

Maybe std::function is the wrong vocabulary type for interfaces, just like std::string is the wrong vocabulary type for interfaces, hence we will have std::string_view.  Having something like llvm::function_ref that Richard Smith described earlier might be a better fit (and LEWG might ask Carl to add such a beast to his proposal).

Carl Cook

unread,
Jul 15, 2016, 5:12:58 AM7/15/16
to ISO C++ Standard - Future Proposals
On Friday, 15 July 2016 02:35:42 UTC+2, Richard Smith wrote:
llvm::function_ref doesn't capture anything, and is not supposed to -- it's a *non-owning* handle to a callable that outlives the handle. (llvm::function_ref is to std::function as std::string_view is to std::string.) This is a feature and a design goal of llvm::function_ref, but I think it means it's addressing a fundamentally different problem than the one you're tackling here (which seems to be essentially, "let me control the size and alignment in std::function's small function optimization"

Understood. What caught me out is that compilers can aggressively limit the scope of lambdas. Hence I believe the following is not legal usage:

int non_const_var = 0xf;
function_ref<int()> f_ref = [&]() { return non_const_var;};
return f_ref();



Giovanni Piero Deretta

unread,
Jul 15, 2016, 6:00:28 AM7/15/16
to ISO C++ Standard - Future Proposals

Isn't an std::function containing an std::reference_wrapper guaranteed not to allocate? Is that different from llvm::function_ref?

-- gpd

Richard Smith

unread,
Jul 15, 2016, 2:27:56 PM7/15/16
to std-pr...@isocpp.org
Right, it's exactly as broken as this:

string g() {
  string_view s_v = string("blah blah");
  return s_v;
}

In both cases, you've bound an object with reference semantics to a (non-lifetime-extended) temporary object.

Carl Cook

unread,
Jul 31, 2016, 8:10:39 AM7/31/16
to ISO C++ Standard - Future Proposals
On Thursday, 14 July 2016 09:58:32 UTC+4, T. C. wrote:
The allocator support in std::function has been zapped as of three weeks ago. 

Do you know what the reason was for allocator support to be dropped? 

Carl Cook

unread,
Jul 31, 2016, 9:29:30 AM7/31/16
to ISO C++ Standard - Future Proposals
In the example below, I am not passing a function_ref as the return value, I am returning the result of the invocation of that function_ref, which I'd assumed would be safe (i.e. return an integer).

However, it is the invocation that has failed, because the compiler has decided that the lambda can already be cleaned up (from what I can tell, looking at the disassembly).

So, granted that the function_ref certainly doesn't copy callable objects, but I don't think it's useful for any lambda, unless that lambda is just used to invoke a function (as in the llvm example usage).

Tony V E

unread,
Jul 31, 2016, 9:41:42 AM7/31/16
to ISO C++ Standard - Future Proposals
It never worked. No one implemented it. 

Type erasure + allocator.... erasure? + std::funtion semantics  Etc
Hard/impossible?


Sent from my BlackBerry portable Babbage Device
From: Carl Cook
Sent: Sunday, July 31, 2016 8:10 AM
To: ISO C++ Standard - Future Proposals
Subject: Re: [std-proposals] Proposal - non allocating std::function

On Thursday, 14 July 2016 09:58:32 UTC+4, T. C. wrote:
The allocator support in std::function has been zapped as of three weeks ago. 

Do you know what the reason was for allocator support to be dropped? 

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Nicol Bolas

unread,
Jul 31, 2016, 9:44:58 AM7/31/16
to ISO C++ Standard - Future Proposals

According to the proposal that drops it, it was because the whole design was generally considered faulty, with a number of open defect reports on them. These faults lead to none of the major standard library implementations doing it right. libstdc++ never even bothered to offer the allocator constructor overloads. libc++ provided the constructors, but never used the allocators. The closest one to right was VS, which would use the allocators, but didn't propagate them correctly on assignment.

Ville Voutilainen

unread,
Jul 31, 2016, 9:48:12 AM7/31/16
to ISO C++ Standard - Future Proposals
On 31 July 2016 at 16:44, Nicol Bolas <jmck...@gmail.com> wrote:
>> Do you know what the reason was for allocator support to be dropped?
> According to the proposal that drops it, it was because the whole design was
> generally considered faulty, with a number of open defect reports on them.
> These faults lead to none of the major standard library implementations
> doing it right. libstdc++ never even bothered to offer the allocator
> constructor overloads. libc++ provided the constructors, but never used the
> allocators. The closest one to right was VS, which would use the allocators,
> but didn't propagate them correctly on assignment.

The problem with correct propagation is that type erasure eradicates
all knowledge of what types
the propagation should be queried for. The mixture of purely
compile-time information of the propagation
traits and the complete lack of any such information at the point of
assignment due to type erasure is a fairly bad mixture.

Nicol Bolas

unread,
Jul 31, 2016, 9:53:22 AM7/31/16
to ISO C++ Standard - Future Proposals

That's a really good analogy. I think the principle concern for the user is this. `string_view` is a great interface type because you can use it to construct pretty much any kind of string you like. It isn't limited to just `std::string` construction. Would a hypothetical `function_view` have an interface that could be used to construct any kind of function wrapper? That is, how could it expose the actual type of the callable in such a way that someone could copy it out?

Though we could probably get away with just allowing it to be converted to a `function`. Or rather more specifically, allowing `function` to be constructed from a `function_view`.

Nicol Bolas

unread,
Jul 31, 2016, 9:58:06 AM7/31/16
to ISO C++ Standard - Future Proposals
It's different in a couple of ways.

1: You can't guarantee that someone has given you a `function` containing such a wrapper. So when you copy/move it around, you can't be guaranteed that it won't allocate memory.

2: It's probably slower to invoke the callback through that than through a hypothetical `function_view`.

While the whole `reference_wrapper` thing is a useful optimization, it's really best to put that sort of thing in the type system.

Casey Carter

unread,
Jul 31, 2016, 10:15:06 AM7/31/16
to ISO C++ Standard - Future Proposals
[&]() { return non_const_var;}; is an expression whose value is a temporary object. That temporary object is destroyed at the end of the full expression
function_ref<int()> f_ref = [&]() { return non_const_var;};
Using the reference stored in f_ref to access that object after the end of its lifetime results in undefined behavior.

Carl Cook

unread,
Jul 31, 2016, 10:26:00 AM7/31/16
to std-pr...@isocpp.org

Understood, thanks. Hence llvm::function_ref really is what it says. I.e. don't use it beyond references to functions.


--
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/vven2Om7Ha8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Richard Smith

unread,
Aug 1, 2016, 12:23:40 PM8/1/16
to std-pr...@isocpp.org
On Sun, Jul 31, 2016 at 6:29 AM, Carl Cook <carl...@gmail.com> wrote:
In the example below, I am not passing a function_ref as the return value, I am returning the result of the invocation of that function_ref, which I'd assumed would be safe (i.e. return an integer).

However, it is the invocation that has failed, because the compiler has decided that the lambda can already be cleaned up (from what I can tell, looking at the disassembly).

So, granted that the function_ref certainly doesn't copy callable objects, but I don't think it's useful for any lambda, unless that lambda is just used to invoke a function (as in the llvm example usage).

Actually, the above is exactly analogous to what happens in the string_view case. Note that I'm returning a string, not a string_view.

In both cases, the problem happens in the construction of the string_view/function_ref object: we're constructing an object that represents a handle to another object, and the other object is a temporary object that goes away at the end of the full-expression. And then on the second line, we do something that tries to access the now-destroyed object through the handle (in the string_view case, we construct a string from the string_view, and in the function_ref case, we attempt to call the destroyed closure object).

This is just how handle-like objects behave when bound to temporaries, and not specific to string_view or function_ref.
 
On Friday, 15 July 2016 20:27:56 UTC+2, Richard Smith wrote:
On Fri, Jul 15, 2016 at 2:12 AM, Carl Cook <carl...@gmail.com> wrote:
On Friday, 15 July 2016 02:35:42 UTC+2, Richard Smith wrote:
llvm::function_ref doesn't capture anything, and is not supposed to -- it's a *non-owning* handle to a callable that outlives the handle. (llvm::function_ref is to std::function as std::string_view is to std::string.) This is a feature and a design goal of llvm::function_ref, but I think it means it's addressing a fundamentally different problem than the one you're tackling here (which seems to be essentially, "let me control the size and alignment in std::function's small function optimization"

Understood. What caught me out is that compilers can aggressively limit the scope of lambdas. Hence I believe the following is not legal usage:

int non_const_var = 0xf;
function_ref<int()> f_ref = [&]() { return non_const_var;};
return f_ref();

Right, it's exactly as broken as this:

string g() {
  string_view s_v = string("blah blah");
  return s_v;
}

In both cases, you've bound an object with reference semantics to a (non-lifetime-extended) temporary object.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Carl Cook

unread,
Aug 1, 2016, 12:26:39 PM8/1/16
to std-pr...@isocpp.org

I didn't spot that you are indeed returning a string. My mistake. Thanks, yes now I see that your example holds perfectly.


You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/vven2Om7Ha8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Tony V E

unread,
Aug 1, 2016, 12:55:38 PM8/1/16
to Standard Proposals
On Sun, Jul 31, 2016 at 9:53 AM, Nicol Bolas <jmck...@gmail.com> wrote:


On Friday, July 15, 2016 at 3:13:58 AM UTC-4, Nevin ":-)" Liber wrote:
On 14 July 2016 at 23:30, Nicol Bolas <jmck...@gmail.com> wrote:
The use case of "someone used `std::function` in an interface, but I use `std::inplace_function` internally, and I need to talk to their code." Right now, `std::function` is how lots of C++ code takes callbacks, and for good reason. But if I've wrapped some function in an `inplace_function`, I still need to be able to talk to them.

Maybe std::function is the wrong vocabulary type for interfaces, just like std::string is the wrong vocabulary type for interfaces, hence we will have std::string_view.  Having something like llvm::function_ref that Richard Smith described earlier might be a better fit (and LEWG might ask Carl to add such a beast to his proposal).

That's a really good analogy. I think the principle concern for the user is this. `string_view` is a great interface type because you can use it to construct pretty much any kind of string you like. It isn't limited to just `std::string` construction.

I don't think that's why string_view is great.  Rather, string_view is great because you probably don't need to create anything at all.  You probably just need to *use* the characters, not keep them.  ie a file_open(string_view) could (in theory at least) just use the characters in the string_view to open the file, it doesn't need to create a string of any kind to keep around. (unfortunately, in real life the OS probably needs a null-terminated string).
 
Would a hypothetical `function_view` have an interface that could be used to construct any kind of function wrapper? That is, how could it expose the actual type of the callable in such a way that someone could copy it out?


Same with function vs function_ref.  In most uses, you want to *call* the function, not keep it or a copy of it.  Like sort(container, my_less).  You want to call my_less, not keep it for later.

Tony
 
Though we could probably get away with just allowing it to be converted to a `function`. Or rather more specifically, allowing `function` to be constructed from a `function_view`.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Nicol Bolas

unread,
Aug 1, 2016, 1:28:43 PM8/1/16
to ISO C++ Standard - Future Proposals
On Monday, August 1, 2016 at 12:55:38 PM UTC-4, Tony V E wrote:
On Sun, Jul 31, 2016 at 9:53 AM, Nicol Bolas <jmck...@gmail.com> wrote:
On Friday, July 15, 2016 at 3:13:58 AM UTC-4, Nevin ":-)" Liber wrote:
On 14 July 2016 at 23:30, Nicol Bolas <jmck...@gmail.com> wrote:
The use case of "someone used `std::function` in an interface, but I use `std::inplace_function` internally, and I need to talk to their code." Right now, `std::function` is how lots of C++ code takes callbacks, and for good reason. But if I've wrapped some function in an `inplace_function`, I still need to be able to talk to them.

Maybe std::function is the wrong vocabulary type for interfaces, just like std::string is the wrong vocabulary type for interfaces, hence we will have std::string_view.  Having something like llvm::function_ref that Richard Smith described earlier might be a better fit (and LEWG might ask Carl to add such a beast to his proposal).

That's a really good analogy. I think the principle concern for the user is this. `string_view` is a great interface type because you can use it to construct pretty much any kind of string you like. It isn't limited to just `std::string` construction.

I don't think that's why string_view is great.  Rather, string_view is great because you probably don't need to create anything at all.  You probably just need to *use* the characters, not keep them.  ie a file_open(string_view) could (in theory at least) just use the characters in the string_view to open the file, it doesn't need to create a string of any kind to keep around. (unfortunately, in real life the OS probably needs a null-terminated string).

Which is why it's a good thing that `string_view` makes it easy to construct a null-terminated string from itself, yes? It makes it easy to support those APIs that have needs that `string_view` doesn't fulfill.

Admittedly, the argument isn't as compelling for `function_view`, since copying functions is very different from copying strings. So if your API needed to store the function long-term, it's more likely that you'd use either `std::function` or a specialized type of your own devising in the interface.

However, that reminds me of something. I recall seeing some proposal or somesuch about an alteration of `string_view` that would know if it were null-terminated or not.

mihailn...@gmail.com

unread,
Sep 20, 2016, 2:33:21 PM9/20/16
to ISO C++ Standard - Future Proposals
I am all for non-allocating function objects - vast majorities of my lambdas are small enough to be "stack-only", but bigger then the std::function SOO threshold!

But isn't it time to think for first-class 'callable' type? 

It is somewhat weird, we have first class, wildly-used, lambdas, but no native way to store and pass them around - we have to use 10+ years old container. It's like a new car with second-hand tires. 

And we also have no clean and efficient way to define an interface which accepts a callable - we either have to use templates (not clean, though Concepts will improve that) or use std::function and not be as efficient (and still not all that clean).
 


D. B.

unread,
Sep 20, 2016, 2:37:31 PM9/20/16
to std-pr...@isocpp.org
On Tue, Sep 20, 2016 at 7:33 PM, <mihailn...@gmail.com> wrote:

But isn't it time to think for first-class 'callable' type? 

It is somewhat weird, we have first class, wildly-used, lambdas, but no native way to store and pass them around - we have to use 10+ years old container. It's like a new car with second-hand tires. 

And we also have no clean and efficient way to define an interface which accepts a callable - we either have to use templates (not clean, though Concepts will improve that) or use std::function and not be as efficient (and still not all that clean).
 


What's the alternative? How does one support arbitrary callable types, if not by using templates? Which std::function is, just as much.

Of course, maybe you have an idea for some other way to do this. Then you're in the right place to propose it.

 
Reply all
Reply to author
Forward
0 new messages