From patchwork Mon Oct 17 13:51:25 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 16573 Received: (qmail 104138 invoked by alias); 17 Oct 2016 13:51:39 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 104124 invoked by uid 89); 17 Oct 2016 13:51:38 -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=1360, coding, sk:explici, yc 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, 17 Oct 2016 13:51:28 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) (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 35B017AE9D; Mon, 17 Oct 2016 13:51:27 +0000 (UTC) Received: from [127.0.0.1] (ovpn01.gateway.prod.ext.ams2.redhat.com [10.39.146.11]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u9HDpPSv020219; Mon, 17 Oct 2016 09:51:26 -0400 Subject: [PATCH v4] Introduce gdb::unique_ptr To: Tom Tromey References: <1476448162-20203-1-git-send-email-palves@redhat.com> <209d21ef-f3cb-6188-8654-943b2254c9b3@redhat.com> <87bmyklqac.fsf@tromey.com> Cc: gdb-patches@sourceware.org From: Pedro Alves Message-ID: <70152188-5427-1eb1-a2bf-d77251d36a78@redhat.com> Date: Mon, 17 Oct 2016 14:51:25 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.2.0 MIME-Version: 1.0 In-Reply-To: <87bmyklqac.fsf@tromey.com> On 10/16/2016 08:13 AM, Tom Tromey wrote: >>>>>> "Pedro" == Pedro Alves writes: > > Pedro> For managing malloc pointers, this adds a gdb::unique_malloc_ptr > Pedro> "specialization" with a custom xfree deleter. > > I found something surprising while using this patch: it can't be used in > a .y file, due to the malloc->xmalloc renaming done in the .y.c rule. > I puzzled over the error about "unique_xmalloc_ptr" for a while until I > remembered this. Oh, I saw that too, when I tried to use it to manage the result of ui_file_xstrdup in c-exp.y, I think, but then added added ui_file_as_string instead, and then I completely forgot about that! FWIW, I also puzzled over the error for a while... > One option would be to fix up that renaming, maybe something like: > > - -e 's/\([^x]\)malloc/\1xmalloc/g' \ > - -e 's/\([^x_]\)malloc/\1xmalloc/g' \ > > Another would be to rename the class unique_xmalloc_ptr after all. Guess renaming makes sense anyway. I renamed it, and forced-pushed this along with the rest of the series (meanwhile split into smaller pieces and a few more patches added; it's now at 32 patches...) to the users/palves/cxx-eliminate-cleanups branch. While splitting up the series, I noticed that it'd be nice to support implicit construction from NULL, since that works in C++11, so I tweaked the patch accordingly. The problem was that the not-a-type-based nullptr emulation wouldn't trigger because it would require two conversions, because I had used a separate class... Inlining that in auto_ptr_base makes things work, and, ends up looking simpler even... So here's v4. WDTY? From f35cf129b55e331c1edd3dfd4bf633a9bd649418 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Mon, 17 Oct 2016 14:11:23 +0100 Subject: [PATCH] Introduce gdb::unique_ptr New in v4: - implict construction from NULL allowed too now, for: gdb::unique_ptr func (....) { return NULL; } - Rename unique_malloc_ptr to unique_xmalloc_ptr. New in v3: - Comment typos fixed. Detail things moved to detail namespace. New in v2: - considering a move to C++11, it no longer makes much sense to add a base class just for the one instance of the safe bool idiom. That's now inlined into unique_ptr directly. - added missing />= comparison operators. - simplified the implementation even further, by removing the "ref" class, and adding converting contructors instead. This actually fixes this use case: unique_ptr func_returning_unique_ptr (.....); ... unique_ptr ptr = func_returning_unique_ptr (.....); Looks like GCC's std::auto_ptr is buggy in that respect, because that doesn't actually work with std::auto_ptr, despite the comments indicating otherwise, and I was copying the comment/bug. - comments updated/rewritten throughout, including the git commit log, to hopefully clarify things. I think that the code looks clearer and less "magic" now. I'd like to get this in soon to unblock Tromey's C++ series, and more. We can move along with the C++11 discussion / plan in parallel. Any objections? I'll be more than happy to remove this when we finally make C++11 a requirement. But I don't want to rush that. We should make sure that the buildbot's buildslaves are ready, for example. ~~~ 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. GDB currently supports building with a C++03 compiler. 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 -- moving (i.e., transfer of ownership of the managed pointer between smart pointers) 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 objects allocated with xmalloc. Since std::unique_ptr's API is quite nice, and eventually we'd like to move to C++11, this patch adds a C++03-compatible smart pointer that exposes the subset of the std::unique_ptr API that we're interested in. An advantage is that whenever we start requiring C++11, we won't have to learn a new API. Meanwhile, this allows continuing to support building with a C++03 compiler. Since C++03 doesn't support rvalue references (boost gets close to emulating them, but it's not fully transparent to user code), the C++03 std::unique_ptr emulation here doesn't try hard to prevent accidentally moving, which is where most of complication of a more thorough emulation would be. Instead, we rely on the fact that GDB will be usually compiled with a C++11 compiler, and use the real std::unique_ptr in that case to catch such accidental moves. IOW, the goal here is to allow code that would be correct using std::unique_ptr to be equally correct in C++03 mode, and, just as efficient. The C++03 version was originally based on GCC 7.0's std::auto_ptr and then heavily customized to behave more like C++11's std::unique_ptr: - Support for custom (stateless) deleters. (Support for stateful deleters could be added, if necessary.) - unique_ptr partial specialization (auto_ptr does not know to use delete[]). - Support for all of 'ptr != NULL', 'ptr == NULL' and 'if (ptr)' using the safe bool idiom. - Assignment from NULL. - Allow using all of 'ptr != NULL', 'ptr == NULL' and 'if (ptr)' using the safe bool idiom to emulate C++11's explicit bool conversion. - Variable names un-uglified (ie., no leading __ prefix everywhere). - Formatting made to follow GDB's coding conventions, including comment style. - Initialization and assignment from NULL (std::unique_ptr allows "ptr = nullptr" instead, while std::auto_ptr allows neither.) - Converting "move" constructors done differently in order to truly support: unique_ptr func_returning_unique_ptr (.....); ... unique_ptr ptr = func_returning_unique_ptr (.....); At this point, it no longer shares much at all with the original file, but, that's the history. See comments in the code to find out more. 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 xmalloc pointers, this adds a gdb::unique_xmalloc_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. --- gdb/common/common-defs.h | 3 + gdb/common/gdb_unique_ptr.h | 360 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 gdb/common/gdb_unique_ptr.h diff --git a/gdb/common/common-defs.h b/gdb/common/common-defs.h index b82906f..5426dd5 100644 --- a/gdb/common/common-defs.h +++ b/gdb/common/common-defs.h @@ -82,4 +82,7 @@ #define EXTERN_C_PUSH extern "C" { #define EXTERN_C_POP } +/* Pull in gdb::unique_ptr and gdb::unique_xmalloc_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..52b5c4b --- /dev/null +++ b/gdb/common/gdb_unique_ptr.h @@ -0,0 +1,360 @@ +/* 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 defines a C++ owning smart pointer that exposes a + subset of the std::unique_ptr API. + + In fact, when compiled with a C++11 compiler, gdb::unique_ptr + actually _is_ std::unique_ptr. When compiled with a C++03 compiler + OTOH, it's an hand coded std::unique_ptr emulation that assumes + code is correct and doesn't try to be too smart. + + This supports custom deleters, but not _stateful_ deleters, so you + can't use those in C++11 mode either. Only the managed pointer is + stored in the smart pointer. That could be changed; it simply + wasn't found necessary. + + At the end of the file you'll find a gdb::unique_ptr partial + specialization that uses a custom (stateless) deleter: + gdb::unique_xmalloc_ptr. That is used to manage pointers to + objects allocated with xmalloc. + + The C++03 version was originally based on GCC 7.0's std::auto_ptr + and then heavily customized to behave more like C++11's + std::unique_ptr, but at this point, it no longer shares much at all + with the original file. But, that's the history and the reason for + the copyright's starting year. + + The C++03 version lets you shoot yourself in the foot, since + similarly to std::auto_ptr, the copy constructor and assignment + operators actually move. Also, in the name of simplicity, no + effort is spent on using SFINAE to prevent invalid conversions, + etc. This is not really a problem, because the goal here is to + allow code that would be correct using std::unique_ptr to be + equally correct in C++03 mode, and, just as efficient. If client + code compiles correctly with a C++11 (or newer) compiler, we know + we're not doing anything invalid by mistake. + + Putting gdb::unique_ptr in standard containers is not supported. +*/ + +#ifndef GDB_UNIQUE_PTR_H +#define GDB_UNIQUE_PTR_H 1 + +#include + +namespace gdb +{ + +#if __cplusplus >= 201103 + +/* In C++11 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; } +}; + +namespace detail +{ +/* Type used to support implicit construction from NULL: + + gdb::unique_ptr func (....) + { + return NULL; + } + + and assignment from NULL: + + gdb::unique_ptr ptr (....); + ... + ptr = NULL; + + It is intentionally not defined anywhere. */ +struct nullptr_t; + +/* Base class of our unique_ptr emulation. Contains code common to + both unique_ptr and unique_ptr. */ + +template +class unique_ptr_base +{ +public: + typedef T *pointer; + typedef T element_type; + typedef D deleter_type; + + /* Takes ownership of a pointer. P is a pointer to an object of + element_type type. Defaults to NULL. */ + explicit unique_ptr_base (element_type *p = NULL) throw () : m_ptr (p) {} + + /* The "move" constructor. Really a copy constructor. Even though + std::unique_ptr is not copyable, our little simpler emulation + allows it, because: + + - There are no rvalue references in C++03. Our move emulation + instead relies on copy/assignment moving, like std::auto_ptr. + - RVO/NRVO requires an accessible copy constructor + */ + unique_ptr_base (const unique_ptr_base &other) throw () + : m_ptr (const_cast (other).release ()) {} + + /* Converting "move" constructor. Really an lvalue ref converting + constructor. This allows constructs such as: + + unique_ptr func_returning_unique_ptr (.....); + ... + unique_ptr ptr = func_returning_unique_ptr (.....); + */ + template + unique_ptr_base (const unique_ptr_base &other) throw () + : m_ptr (const_cast&> (other).release ()) {} + + /* The "move" assignment operator. Really an lvalue ref assignment + operator that actually moves. See comments above. */ + unique_ptr_base &operator= (const unique_ptr_base &other) throw () + { + reset (const_cast (other).release ()); + return *this; + } + + /* std::unique_ptr does not allow assignment, except from nullptr. + nullptr doesn't exist in C++03, so we allow assignment from NULL + instead [ptr = NULL;]. + */ + unique_ptr_base &operator= (detail::nullptr_t *) throw () + { + reset (); + return *this; + } + + ~unique_ptr_base () { call_deleter (); } + + /* "explicit operator bool ()" emulation using the safe bool + idiom. */ +private: + typedef void (unique_ptr_base::*explicit_operator_bool) () const; + void this_type_does_not_support_comparisons () const {} + +public: + operator explicit_operator_bool () const + { + return (m_ptr != NULL + ? &unique_ptr_base::this_type_does_not_support_comparisons + : 0); + } + + element_type *get () const throw () { return m_ptr; } + + element_type *release () throw () + { + pointer tmp = m_ptr; + m_ptr = NULL; + return tmp; + } + + void reset (element_type *p = NULL) throw () + { + if (p != m_ptr) + { + call_deleter (); + m_ptr = p; + } + } + +private: + + /* Call the deleter. Note we assume the deleter is "stateless". */ + void call_deleter () + { + D d; + + d (m_ptr); + } + + element_type *m_ptr; +}; + +} /* namespace detail */ + +/* Macro used to create a unique_ptr_base "partial specialization" -- + a subclass that uses a specific deleter. Basically this re-defines + the necessary constructors. This is necessary because C++03 + doesn't support inheriting constructors with "using". 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 TYPE is inheriting from. */ +#define DEFINE_UNIQUE_PTR(TYPE) \ +public: \ + explicit TYPE (T *p = NULL) throw () \ + : base_type (p) {} \ + \ + TYPE (const TYPE &other) throw () : base_type (other) {} \ + \ + TYPE (detail::nullptr_t *) throw () : base_type (NULL) {} \ + \ + template \ + TYPE (const detail::unique_ptr_base &other) throw () \ + : base_type (other) {} \ + \ + using base_type::operator=; + +/* Define non-array gdb::unique_ptr. */ + +template > +class unique_ptr : public detail::unique_ptr_base +{ + typedef detail::unique_ptr_base base_type; + + DEFINE_UNIQUE_PTR (unique_ptr) + +public: + /* 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 detail::unique_ptr_base +{ + typedef detail::unique_ptr_base base_type; + + DEFINE_UNIQUE_PTR (unique_ptr) + +public: + /* Indexing operator. */ + T &operator[] (size_t i) const { return this->get ()[i]; } +}; + +/* Comparison operators. */ + +template +inline bool +operator== (const detail::unique_ptr_base &x, + const detail::unique_ptr_base &y) +{ return x.get() == y.get(); } + +template +inline bool +operator!= (const detail::unique_ptr_base &x, + const detail::unique_ptr_base &y) +{ return x.get() != y.get(); } + +template +inline bool +operator< (const detail::unique_ptr_base &x, + const detail::unique_ptr_base &y) +{ return x.get() < y.get (); } + +template +inline bool +operator<= (const detail::unique_ptr_base &x, + const detail::unique_ptr_base &y) +{ return !(y < x); } + +template +inline bool +operator> (const detail::unique_ptr_base &x, + const detail::unique_ptr_base &y) +{ return y < x; } + +template +inline bool +operator>= (const detail::unique_ptr_base &x, + const detail::unique_ptr_base &y) +{ return !(x < y); } + +/* std::move "emulation". This is as simple as it can be -- no + attempt is made to emulate rvalue references. Instead relies on + the fact that gdb::unique_ptr has move semantics like + std::auto_ptr. I.e., copy/assignment actually moves. */ + +template +unique_ptr +move (unique_ptr v) +{ + return v; +} + +#endif /* C++11 */ + +/* Define gdb::unique_xmalloc_ptr, a gdb::unique_ptr that manages + xmalloc'ed memory. */ + +/* The deleter for gdb::unique_xmalloc_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_xmalloc_ptr + = std::unique_ptr>; + +#else /* C++11 */ + +/* In C++03, we don't have template aliases, so we need to define a + subclass instead, and re-define the constructors, because C++03 + doesn't support inheriting constructors either. */ + +template +class unique_xmalloc_ptr : public unique_ptr > +{ + typedef unique_ptr > base_type; + + DEFINE_UNIQUE_PTR (unique_xmalloc_ptr) +}; + +#endif /* C++11 */ + +} /* namespace gdb */ + +#endif /* GDB_UNIQUE_PTR_H */