| /* Offset types for GDB. | 
 |  | 
 |    Copyright (C) 2017-2024 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 <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | /* Define an "offset" type.  Offset types are distinct integer types | 
 |    that are used to represent an offset into anything that is | 
 |    addressable.  For example, an offset into a DWARF debug section. | 
 |    The idea is catch mixing unrelated offset types at compile time, in | 
 |    code that needs to manipulate multiple different kinds of offsets | 
 |    that are easily confused.  They're safer to use than native | 
 |    integers, because they have no implicit conversion to anything. | 
 |    And also, since they're implemented as "enum class" strong | 
 |    typedefs, they're still integers ABI-wise, making them a bit more | 
 |    efficient than wrapper structs on some ABIs. | 
 |  | 
 |    Some properties of offset types, loosely modeled on pointers: | 
 |  | 
 |    - You can compare offsets of the same type for equality and order. | 
 |      You can't compare an offset with an unrelated type. | 
 |  | 
 |    - You can add/substract an integer to/from an offset, which gives | 
 |      you back a shifted offset. | 
 |  | 
 |    - You can subtract two offsets of the same type, which gives you | 
 |      back the delta as an integer (of the enum class's underlying | 
 |      type), not as an offset type. | 
 |  | 
 |    - You can't add two offsets of the same type, as that would not | 
 |      make sense. | 
 |  | 
 |    However, unlike pointers, you can't deference offset types.  */ | 
 |  | 
 | #ifndef COMMON_OFFSET_TYPE_H | 
 | #define COMMON_OFFSET_TYPE_H | 
 |  | 
 | /* Declare TYPE as being an offset type.  This declares the type and | 
 |    enables the operators defined below.  */ | 
 | #define DEFINE_OFFSET_TYPE(TYPE, UNDERLYING)	\ | 
 |   enum class TYPE : UNDERLYING {};		\ | 
 |   void is_offset_type (TYPE) | 
 |  | 
 | /* The macro macro is all you need to know use offset types.  The rest | 
 |    below is all implementation detail.  */ | 
 |  | 
 | /* For each enum class type that you want to support arithmetic | 
 |    operators, declare an "is_offset_type" overload that has exactly | 
 |    one parameter, of type that enum class.  E.g.,: | 
 |  | 
 |      void is_offset_type (sect_offset); | 
 |  | 
 |    The function does not need to be defined, only declared. | 
 |    DEFINE_OFFSET_TYPE declares this. | 
 |  | 
 |    A function declaration is preferred over a traits type, because the | 
 |    former allows calling the DEFINE_OFFSET_TYPE macro inside a | 
 |    namespace to define the corresponding offset type in that | 
 |    namespace.  The compiler finds the corresponding is_offset_type | 
 |    function via ADL. | 
 | */ | 
 |  | 
 | /* Adding or subtracting an integer to an offset type shifts the | 
 |    offset.  This is like "PTR = PTR + INT" and "PTR += INT".  */ | 
 |  | 
 | #define DEFINE_OFFSET_ARITHM_OP(OP)					\ | 
 |   template<typename E,							\ | 
 | 	   typename = decltype (is_offset_type (std::declval<E> ()))>	\ | 
 |   constexpr E								\ | 
 |   operator OP (E lhs, typename std::underlying_type<E>::type rhs)	\ | 
 |   {									\ | 
 |     using underlying = typename std::underlying_type<E>::type;		\ | 
 |     return (E) (static_cast<underlying> (lhs) OP rhs);			\ | 
 |   }									\ | 
 | 									\ | 
 |   template<typename E,							\ | 
 | 	   typename = decltype (is_offset_type (std::declval<E> ()))>	\ | 
 |   constexpr E								\ | 
 |   operator OP (typename std::underlying_type<E>::type lhs, E rhs)	\ | 
 |   {									\ | 
 |     using underlying = typename std::underlying_type<E>::type;		\ | 
 |     return (E) (lhs OP static_cast<underlying> (rhs));			\ | 
 |   }									\ | 
 | 									\ | 
 |   template<typename E,							\ | 
 | 	   typename = decltype (is_offset_type (std::declval<E> ()))>	\ | 
 |   E &									\ | 
 |   operator OP ## = (E &lhs, typename std::underlying_type<E>::type rhs)	\ | 
 |   {									\ | 
 |     using underlying = typename std::underlying_type<E>::type;		\ | 
 |     lhs = (E) (static_cast<underlying> (lhs) OP rhs);			\ | 
 |     return lhs;								\ | 
 |   } | 
 |  | 
 | DEFINE_OFFSET_ARITHM_OP(+) | 
 | DEFINE_OFFSET_ARITHM_OP(-) | 
 |  | 
 | /* Adding two offset types doesn't make sense, just like "PTR + PTR" | 
 |    doesn't make sense.  This is defined as a deleted function so that | 
 |    a compile error easily brings you to this comment.  */ | 
 |  | 
 | template<typename E, | 
 | 	 typename = decltype (is_offset_type (std::declval<E> ()))> | 
 | constexpr typename std::underlying_type<E>::type | 
 | operator+ (E lhs, E rhs) = delete; | 
 |  | 
 | /* Subtracting two offset types, however, gives you back the | 
 |    difference between the offsets, as an underlying type.  Similar to | 
 |    how "PTR2 - PTR1" returns a ptrdiff_t.  */ | 
 |  | 
 | template<typename E, | 
 | 	 typename = decltype (is_offset_type (std::declval<E> ()))> | 
 | constexpr typename std::underlying_type<E>::type | 
 | operator- (E lhs, E rhs) | 
 | { | 
 |   using underlying = typename std::underlying_type<E>::type; | 
 |   return static_cast<underlying> (lhs) - static_cast<underlying> (rhs); | 
 | } | 
 |  | 
 | #endif /* COMMON_OFFSET_TYPE_H */ |