From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 18403 invoked by alias); 10 Oct 2016 16:46:47 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 18295 invoked by uid 89); 10 Oct 2016 16:46:46 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.2 required=5.0 tests=BAYES_00,RP_MATCHES_RCVD,SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=Safe, 7s, Automatic, foot X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 10 Oct 2016 16:46:36 +0000 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 14D008E670 for ; Mon, 10 Oct 2016 16:46:35 +0000 (UTC) Received: from cascais.lan (ovpn01.gateway.prod.ext.ams2.redhat.com [10.39.146.11]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u9AGkWFv024645 for ; Mon, 10 Oct 2016 12:46:34 -0400 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 1/3] Introduce gdb::unique_ptr Date: Mon, 10 Oct 2016 16:46:00 -0000 Message-Id: <1476117992-5689-2-git-send-email-palves@redhat.com> In-Reply-To: <1476117992-5689-1-git-send-email-palves@redhat.com> References: <1476117992-5689-1-git-send-email-palves@redhat.com> X-SW-Source: 2016-10/txt/msg00224.txt.bz2 Many make_cleanup uses in the code base are best eliminated by using a "owning" smart pointer to manage ownership of the resource automatically. The question is _which_ smart pointer. We have std::auto_ptr in C++03, but, as is collective wisdom by now, that's too easy to misuse, and has therefore been deprecated in C++11 and finally removed in C++17. It'd be nice to be able to use std::unique_ptr instead, which is the modern, safe std::auto_ptr replacement in C++11. In addition to extra safety -- ownership transfer must be explicit -- std::unique_ptr has (among others) one nice feature that std::auto_ptr doesn't --- ability to specify a custom deleter as template parameter. In gdb's context, that allows easily creating a smart pointer for memory allocated with xmalloc -- the smart pointer then knows to release with xfree instead of delete. This is particularly interesting when managing objects allocated in C libraries, and also, for C++-fying parts of GDB that interact with other parts that still return object allocated with malloc. Since std::unique_ptr is supposedly mostly a drop-in replacement for std::auto_ptr -- basically "upgrading" is usually a matter of find/replace and then fix the "bad" implicit ownership transfer cases, I thought of actually taking advantage of std::unique_ptr when GDB is compiled with a C++11 compiler. And then since we don't require C++11 yet, I wrote a very lightweight std::unique_ptr "emulation" for C++03. The emulation started out as a copy of GCC 7's std::auto_ptr, and then heavilly customized to make it behave more like std::unique_ptr: - unique_ptr specialization. auto_ptr does not know to use delete[]. - custom deleters (though only stateless deleters --- support for stateful deleters could be added, but I saw no need for those at this point). - support for all of 'ptr != NULL', 'ptr == NULL' and 'if (ptr)' using the safe bool idiom. - initialization and assignment from NULL (std::unique_ptr allows "ptr = nullptr" instead, while std::auto_ptr allows neither.) The "emulation" isn't perfect in the sense that just like std::auto_ptr, it defines a copy constructor with greedy ownership transfer. However, since if compiling gdb with a C++11 (or newer) compiler, we're actually using the real std::unique_ptr, as long as GDB builds with GCC 6 or later, or any other compiler that defaults to C++11 or later, then we know we're not misusing the C++03 version. I thought of putting the "emulation" / shim in the "std" namespace, so that when we start requiring C++11 at some point, no actual changes to users of the smart pointer throughout would be necessary. Putting things in the std namespace is technically undefined, however in practice it doesn't cause any issue with any compiler. However, thinking that people might be confused with seeing std::unique_ptr and thinking that we're actually requiring C++11 already, I put the new types in the "gdb" namespace instead. For managing malloc pointers, this adds a gdb::unique_malloc_ptr "specialization" with a custom xfree deleter. No actual use of any smart pointer is introduced in this patch. That'll be done in following patches. Tested (along with the rest of the series) on: - NetBSD 5.1 (gcc70 on the compile farm), w/ gcc 4.1.3 - x86-64 Fedora 23, gcc 5.3.1 (gnu++03) - x86-64 Fedora 23, and gcc 7.0 (gnu++14) gdb/ChangeLog: yyyy-mm-dd Pedro Alves * common/common-defs.h: Include "gdb_unique_ptr.h". * common/gdb_unique_ptr.h: New. * common/safe-bool.h: New. --- gdb/common/common-defs.h | 3 + gdb/common/gdb_unique_ptr.h | 363 ++++++++++++++++++++++++++++++++++++++++++++ gdb/common/safe-bool.h | 67 ++++++++ 3 files changed, 433 insertions(+) create mode 100644 gdb/common/gdb_unique_ptr.h create mode 100644 gdb/common/safe-bool.h diff --git a/gdb/common/common-defs.h b/gdb/common/common-defs.h index 0d8d100..52ecb08 100644 --- a/gdb/common/common-defs.h +++ b/gdb/common/common-defs.h @@ -79,4 +79,7 @@ #define EXTERN_C_PUSH extern "C" { #define EXTERN_C_POP } +/* Pull in gdb::unique_ptr and gdb::unique_malloc_ptr. */ +#include "common/gdb_unique_ptr.h" + #endif /* COMMON_DEFS_H */ diff --git a/gdb/common/gdb_unique_ptr.h b/gdb/common/gdb_unique_ptr.h new file mode 100644 index 0000000..1cda298 --- /dev/null +++ b/gdb/common/gdb_unique_ptr.h @@ -0,0 +1,363 @@ +/* gdb::unique_ptr, a simple std::unique_ptr replacement for C++03. + + Copyright (C) 2007-2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* gdb::unique_ptr maps to std::unique_ptr in C++11 mode, and to a + simplified emulation in C++03 mode. + + The emulation code was originally based on GCC 7.0's std::auto_ptr + and then heavily customized to behave more like std::unique_ptr + (T[] specialization, assignment from NULL, explicit bool + conversion, etc.). + + Our emulation actually lets you shoot yourself in the foot, just + like std::auto_ptr, since the copy ctor actually moves, but we know + that if gdb builds in C++11 mode, then we're not doing anything + unsafe. + + Note, our emulation does NOT support "stateful" custom deleters. + You can't pass a deleter argument to the constructor. Only the + managed pointer is stored. Turns out that we don't really need + stateful custom deleters in practice. + + At the end of the file you'll find a gdb::unique_ptr specialization + that uses a custom (stateless) deleter -- gdb::unique_malloc_ptr. +*/ + +#ifndef GDB_UNIQUE_PTR_H +#define GDB_UNIQUE_PTR_H 1 + +#include + +#include "safe-bool.h" + +namespace gdb +{ + +#if __cplusplus >= 201103 + +/* In C++ mode, all we need is import the standard + std::unique_ptr. */ +template using unique_ptr + = std::unique_ptr; + +/* Pull in move as well. */ +using std::move; + +#else /* C++11 */ + +/* Default destruction policy used by gdb::unique_ptr when no + deleter is specified. Uses delete. */ + +template +struct default_delete +{ + void operator () (T *ptr) const { delete ptr; } +}; + +/* Specialization for arrays. Uses delete[]. */ + +template +struct default_delete +{ + void operator () (T *ptr) const { delete [] ptr; } +}; + +/* Type used to support assignment from NULL: + + gdb::unique_ptr ptr (....); + ... + ptr = NULL; +*/ +struct unique_ptr_nullptr_t +{ +private: + struct private_type; +public: + /* Since null_type is private, the only way to construct this class + is by passing a NULL pointer. See unique_ptr_base::operator= + further below. */ + unique_ptr_nullptr_t (private_type *) {} +}; + +/* Base class of our unique_ptr emulation. Contains code common to + both the unique_ptr and unique_ptr. */ + +template +class unique_ptr_base : public safe_bool > +{ +public: + typedef T *pointer; + typedef T element_type; + typedef D deleter_type; + + template + struct unique_ptr_base_ref + { + T1 *m_ptr; + + explicit unique_ptr_base_ref (T1 *p): m_ptr (p) {} + }; + + typedef unique_ptr_base_ref ref_type; + + /* An unique_ptr is usually constructed from a raw pointer. P - a + pointer (defaults to NULL). This object now owns the object + pointed to by P. */ + explicit unique_ptr_base (element_type *p = NULL) throw() : m_ptr (p) {} + + /* Even though std::unique_ptr is not copyable, our little simpler + emulation allows it, because RVO/NRVO requires an accessible copy + constructor, and also because our move emulation relies on this. + + An unique_ptr_base can be constructed from another + unique_ptr_base. A is another unique_ptr_base of the same type. + + This object now owns the object previously owned by A, which has + given up ownership. */ + unique_ptr_base (unique_ptr_base& a) throw() : m_ptr (a.release ()) {} + + /* Similarly, we implement this simply to allow std::swap work + without having to provide our own implementation. We know that + if GDB compiles with real std::unique_ptr, this won't be called + "incorrectly". + + Assignment operator. A is another unique_ptr_base of the same + type. This object now owns the object previously owned by A, + which has given up ownership. The object that this one used to + own and track has been deleted. */ + unique_ptr_base& + operator= (unique_ptr_base &a) throw() + { + reset (a.release ()); + return *this; + } + + /* std::unique_ptr does not allow assignment, except from nullptr. + nullptr doesn't exist before C++11, so we allowing assignment + from NULL instead: + ptr = NULL; + */ + unique_ptr_base & + operator= (const unique_ptr_nullptr_t &) throw() + { + reset (); + return *this; + } + + /* When the unique_ptr_base goes out of scope, the object it owns is + deleted. If it no longer owns anything (i.e., get() is NULL, + then this has no effect. */ + ~unique_ptr_base () { call_deleter (); } + + /* "explicit operator bool" emulation using the safe bool idiom. */ + bool explicit_operator_bool () const + { + return m_ptr != NULL; + } + + /* Bypassing the smart pointer. + Returns the raw pointer being managed. + + You can get a copy of the pointer that this object owns, for + situations such as passing to a function which only accepts a raw + pointer. Note, this smarts pointer still owns the memory. */ + element_type *get () const throw() { return m_ptr; } + + /* Bypassing the smart pointer. Returns the raw pointer being + managed. + + You can get a copy of the pointer that this object owns, for + situations such as passing to a function which only accepts a raw + pointer. + + Note, this smart pointer no longer owns the memory. When this + object goes out of scope, nothing will happen. */ + element_type *release () throw() + { + pointer tmp = m_ptr; + m_ptr = NULL; + return tmp; + } + + /* Forcibly delete the managed object. P is a pointer (defaults to + NULL). This object now owns the object pointed to by P. The + previous object has been deleted. */ + void reset (element_type *p = NULL) throw() + { + if (p != m_ptr) + { + call_deleter (); + m_ptr = p; + } + } + + /* Automatic conversions. + + These operations convert an unique_ptr_base into and from an + unique_ptr_base_ref automatically as needed. This allows + constructs such as: + + unique_ptr func_returning_unique_ptr (.....); + ... + unique_ptr ptr = func_returning_unique_ptr (.....); + */ + unique_ptr_base (unique_ptr_base_ref ref) throw() + : m_ptr (ref.m_ptr) {} + + unique_ptr_base & + operator= (unique_ptr_base_ref ref) throw() + { + if (ref.m_ptr != this->get()) + { + call_deleter (); + m_ptr = ref.m_ptr; + } + return *this; + } + + template + operator unique_ptr_base_ref () throw() + { return unique_ptr_base_ref (this->release ()); } + + template + operator unique_ptr_base () throw() + { return unique_ptr_base (this->release ()); } + +private: + + /* Call the deleter. Note we assume the deleter is "stateless". */ + void call_deleter () + { + D d; + + d (m_ptr); + } + + element_type *m_ptr; +}; + +/* Macro used to create a unique_ptr_base "specialization" -- a + subclass that uses a specific deleter. Basically this re-defines + the necessary constructors. This is necessary because we can't + inherit constructors with "using" without C++11. While at it, we + inherit the assignment operator. TYPE is the name of the type + being defined. Assumes that 'base_type' is a typedef of the + baseclass UNIQUE_PTR is inheriting from. */ +#define DEFINE_UNIQUE_PTR(TYPE) \ + public: \ + explicit TYPE (T *p = NULL) throw() \ + : base_type (p) {} \ + TYPE (typename base_type::ref_type ref) throw() \ + : base_type (ref.m_ptr) {} \ + \ + using base_type::operator=; + +/* Finally, define gdb::unique_ptr. */ + +template > +class unique_ptr : public unique_ptr_base +{ + typedef unique_ptr_base base_type; + + DEFINE_UNIQUE_PTR (unique_ptr) + + /* Dereferencing. */ + T &operator* () const throw() { return *this->get (); } + T *operator-> () const throw() { return this->get (); } +}; + +/* gdb::unique_ptr specialization for T[]. */ + +template +class unique_ptr : public unique_ptr_base +{ + typedef unique_ptr_base base_type; + + DEFINE_UNIQUE_PTR (unique_ptr) + + /* Indexing operator. */ + T& operator[] (size_t i) const + { return this->get ()[i]; } +}; + +/* Comparison operators. */ + +template +inline bool +operator== (const unique_ptr_base& x, + const unique_ptr_base& y) +{ return x.get() == y.get(); } + +template +inline bool +operator!= (const unique_ptr_base& x, + const unique_ptr_base& y) +{ return x.get() != y.get(); } + +/* std::move "emulation". This is as simple as it can be -- relies on + the fact that our std::unique_ptr emulation actually behaves like + std::auto_ptr -- copy/assignment actually moves. */ + +template +unique_ptr_base +move (unique_ptr_base v) +{ + return v; +} + +#endif /* C++11 */ + +/* Define gdb::unique_malloc_ptr, a gdb::unique_ptr that manages + malloc'ed memory. */ + +/* The deleter for gdb::unique_malloc_ptr. Uses xfree. */ +template +struct xfree_deleter +{ + void operator() (T *ptr) const { xfree (ptr); } +}; + +#if __cplusplus >= 201103 + +/* In C++11, we just import the standard unique_ptr to our + namespace with a custom deleter. */ + +template using unique_malloc_ptr + = std::unique_ptr>; + +#else /* C++11 */ + +/* In C++03 mode, we need to define a subclass instead (and re-define + the constructors). */ + +template +class unique_malloc_ptr : public unique_ptr > +{ + typedef unique_ptr > base_type; + + DEFINE_UNIQUE_PTR (unique_malloc_ptr) +}; + +#endif /* C++11 */ + +} /* namespace gdb */ + +#endif /* GDB_UNIQUE_PTR_H */ diff --git a/gdb/common/safe-bool.h b/gdb/common/safe-bool.h new file mode 100644 index 0000000..b0075c3 --- /dev/null +++ b/gdb/common/safe-bool.h @@ -0,0 +1,67 @@ +/* Safe bool idiom implementation. + + Copyright (C) 2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef SAFE_BOOL_H +#define SAFE_BOOL_H 1 + +/* Helper classes used to implement the safe bool idiom, for compilers + that don't support "explicit operator bool()" (C++11). Classes + that want to support explicit boolean conversion inherit from + safe_bool (using CRTP) and implement the explicit_operator_bool + method. */ + +class safe_bool_base +{ +protected: + typedef void (safe_bool_base::*bool_type) () const; + void this_type_does_not_support_comparisons () const {} +}; + +/* Baseclass, using CRTP. */ + +template +class safe_bool : public safe_bool_base { +public: + operator bool_type () const + { + return (static_cast(this))->explicit_operator_bool () + ? &safe_bool_base::this_type_does_not_support_comparisons : 0; + } + +protected: + ~safe_bool () {} +}; + +/* Comparison operators. */ + +template +bool operator== (const safe_bool &lhs, const safe_bool &rhs) +{ + lhs.this_type_does_not_support_comparisons (); + return false; +} + +template +bool operator!= (const safe_bool &lhs, const safe_bool &rhs) +{ + lhs.this_type_does_not_support_comparisons (); + return false; +} + +#endif /* SAFE_BOOL_H */ -- 2.5.5