[PATCH] D55045: Add a version of std::function that includes a few optimizations.

Louis Dionne via Phabricator reviews at reviews.llvm.org
Tue Dec 4 07:32:24 PST 2018


ldionne requested changes to this revision.
ldionne added a comment.
This revision now requires changes to proceed.

I think we need to use `std::launder` when accessing the function object through the small buffer. This is a problem we seem to have in the current implementation too.



================
Comment at: include/__config:98
 #  define _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION
+// Eliminate conditionals and pointer indirection from std::function.
+#  define _LIBCPP_ABI_OPTIMIZED_FUNCTION
----------------
Instead, I think this should be described as an "Unstable attempt to provide a more optimized `std::function`" or something along those lines. My understanding is that we're not bound to your current implementation for the new `std::function` -- we should figure out whatever is fastest and use that.


================
Comment at: include/functional:1476
+template<class _Fp, class _Ap, class _Rp, class ..._ArgTypes>
+class __func<_Fp, _Ap, _Rp(_ArgTypes...)> {
+  __compressed_pair<_Fp, _Ap> __f_;
----------------
We already have one of those in `__functional_03`, right? I understand both files can't be included at the same time, but I would consider changing the name to avoid confusion.


================
Comment at: include/functional:1483
+
+  const _Target& target() const { return __f_.first(); }
+  const _Alloc& allocator() const { return __f_.second(); }
----------------
`_LIBCPP_INLINE_VISIBILITY` here and on `allocator()` too. Also, those should be called `__target()` and `__allocator()` since they are internal, no?


================
Comment at: include/functional:1486
+
+  _LIBCPP_INLINE_VISIBILITY
+  explicit __func(_Target&& __f)
----------------
jsoyke wrote:
> Let me know if I'm using this macro correctly. It seems like it should be present on all non-public symbols?
Roughly speaking, it needs to appear on everything that we don't want to export from the dylib. It's a bit more complicated, but that's the general idea.


================
Comment at: include/functional:1535
 template<class _Rp, class ..._ArgTypes>
-class __base<_Rp(_ArgTypes...)>
+class __vfunc<_Rp(_ArgTypes...)>
 {
----------------
jsoyke wrote:
> No strong feelings on any of the class names I've used here. I was thinking vfunc = vtable based and pfunc = policy based, but I'd be fine with something more descriptive as well.
I'm trying to ponder whether changing this name would break the ABI. If someone explicitly instantiates a `std::function` in a shared object with default visibility, I think this is an ABI break because the dylib (if not recompiled) will export the vtable for `__base`, but the application (when recompiled against new headers) will try to find a definition for the vtable of `__vfunc`.

Now, I'm willing to assume that nobody did something like that. But even then, is there a possibility for an ABI break? @EricWF can you think of something?



================
Comment at: include/functional:1672
+// Policy contains information about how to copy, destroy, and move the
+// undelying functor. You can think of it as a vtable of sorts.
+struct __policy
----------------
Typo: underlying


================
Comment at: include/functional:1679
+    // True if this is the null policy (no value).
+    bool __is_null;
+
----------------
I think this can be made `const`.


================
Comment at: include/functional:1682
+    // The target type. May be null if RTTI is disabled.
+    const std::type_info* type_info;
+
----------------
Mangle to `__type_info` or similar. I know it's not necessary because users can't `#define type_info`, but I find it better to be consistent and mangle everything that's not exposed to users.


================
Comment at: include/functional:1773
+  static __invoker __create() {
+    return __invoker(__choose_call<_Fun>(__use_small_storage<_Fun>()));
+  }
----------------
This would be more readable: `__invoker(__use_small_storage<_Fun>() ? __call_small<_Fun> : __call_large<_Fun>)`.


================
Comment at: include/functional:2079
+      if (__policy_->__is_null || typeid(_Tp) != *__policy_->type_info)
+          return 0;
+      if (__policy_->__clone)  // Out of line storage.
----------------
`nullptr`?


================
Comment at: include/functional:2081
+      if (__policy_->__clone)  // Out of line storage.
+          return (const _Tp*)__buf_.__large;
+      else
----------------
`reinterpret_cast`


================
Comment at: include/functional:2170
     _LIBCPP_INLINE_VISIBILITY
-        _LIBCPP_EXPLICIT operator bool() const _NOEXCEPT {return __f_;}
+        _LIBCPP_EXPLICIT operator bool() const _NOEXCEPT {return (bool)(__f_);}
 
----------------
`static_cast<bool>`? Just a matter of style.


================
Comment at: include/functional:2237
 {
-    *this = nullptr;
-    if (__f.__f_ == 0)
-        __f_ = 0;
-    else if ((void *)__f.__f_ == &__f.__buf_)
-    {
-        __f_ = __as_base(&__buf_);
-        __f.__f_->__clone(__f_);
-    }
-    else
-    {
-        __f_ = __f.__f_;
-        __f.__f_ = 0;
+    if (&__f != this) {
+        *this = nullptr;
----------------
We didn't check for that before, did we? IOW the implementation worked transparently even in the case of a self move-assignment. Would it be possible to retain that property (and save this check in the current implementation)?


Repository:
  rCXX libc++

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D55045/new/

https://reviews.llvm.org/D55045





More information about the libcxx-commits mailing list