Comments (7)
I want to also comment on this issue because it is more far reaching than it initially appears.
Not having a customization point for the default constructed inplace_function
handler makes it unusable for embedded work for multiple reasons. One example is banning of exceptions which does not permit throw syntax at all in header files.
Currently inplace_function
uses the SG14_INPLACE_FUNCTION_THROW
macro to customize it's behavior. This makes it completely unusable as a proper library. Let me elaborate:
Say library A
's A/inplace_function.hpp
file includes SG14/inplace_function
, uses it's macro to customize it's behavior and wraps it in it's own namespace as a::inplace_function
.
Say library B
also does the same and defines it's own b::inplace_function
but perhaps with
a different SG14_INPLACE_FUNCTION_THROW
definition.
Then when library C
tries to include both A
and B
, due to include guards / #pragma once
B
's include of SG14/inplace_function
won't occur so it won't be able to customize it as it needs to. Instead it's going to use library A
's customized version.
This is completely breaking behavior and forces libraries to literally copy paste code in order to get around this issue. The customization of inplace_function
needs to be addressed as a proper library customization feature and not using macros.
A proposal is to add a policy to inplace_function
in order to allow users to customize the behavior of the default constructor. This should come at no cost since it won't affect inplace_function
any more than the Capacity
template parameter does.
from sg14.
@liarokapisv: See "The space of design choices for std::function
" (2019-03-27). I firmly believe that if we added "policy parameters" for every different thing a user might want, we would cause more harm than benefit. The goal of library design should be design — we should figure out what most users want, and then implement it. If we just dump a bunch of template Tinkertoys on the floor and say "build your own type with whatever policies you think you want today," we're abdicating the library-design role and also increasing the difficulty of teaching (and learning) the SG14 library, because users will have to learn all the things it might do instead of the one thing it does. (And people who want that much control over the code are perfectly capable of cutting-pasting-and-editing this code to do exactly what they want, anyway.)
Right now, if you call a default-constructed inplace_function
, you don't violate any contract; it will throw bad_function_call
(or, if exceptions are turned off, then it does whatever you've defined SG14_INPLACE_FUNCTION_THROW
to do — the default is UB).
I think it's perfectly reasonable to argue that calling a default-constructed inplace_function
should be a contract-violation, and thus be UB all the time regardless of SG14_INPLACE_FUNCTION_THROW
. This would eliminate the library's reliance on exception handling. This would make it slightly less of a drop-in replacement for std::function
, though, which I think is why we haven't made that change yet.
Do you have any particular use-case for calling operator()
on a default-constructed or moved-from inplace_function
? Would it cause you any hardship if the library disallowed calling operator()
on a default-constructed or moved-from inplace_function
?
from sg14.
I just want to say that I use this library on microcontroller-based embedded projects without exceptions... But I don't have a complex project structure with multiple inplace_function.hpp
files embedded within multiple libraries. I have a single copy of the header within a framework.
Would it cause you any hardship if the library disallowed calling operator() on a default-constructed or moved-from inplace_function?
I'm +1 for this.
from sg14.
One interesting use of the default constructor is callbacks with void return type. In those cases one may want to just ignore everything by default which reduces boilerplate on creation. One may want this to be undefined in which case they can terminate
, throw
or even call a custom error handler.
I am admitedly more interested in allowing usage of inplace_function
in embedded projects than customizing the default constructor per se. However I have the following argument about not relying on the usage of SG14_INPLACE_FUNCTION_THROW
macro to support this.
If the purpose of SG13_INPLACE_FUNCTION_THROW
is for arbitrary customization then that's a poor choice due to the scenario demonstrated above. I am currently in a position that I use inplace_function
in my codebase and I also want to use an external library that also uses inplace_function internally. As it is, one library's behaviour overrides the behaviour of the other library forcing both me and the author of the library to copy paste the header and also change the namespaces to avoid ODR violations.
If the purpose of SG14_INPLACE_FUNCTION_THROW
is to just provide support for embedded projects then a simple SG14_DISALLOW_EXCEPTIONS
macro config check would be better suited to this task but then the library needs to support a reasonable default.
If we accept that both the embedded usecase of not allowing exceptions is real and that it's also worth allowing users to define the behaviour of the default constructor in that case (I argue that it is useful since there is no catch-all error reporting approach in that case as is the case of exceptions) , then that's accepting that this is a real customization point of the library in which case I argue that a policy would be better suited to this task due to the multiple problems of the macro approach.
Personally I don't mind banning the default constructor altogether in an embedded setting. What I have seen some libraries do is to check if exceptions are disabled and just declare but not define the relevant function leading to a linker error in case of misuse. It's then easy to build a wrapper on top of that that also allows for a default constructor.
from sg14.
Personally I don't mind banning the default constructor altogether in an embedded setting. What I have seen some libraries do is to check if exceptions are disabled and just declare but not define the relevant function leading to a linker error in case of misuse. It's then easy to build a wrapper on top of that that also allows for a default constructor.
This would work for me as well!
from sg14.
Personally I don't mind banning the default constructor altogether in an embedded setting.
Banning the default constructor wouldn't solve the "narrow contract" issue: a moved-from object is in the same "disengaged" state as a default-constructed one.
stdext::inplace_function<void()> f; f(); // UB okay?
stdext::inplace_function<void()> g = [](){}; f = std::move(g); g(); // UB okay?
Also, if we banned the default constructor then inplace_function
wouldn't model C++2a std::semiregular
. Default-constructibility is overrated, but for something that's supposed to play well with the STL, I'd be hesitant in practice to make it not-default-constructible.
If g()
above is allowed to be out-of-contract and have UB, then we don't need to eliminate the default constructor; we just say that f()
above is equally out-of-contract and has UB. Note that calling a default-constructed inplace_function
would have UB, but merely having one (creating one, copying one, etc) wouldn't have UB.
from sg14.
A default constructed inplace_function
object does not have to be disengaged.
A default constructor just enforces the intended default semantics of the library author.
One could very well implement inplace_function
so as to just ignore everything in a default constructed state. As I said in my last post this is very useful in the case of event handlers.
In the disengaged state there are again multiple approaches that one may take.
Ub is a valid approach, but one may also want to throw an exception, early abort, or call a custom event handler perhaps in a debug build.
But the whole discussion does not really have to do with either of these.
The point is, should the library support embedded projects?
If not that's fine.
If it should, then it needs to do away with the macro approach because it's just completely broken.
In this case there are two approaches:
- Not allow embedded developers to customize the disengaged state in an arbitrary way. In this case a simple configuration define should suffice and the library must settle on some sane default behavior.
- Allow embedded developers to customize the disengaged state which means choosing between UB, aborting, custom handler etc. Since using macros for this is broken, another approach must be taken that has first-class supported by the library and most likely also applies to all users not only to embedded developers.
As such there are three choices that can be taken:
- Do not support embedded developers.
- Support embedded developers with a switch and some default catch-all behavior.
- Add a custom configuration facility (such as policies) as a first class citizen of the library.
from sg14.
Related Issues (20)
- [slot_map] constructors HOT 1
- [inplace_function] benchmark HOT 4
- [slot_map] Can you get a key using an iterator? HOT 5
- [slot_map] R1/R2 criticisms HOT 12
- [inplace_function] Ambiguous overloads. HOT 5
- [inplace_function] Should [](){return 42;} be convertible to inplace_function<void()>? HOT 1
- [inplace_function] make it usable without exceptions HOT 2
- inplace_function doesn't handle arguments with rvalue references well HOT 1
- [inplace function] Problems assigning if my starting point is empty/nullptr HOT 4
- [inplace_function] Const-callability means thread-safety HOT 4
- [inplace_function] Giant Sized Buffers Required for Compilation HOT 5
- volatile inplace_function fails to compile HOT 6
- License is missing on some includes HOT 1
- `sg14::ring_span`: clear () member function gone awol HOT 2
- Provide CMake & Conan installation support
- slot_map: custom key causing clang to seg fault. HOT 1
- [inplace_function] usage at shared library boundaries HOT 5
- sg14::span_ring: class template argument deduction
- [inplace_function.h] Copyright holder HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from sg14.