Blog coding and discussion of coding about JavaScript, PHP, CGI, general web building etc.

Monday, August 15, 2016

Dynamic casting for unique_ptr

Dynamic casting for unique_ptr


As it was the case in Boost, C++11 provides some functions for casting shared_ptr:

std::static_pointer_cast  std::dynamic_pointer_cast  std::const_pointer_cast  

I am wondering, however, why there are no equivalents functions for unique_ptr.

Consider the following simple example:

class A { virtual ~A(); ... }  class B : public A { ... }    unique_ptr pA(new B(...));    unique_ptr qA = std::move(pA); // This is legal since there is no casting  unique_ptr pB = std::move(pA); // This is not legal    // I would like to do something like:  // (Of course, it is not valid, but that would be the idea)  unique_ptr pB = std::move(std::dynamic_pointer_cast(pA));  

Is there any reason why this usage pattern is discouraged, and thus, equivalent functions to the ones present in shared_ptr are not provided for unique_ptr?

Answer by Mark Ransom for Dynamic casting for unique_ptr


The functions you refer to each make a copy of the pointer. Since you can't make a copy of a unique_ptr it doesn't make sense to provide those functions for it.

Answer by David for Dynamic casting for unique_ptr


This isn't an answer to why, but it is a way to do it...

std::unique_ptr x(new B);  std::unique_ptr y(dynamic_cast(x.get()));  if(y)      x.release();  

It's not entirely clean since for a brief moment 2 unique_ptrs think they own the same object. And as was commented, you'll also have to manage moving a custom deleter if you use one (but that's very rare).

Answer by Jonathan Wakely for Dynamic casting for unique_ptr


In addition to Mark Ransom's answer, a unique_ptr might not even store an X*.

If the deleter defines the type D::pointer then that's what is stored, and that might not be a real pointer, it only needs to meet the NullablePointer requirements and (if unique_ptr::get() is called) have an operator* that returns X&, but it isn't required to support casting to other types.

unique_ptr is quite flexible and doesn't necessarily behave very much like a built-in pointer type.

As requested, here is an example where the stored type is not a pointer, and therefore casting is not possible. It's a bit contrived, but wraps a made-up database API (defined as a C-style API) in a C++ RAII-style API. The OpaqueDbHandle type meets the NullablePointer requirements, but only stores an integer, which is used as a key to lookup the actual DB connection via some implementation-defined mapping. I'm not showing this as an example of great design, just as an example of using unique_ptr to manage a non-copyable, movable resource which is not a dynamically-allocated pointer, where the "deleter" doesn't just call a destructor and deallocate memory when the unique_ptr goes out of scope.

#include     // native database API  extern "C"  {    struct Db;    int db_query(Db*, const char*);    Db* db_connect();    void db_disconnect(Db*);  }    // wrapper API  class OpaqueDbHandle  {  public:    explicit OpaqueDbHandle(int id) : id(id) { }      OpaqueDbHandle(std::nullptr_t) { }    OpaqueDbHandle() = default;    OpaqueDbHandle(const OpaqueDbHandle&) = default;      OpaqueDbHandle& operator=(const OpaqueDbHandle&) = default;    OpaqueDbHandle& operator=(std::nullptr_t) { id = -1; return *this; }      Db& operator*() const;      explicit operator bool() const { return id > 0; }      friend bool operator==(const OpaqueDbHandle& l, const OpaqueDbHandle& r)    { return l.id == r.id; }    private:    friend class DbDeleter;    int id = -1;  };    inline bool operator!=(const OpaqueDbHandle& l, const OpaqueDbHandle& r)  { return !(l == r); }    struct DbDeleter  {    typedef OpaqueDbHandle pointer;      void operator()(pointer p) const;  };    typedef std::unique_ptr safe_db_handle;    safe_db_handle safe_connect();    int main()  {    auto db_handle = safe_connect();    (void) db_query(&*db_handle, "SHOW TABLES");  }      // defined in some shared library    namespace {    std::map connections;      // all active DB connections    std::list unused_connections;   // currently unused ones    int next_id = 0;    const unsigned cache_unused_threshold = 10;  }    Db& OpaqueDbHandle::operator*() const  {     return connections[id];  }    safe_db_handle safe_connect()  {    int id;    if (!unused_connections.empty())    {      id = unused_connections.back();      unused_connections.pop_back();    }    else    {      id = next_id++;      connections[id] = db_connect();    }    return safe_db_handle( OpaqueDbHandle(id) );  }    void DbDeleter::operator()(DbDeleter::pointer p) const  {    if (unused_connections.size() >= cache_unused_threshold)    {      db_disconnect(&*p);      connections.erase(p.id);    }    else      unused_connections.push_back(p.id);  }  

Answer by cdhowie for Dynamic casting for unique_ptr


To build on Dave's answer, this template function will attempt to move the contents of one unique_ptr to another of a different type.

  • If it returns true, then either:
    • The source pointer was empty. The destination pointer will be cleared to comply with the semantic request of "move the contents of this pointer (nothing) into that one."
    • The object pointed to by the source pointer was convertible to the destination pointer type. The source pointer will be empty, and the destination pointer will point to the same object it used to point to. The destination pointer will receive the source pointer's deleter (only when using the first overload).
  • If it returns false, the operation was unsuccessful. Neither pointer will have changed state.

template   bool dynamic_pointer_move(std::unique_ptr & dest,                            std::unique_ptr & src) {      if (!src) {          dest.reset();          return true;      }        T_DEST * dest_ptr = dynamic_cast(src.get());      if (!dest_ptr)          return false;        std::unique_ptr dest_temp(          dest_ptr,          std::move(src.get_deleter()));        src.release();      dest.swap(dest_temp);      return true;  }    template   bool dynamic_pointer_move(std::unique_ptr & dest,                            std::unique_ptr & src) {      if (!src) {          dest.reset();          return true;      }        T_DEST * dest_ptr = dynamic_cast(src.get());      if (!dest_ptr)          return false;        src.release();      dest.reset(dest_ptr);      return true;  }  

Note that the second overload is required for pointers declared std::unique_ptr and std::unique_ptr. The first function will not work because the first pointer will actually be of type std::unique_ptr > and the second of std::unique_ptr >; the deleter types won't be compatible and so the compiler will not allow you to use this function.

Answer by Bob F for Dynamic casting for unique_ptr


How about this for a C++11 approach:

template   std::unique_ptr unique_cast(std::unique_ptr &&src)  {      if (!src) return std::unique_ptr();        // Throws a std::bad_cast() if this doesn't work out      T_DEST *dest_ptr = &dynamic_cast(*src.get());        src.release();      return std::unique_ptr ret(dest_ptr);  }  

Answer by Emile Cormier for Dynamic casting for unique_ptr


If you're only going the be using the downcast pointer in a small scope, one alternative is to simply downcast the reference to the object being managed by the unique_ptr:

auto derived = dynamic_cast(*pBase);  derived.foo();  


Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 72

0 comments:

Post a Comment

Popular Posts

Powered by Blogger.