Date: Tue, 19 Nov 2024 10:21:00 +0100
The specialisations would be essentially more verbose, although maybe
easier to follow, than just enable/disable certain features (using
requires) based on the config. I.e. you could have things like:
```cpp
template <typename Signature, typename Conf>
class function {
...
public:
constexpr function(function const&) noexcept(Conf::is_trivial_copyable ||
Conf::is_noexcept_copyable) requires(Conf::is_copy_constructible) ...
```
But the combinatorials of all flags and knobs vs. all constructors and
assignment operators would explode. All types of constructors and
assignment operators would have to be implemented *somewhere* and then
either "pulled in" or "enabled" depending on the flags.. No, I am starting
to believe that since a std:: function replacement does not seem too hard
to implement, it may not be worth the effort trying to create a 'completely
configurable std::function'. It may, however, make more sense in having the
standard supplying an easy way to incorporate SOO yourself, but that would
either essentially be a 'std::any' or a 'std:: variant' I guess..
// Robin
On Tue, Nov 19, 2024, 09:52 Marcin Jaczewski <marcinjaczewski86_at_[hidden]>
wrote:
> wt., 19 lis 2024 o 06:44 Robin Savonen Söderholm
> <robinsavonensoderholm_at_[hidden]> napisał(a):
> >
> > Try to implement that before proposing it, as you would essentially have
> to implement all versions of the func at once and then disable everything
> that should not be enabled. Tricky to get completely right.
> >
> > // Robin
> >
>
> This is only partially true, remember that we have specializations?
> This means some implementation can look like:
>
> ```
> template<typename Func>
> class base_func<Func, func_clasic> : std::function<Func>
> {
> };
> ```
>
> And this can be fully independent from the rest of implementation. And
> this is C++98 tool.
> As far as I know adding new `require` based on new unique type should
> be ABI compatible (but probably bend ODR a bit).
> This means we could always "fork" implementation to add new features like:
>
> ```
> template<typename Func, typename Config> require !haveSpecialType<Config>
> class base_func<Func, Config>
> {
> // old
> };
> template<typename Func, typename Config> require haveSpecialType<Config>
> class base_func<Func, Config>
> {
> // new
> };
> ```
>
> Alternatively if we could guarantee ordering then we could even keep
> the base template `require` free.
>
> Overall this will be effective "umbrella type" that will contains lot
> of unique types with same name and
> allow writing some generic code in some cases without assuming
> anything about callback:
>
> ```
> template<typename Config>
> void repeat(base_func<void(), Config> f)
> {
> f();
> f();
> f();
> }
> ```
>
> >
> > On Mon, Nov 18, 2024, 22:41 Marcin Jaczewski via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
> >>
> >> pon., 18 lis 2024 o 17:02 Arthur O'Dwyer via Std-Proposals
> >> <std-proposals_at_[hidden]> napisał(a):
> >> >
> >> > On Mon, Nov 18, 2024 at 10:23 AM Robin Savonen Söderholm via
> Std-Proposals <std-proposals_at_[hidden]> wrote:
> >> >>
> >> >> Hi!
> >> >>
> >> >> In certain embedded situations, you may want to avoid needless heap
> allocations. Since the major compiler makers apply "small object
> optimisation", it would be nice to somehow query if a type satisfies the
> SOO or if std::function would do a heap allocation (something that we could
> maybe static_assert in the very least if no other options are automatically
> available).
> >> >> I believe that whether an object satisfies SOO or not may depend on
> both size and alignment of the type, so the most straight-forward API I can
> think of is something along the lines of
> `std::function_is_inplace_v<FUNCTION, TYPE>` or something like that.
> >> >
> >> >
> >> > It can also depend on the trivial copyability (or trivial
> relocatability) of the type, the nothrow move-constructibility, etc. etc.,
> depending on the vendor.
> >> > Nit: When we say "the type," we mean "the decayed type," e.g. if TYPE
> is a function type, we really care about the function pointer type, not the
> function type.
> >> >
> >> > using F = std::function<int(int)>;
> >> >
> >> > The trait you're looking for would have to be a member of `F`, not
> some sort of global trait unrelated to `F`.
> >> > It feels to me like there are several possible designs (type trait?
> constexpr function? member function of a constructed `F`?) but the simplest
> is a type trait:
> >> >
> >> > template<class T>
> >> > F wrap_in_function(T&& t) {
> >> > static_assert(F::uses_inplace_storage<T>::value);
> >> > return F(std::forward<T>(t));
> >> > }
> >> >
> >> > I think the biggest objections to such a function are minor but
> easy-to-stall-a-WG21-proposal:
> >> > (1) Naming is hard. It's easier now that we have the adjective
> "inplace" (as in "inplace_vector"), but notice the vast difference between
> your suggested name and mine. Should the standard library types provide
> both `F::bikeshed<T>::value` and `F::bikeshed_v<T>`? What about
> user-provided `F`s — do they need to provide both names?
> >> > (2) By exposing this information to the programmer, you're basically
> baking it into the ABI more than it used to be. A lot of type-erasure
> schemes continue to work at the ABI level even if the internal
> small-storage criterion changes over time; but if you actually expose the
> criterion to the user, then the user might start making ABI/API-relevant
> decisions based on that criterion, and then the vendor can't change their
> criterion without breaking the user's code. Of course this is already the
> case if the user is implicitly relying on the vendor's criterion in order
> to achieve correctness at runtime (e.g. assuming that a certain
> construction won't heap-allocate and therefore won't block), but WG21 has a
> lot more tolerance for breaking runtime behavior than breaking actual
> compilation. (A form of the quantitative fallacy: it's a lot easier to
> measure "broken builds" than "subtly broken runtime behavior" and so the
> former gets a lot more attention than the latter.)
> >> >
> >> >>
> >> >> It would also be nice to have a "std::inplace_function" that is a
> stand-in replacement for std::function when it is unfit for similar
> reasons. I see a "SG14" inplace_function, but I am unsure if
> inplace_function has been up for discussion before and shot down for some
> reason.
> >> >
> >> >
> >> > I don't think it's ever been up for discussion. Carl Cook started
> work on a proposal D0419 in 2016, but never completed it and it was never
> put into a mailing.
> >> > The current version of sg14::inplace_function is here:
> >> >
> https://github.com/Quuxplusone/SG14/?tab=readme-ov-file#in-place-type-erased-types-future--c14
> >> >
> >> > IMHO inplace_function shouldn't be standardized (and neither should
> std::move_only_function or std::copyable_function have been standardized)
> because there are simply too many knobs to fiddle with, and the correct
> knob-settings are known only to the programmer of each individual codebase.
> It is a fool's errand to try to standardize each possible setting of the
> knobs, each under a different name. (immovable_function, nonnull_function,
> non_implicitly_void_converting_function, etc. etc.) And we know this not
> just intuitively but also empirically: the STL already picked the "wrong"
> choice in several of these dimensions, several times in a row.
> >> >
> https://quuxplusone.github.io/blog/2019/03/27/design-space-for-std-function/
> >>
> >> What if we make all these configs exposed as one parameter type?
> >> Image template:
> >>
> >> ```
> >> template<typename Func, typename Config = func_def>
> >> class base_func;
> >>
> >> base_func<int(), func_non_move> foo1;
> >> base_func<int(), sbo<16, 8>> foo2;
> >> base_func<int(), custom<sbo<16, 8>, func_move, func_copy>> foo3;
> >> base_func<int(), my_own_tag<10>> foo4;
> >> ```
> >>
> >> And function implementation will querry some data from this config, we
> >> could define too who conversion
> >> work between diffrent types.
> >> If the committee forgets some "dimension" it can add new tags later
> >> that expose this to users.
> >> Could this work better?
> >>
> >>
> >>
> >>
> >> >
> >> > Data point: AFAICT, boost::function does not support anything like
> `F::uses_inplace_storage<T>`.
> >> >
> >> > HTH,
> >> > Arthur
> >> > --
> >> > Std-Proposals mailing list
> >> > Std-Proposals_at_[hidden]
> >> > https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> >> --
> >> Std-Proposals mailing list
> >> Std-Proposals_at_[hidden]
> >> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
easier to follow, than just enable/disable certain features (using
requires) based on the config. I.e. you could have things like:
```cpp
template <typename Signature, typename Conf>
class function {
...
public:
constexpr function(function const&) noexcept(Conf::is_trivial_copyable ||
Conf::is_noexcept_copyable) requires(Conf::is_copy_constructible) ...
```
But the combinatorials of all flags and knobs vs. all constructors and
assignment operators would explode. All types of constructors and
assignment operators would have to be implemented *somewhere* and then
either "pulled in" or "enabled" depending on the flags.. No, I am starting
to believe that since a std:: function replacement does not seem too hard
to implement, it may not be worth the effort trying to create a 'completely
configurable std::function'. It may, however, make more sense in having the
standard supplying an easy way to incorporate SOO yourself, but that would
either essentially be a 'std::any' or a 'std:: variant' I guess..
// Robin
On Tue, Nov 19, 2024, 09:52 Marcin Jaczewski <marcinjaczewski86_at_[hidden]>
wrote:
> wt., 19 lis 2024 o 06:44 Robin Savonen Söderholm
> <robinsavonensoderholm_at_[hidden]> napisał(a):
> >
> > Try to implement that before proposing it, as you would essentially have
> to implement all versions of the func at once and then disable everything
> that should not be enabled. Tricky to get completely right.
> >
> > // Robin
> >
>
> This is only partially true, remember that we have specializations?
> This means some implementation can look like:
>
> ```
> template<typename Func>
> class base_func<Func, func_clasic> : std::function<Func>
> {
> };
> ```
>
> And this can be fully independent from the rest of implementation. And
> this is C++98 tool.
> As far as I know adding new `require` based on new unique type should
> be ABI compatible (but probably bend ODR a bit).
> This means we could always "fork" implementation to add new features like:
>
> ```
> template<typename Func, typename Config> require !haveSpecialType<Config>
> class base_func<Func, Config>
> {
> // old
> };
> template<typename Func, typename Config> require haveSpecialType<Config>
> class base_func<Func, Config>
> {
> // new
> };
> ```
>
> Alternatively if we could guarantee ordering then we could even keep
> the base template `require` free.
>
> Overall this will be effective "umbrella type" that will contains lot
> of unique types with same name and
> allow writing some generic code in some cases without assuming
> anything about callback:
>
> ```
> template<typename Config>
> void repeat(base_func<void(), Config> f)
> {
> f();
> f();
> f();
> }
> ```
>
> >
> > On Mon, Nov 18, 2024, 22:41 Marcin Jaczewski via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
> >>
> >> pon., 18 lis 2024 o 17:02 Arthur O'Dwyer via Std-Proposals
> >> <std-proposals_at_[hidden]> napisał(a):
> >> >
> >> > On Mon, Nov 18, 2024 at 10:23 AM Robin Savonen Söderholm via
> Std-Proposals <std-proposals_at_[hidden]> wrote:
> >> >>
> >> >> Hi!
> >> >>
> >> >> In certain embedded situations, you may want to avoid needless heap
> allocations. Since the major compiler makers apply "small object
> optimisation", it would be nice to somehow query if a type satisfies the
> SOO or if std::function would do a heap allocation (something that we could
> maybe static_assert in the very least if no other options are automatically
> available).
> >> >> I believe that whether an object satisfies SOO or not may depend on
> both size and alignment of the type, so the most straight-forward API I can
> think of is something along the lines of
> `std::function_is_inplace_v<FUNCTION, TYPE>` or something like that.
> >> >
> >> >
> >> > It can also depend on the trivial copyability (or trivial
> relocatability) of the type, the nothrow move-constructibility, etc. etc.,
> depending on the vendor.
> >> > Nit: When we say "the type," we mean "the decayed type," e.g. if TYPE
> is a function type, we really care about the function pointer type, not the
> function type.
> >> >
> >> > using F = std::function<int(int)>;
> >> >
> >> > The trait you're looking for would have to be a member of `F`, not
> some sort of global trait unrelated to `F`.
> >> > It feels to me like there are several possible designs (type trait?
> constexpr function? member function of a constructed `F`?) but the simplest
> is a type trait:
> >> >
> >> > template<class T>
> >> > F wrap_in_function(T&& t) {
> >> > static_assert(F::uses_inplace_storage<T>::value);
> >> > return F(std::forward<T>(t));
> >> > }
> >> >
> >> > I think the biggest objections to such a function are minor but
> easy-to-stall-a-WG21-proposal:
> >> > (1) Naming is hard. It's easier now that we have the adjective
> "inplace" (as in "inplace_vector"), but notice the vast difference between
> your suggested name and mine. Should the standard library types provide
> both `F::bikeshed<T>::value` and `F::bikeshed_v<T>`? What about
> user-provided `F`s — do they need to provide both names?
> >> > (2) By exposing this information to the programmer, you're basically
> baking it into the ABI more than it used to be. A lot of type-erasure
> schemes continue to work at the ABI level even if the internal
> small-storage criterion changes over time; but if you actually expose the
> criterion to the user, then the user might start making ABI/API-relevant
> decisions based on that criterion, and then the vendor can't change their
> criterion without breaking the user's code. Of course this is already the
> case if the user is implicitly relying on the vendor's criterion in order
> to achieve correctness at runtime (e.g. assuming that a certain
> construction won't heap-allocate and therefore won't block), but WG21 has a
> lot more tolerance for breaking runtime behavior than breaking actual
> compilation. (A form of the quantitative fallacy: it's a lot easier to
> measure "broken builds" than "subtly broken runtime behavior" and so the
> former gets a lot more attention than the latter.)
> >> >
> >> >>
> >> >> It would also be nice to have a "std::inplace_function" that is a
> stand-in replacement for std::function when it is unfit for similar
> reasons. I see a "SG14" inplace_function, but I am unsure if
> inplace_function has been up for discussion before and shot down for some
> reason.
> >> >
> >> >
> >> > I don't think it's ever been up for discussion. Carl Cook started
> work on a proposal D0419 in 2016, but never completed it and it was never
> put into a mailing.
> >> > The current version of sg14::inplace_function is here:
> >> >
> https://github.com/Quuxplusone/SG14/?tab=readme-ov-file#in-place-type-erased-types-future--c14
> >> >
> >> > IMHO inplace_function shouldn't be standardized (and neither should
> std::move_only_function or std::copyable_function have been standardized)
> because there are simply too many knobs to fiddle with, and the correct
> knob-settings are known only to the programmer of each individual codebase.
> It is a fool's errand to try to standardize each possible setting of the
> knobs, each under a different name. (immovable_function, nonnull_function,
> non_implicitly_void_converting_function, etc. etc.) And we know this not
> just intuitively but also empirically: the STL already picked the "wrong"
> choice in several of these dimensions, several times in a row.
> >> >
> https://quuxplusone.github.io/blog/2019/03/27/design-space-for-std-function/
> >>
> >> What if we make all these configs exposed as one parameter type?
> >> Image template:
> >>
> >> ```
> >> template<typename Func, typename Config = func_def>
> >> class base_func;
> >>
> >> base_func<int(), func_non_move> foo1;
> >> base_func<int(), sbo<16, 8>> foo2;
> >> base_func<int(), custom<sbo<16, 8>, func_move, func_copy>> foo3;
> >> base_func<int(), my_own_tag<10>> foo4;
> >> ```
> >>
> >> And function implementation will querry some data from this config, we
> >> could define too who conversion
> >> work between diffrent types.
> >> If the committee forgets some "dimension" it can add new tags later
> >> that expose this to users.
> >> Could this work better?
> >>
> >>
> >>
> >>
> >> >
> >> > Data point: AFAICT, boost::function does not support anything like
> `F::uses_inplace_storage<T>`.
> >> >
> >> > HTH,
> >> > Arthur
> >> > --
> >> > Std-Proposals mailing list
> >> > Std-Proposals_at_[hidden]
> >> > https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> >> --
> >> Std-Proposals mailing list
> >> Std-Proposals_at_[hidden]
> >> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2024-11-19 09:21:14