samedi 22 décembre 2018

std::bad_weak_ptr when inheriting std::shared_from_this from base class

First of all this issue seems to be related to using clang (any version) and libc++ higher than version 6.5.0 together.

I'm using the following idiom in my codebase to hide the implementation from the user:

#include <memory>

class myclass : public std::enable_shared_from_this<myclass> {
  class impl;

protected:
  myclass() = default;

public:
  myclass(myclass&&) = delete;
  myclass(myclass const&) = delete;
  myclass& operator=(myclass&&) = delete;
  myclass& operator=(myclass const&) = delete;
  virtual ~myclass() = default;

  static std::shared_ptr<myclass> create();

  int get();
};

class myclass::impl : public myclass {
public:
  using myclass::myclass;

  int get_impl() {
    return 33;
  }
};

std::shared_ptr<myclass> myclass::create() {
  return std::make_shared<impl>();
}

int myclass::get() {
  return static_cast<impl*>(this)->get_impl();
}

int main() {
  auto ref = myclass::create();
  return ref->shared_from_this()->get();
}

The idiom uses a private class which inherits and implements the public base class. When running this snippet under ubuntu 18.04 using clang++ -O3 -std=c++11 main.cpp && ./a.out the snippet crashes with the following output:

terminate called after throwing an instance of 'std::bad_weak_ptr'
  what():  bad_weak_ptr

with the following backtrace:

#0  0x00007ffa76a7de97 in raise () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffa76a7f801 in abort () from /lib/x86_64-linux-gnu/libc.so.6
#2  0x00007ffa774728fb in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffa77478d3a in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffa77478d95 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffa77478fe8 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x0000000000404f7c in std::__throw_bad_weak_ptr() ()
#7  0x0000000000404e92 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count(std::__weak_count<(__gnu_cxx::_Lock_policy)2> const&) ()
#8  0x0000000000404e2f in std::__shared_ptr<myclass, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<myclass, void>(std::__weak_ptr<myclass, (__gnu_cxx::_Lock_policy)2> const&) ()
#9  0x0000000000404df8 in std::shared_ptr<myclass>::shared_ptr<myclass, void>(std::weak_ptr<myclass> const&) ()
#10 0x0000000000403d2c in std::enable_shared_from_this<myclass>::shared_from_this() ()
#11 0x0000000000403ac8 in main ()

The test platform runs the following compiler and standard library:

clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.3.0

Although this code works fine on other platforms and/or compilers:

  • GCC 7.3.0 and "lib/gcc/x86_64-linux-gnu/7.3.0" works on the same platform
  • Clang 3.8.0 and "lib/gcc/x86_64-linux-gnu/6.5.0" works on another platform
  • Clang 7.0.1 and "lib/gcc/x86_64-linux-gnu/6.5.0" works on another platform
  • Windows MSVC 15.9.4

Overall it seems that the inheritance from std::shared_from_this isn't detected by std::make_shared when inheriting it from a parent class in libc++ higher than version 6.5.0 when using any clang version.

Is it possible to work around this while keeping the idiom?

What could possibly cause the defect here? Should this reported to any bugtracker (but which one is the rihght one for it since this seems like an interoperability issue between clang and libc++).

Aucun commentaire:

Enregistrer un commentaire