text: /*
      pybind11/typeid.h: Convenience wrapper classes for basic Python types
  
      Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
  
      All rights reserved. Use of this source code is governed by a
      BSD-style license that can be found in the LICENSE file.
  */
  
  #pragma once
  
  #include "common.h"
  #include <utility>
  #include <type_traits>
  
  NAMESPACE_BEGIN(pybind11)
  
  /* A few forward declarations */
  class object;
  class str;
  class object;
  class dict;
  class iterator;
  namespace detail { class accessor; }
  
  /// Holds a reference to a Python object (no reference counting)
  class handle {
  public:
      handle() : m_ptr(nullptr) { }
      handle(const handle &other) : m_ptr(other.m_ptr) { }
      handle(PyObject *ptr) : m_ptr(ptr) { }
      PyObject *ptr() { return m_ptr; }
      const PyObject *ptr() const { return m_ptr; }
      void inc_ref() const { Py_XINCREF(m_ptr); }
      void dec_ref() const { Py_XDECREF(m_ptr); }
      int ref_count() const { return (int) Py_REFCNT(m_ptr); }
      handle get_type() { return (PyObject *) Py_TYPE(m_ptr); }
      inline iterator begin();
      inline iterator end();
      inline detail::accessor operator[](handle key);
      inline detail::accessor operator[](const char *key);
      inline detail::accessor attr(handle key);
      inline detail::accessor attr(const char *key);
      inline pybind11::str str() const;
      template <typename T> T cast();
      template <typename ... Args> object call(Args&&... args_);
      operator bool() const { return m_ptr != nullptr; }
  protected:
      PyObject *m_ptr;
  };
  
  /// Holds a reference to a Python object (with reference counting)
  class object : public handle {
  public:
      object() { }
      object(const object &o) : handle(o) { inc_ref(); }
      object(const handle &h, bool borrowed) : handle(h) { if (borrowed) inc_ref(); }
      object(PyObject *ptr, bool borrowed) : handle(ptr) { if (borrowed) inc_ref(); }
      object(object &&other) { m_ptr = other.m_ptr; other.m_ptr = nullptr; }
      ~object() { dec_ref(); }
  
      object& operator=(object &other) {
          Py_XINCREF(other.m_ptr);
          Py_XDECREF(m_ptr);
          m_ptr = other.m_ptr;
          return *this;
      }
  
      object& operator=(object &&other) {
          if (this != &other) {
              PyObject *temp = m_ptr;
              m_ptr = other.m_ptr;
              other.m_ptr = nullptr;
              Py_XDECREF(temp);
          }
          return *this;
      }
  };
  
  class iterator : public object {
  public:
      iterator(PyObject *obj, bool borrowed = false) : object(obj, borrowed) { ++*this; }
      iterator& operator++() {
          if (ptr())
              value = object(PyIter_Next(ptr()), false);
          return *this;
      }
      bool operator==(const iterator &it) const { return *it == **this; }
      bool operator!=(const iterator &it) const { return *it != **this; }
      object operator*() { return value; }
      const object &operator*() const { return value; }
      bool check() const { return PyIter_Check(ptr()); }
  private:
      object value;
  };
  
  NAMESPACE_BEGIN(detail)
  inline PyObject *get_function(PyObject *value) {
      if (value == nullptr)
          return nullptr;
  #if PY_MAJOR_VERSION >= 3
      if (PyInstanceMethod_Check(value))
          value = PyInstanceMethod_GET_FUNCTION(value);
  #endif
      if (PyMethod_Check(value))
          value = PyMethod_GET_FUNCTION(value);
      return value;
  }
  
  class accessor {
  public:
      accessor(PyObject *obj, PyObject *key, bool attr)
          : obj(obj), key(key), attr(attr) { Py_INCREF(key);  }
      accessor(PyObject *obj, const char *key, bool attr)
          : obj(obj), key(PyUnicode_FromString(key)), attr(attr) { }
      accessor(const accessor &a) : obj(a.obj), key(a.key), attr(a.attr)
          { Py_INCREF(key); }
      ~accessor() { Py_DECREF(key); }
  
      void operator=(accessor o) { operator=(object(o)); }
  
      void operator=(const handle &h) {
          if (attr) {
              if (PyObject_SetAttr(obj, key, (PyObject *) h.ptr()) < 0)
                  throw std::runtime_error("Unable to set object attribute");
          } else {
              if (PyObject_SetItem(obj, key, (PyObject *) h.ptr()) < 0)
                  throw std::runtime_error("Unable to set object item");
          }
      }
  
      operator object() const {
          object result(attr ? PyObject_GetAttr(obj, key)
                             : PyObject_GetItem(obj, key), false);
          if (!result) PyErr_Clear();
          return result;
      }
  
      operator bool() const {
          if (attr) {
              return (bool) PyObject_HasAttr(obj, key);
          } else {
              object result(PyObject_GetItem(obj, key), false);
              if (!result) PyErr_Clear();
              return (bool) result;
          }
      };
  
  private:
      PyObject *obj;
      PyObject *key;
      bool attr;
  };
  
  struct list_accessor {
  public:
      list_accessor(PyObject *list, size_t index) : list(list), index(index) { }
      void operator=(list_accessor o) { return operator=(object(o)); }
      void operator=(const handle &o) {
          o.inc_ref(); // PyList_SetItem steals a reference
          if (PyList_SetItem(list, (ssize_t) index, (PyObject *) o.ptr()) < 0)
              throw std::runtime_error("Unable to assign value in Python list!");
      }
      operator object() const {
          PyObject *result = PyList_GetItem(list, (ssize_t) index);
          if (!result)
              throw std::runtime_error("Unable to retrieve value from Python list!");
          return object(result, true);
      }
  private:
      PyObject *list;
      size_t index;
  };
  
  struct tuple_accessor {
  public:
      tuple_accessor(PyObject *tuple, size_t index) : tuple(tuple), index(index) { }
      void operator=(tuple_accessor o) { return operator=(object(o)); }
      void operator=(const handle &o) {
          o.inc_ref(); // PyTuple_SetItem steals a reference
          if (PyTuple_SetItem(tuple, (ssize_t) index, (PyObject *) o.ptr()) < 0)
              throw std::runtime_error("Unable to assign value in Python tuple!");
      }
      operator object() const {
          PyObject *result = PyTuple_GetItem(tuple, (ssize_t) index);
          if (!result)
              throw std::runtime_error("Unable to retrieve value from Python tuple!");
          return object(result, true);
      }
  private:
      PyObject *tuple;
      size_t index;
  };
  
  struct dict_iterator {
  public:
      dict_iterator(PyObject *dict = nullptr, ssize_t pos = -1) : dict(dict), pos(pos) { }
      dict_iterator& operator++() {
          if (!PyDict_Next(dict, &pos, &key, &value))
              pos = -1;
          return *this;
      }
      std::pair<object, object> operator*() {
          return std::make_pair(object(key, true), object(value, true));
      }
      bool operator==(const dict_iterator &it) const { return it.pos == pos; }
      bool operator!=(const dict_iterator &it) const { return it.pos != pos; }
  private:
      PyObject *dict, *key, *value;
      ssize_t pos = 0;
  };
  
  #if PY_MAJOR_VERSION >= 3
  using ::PyLong_AsUnsignedLongLong;
  using ::PyLong_AsLongLong;
  #else
  inline long long PyLong_AsLongLong(PyObject *o) {
      if (PyInt_Check(o)) /// workaround: PyLong_AsLongLong doesn't accept 'int' on Python 2.x
          return (long long) PyLong_AsLong(o);
      else
          return ::PyLong_AsLongLong(o);
  }
  
  inline unsigned long long PyLong_AsUnsignedLongLong(PyObject *o) {
      if (PyInt_Check(o)) /// workaround: PyLong_AsUnsignedLongLong doesn't accept 'int' on Python 2.x
          return (unsigned long long) PyLong_AsUnsignedLong(o);
      else
          return ::PyLong_AsUnsignedLongLong(o);
  }
  #endif
  
  NAMESPACE_END(detail)
  
  inline detail::accessor handle::operator[](handle key) { return detail::accessor(ptr(), key.ptr(), false); }
  inline detail::accessor handle::operator[](const char *key) { return detail::accessor(ptr(), key, false); }
  inline detail::accessor handle::attr(handle key) { return detail::accessor(ptr(), key.ptr(), true); }
  inline detail::accessor handle::attr(const char *key) { return detail::accessor(ptr(), key, true); }
  inline iterator handle::begin() { return iterator(PyObject_GetIter(ptr())); }
  inline iterator handle::end() { return iterator(nullptr); }
  
  #define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, CvtStmt) \
      Name(const handle &h, bool borrowed) : Parent(h, borrowed) { CvtStmt; } \
      Name(const object& o): Parent(o) { CvtStmt; } \
      Name(object&& o): Parent(std::move(o)) { CvtStmt; } \
      Name& operator=(object&& o) { return static_cast<Name&>(object::operator=(std::move(o))); CvtStmt; } \
      Name& operator=(object& o) { return static_cast<Name&>(object::operator=(o)); CvtStmt; } \
      bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); }
  
  #define PYBIND11_OBJECT(Name, Parent, CheckFun) \
      PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, )
  
  #define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \
      PYBIND11_OBJECT(Name, Parent, CheckFun) \
      Name() : Parent() { }
  
  class str : public object {
  public:
      PYBIND11_OBJECT_DEFAULT(str, object, PyUnicode_Check)
      str(const char *s) : object(PyUnicode_FromString(s), false) { }
      operator const char *() const {
  #if PY_MAJOR_VERSION >= 3
          return PyUnicode_AsUTF8(m_ptr);
  #else
          m_temp = object(PyUnicode_AsUTF8String(m_ptr), false);
          if (m_temp.ptr() == nullptr)
              return nullptr;
          return PyString_AsString(m_temp.ptr());
  #endif
      }
  private:
  #if PY_MAJOR_VERSION < 3
      mutable object m_temp;
  #endif
  };
  
  inline pybind11::str handle::str() const {
      PyObject *str = PyObject_Str(m_ptr);
  #if PY_MAJOR_VERSION < 3
      PyObject *unicode = PyUnicode_FromEncodedObject(str, "utf-8", nullptr);
      Py_XDECREF(str); str = unicode;
  #endif
      return pybind11::str(str, false);
  }
  
  class bool_ : public object {
  public:
      PYBIND11_OBJECT_DEFAULT(bool_, object, PyBool_Check)
      bool_(bool value) : object(value ? Py_True : Py_False, true) { }
      operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; }
  };
  
  class int_ : public object {
  public:
      PYBIND11_OBJECT_DEFAULT(int_, object, PyLong_Check)
      template <typename T,
                typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
      int_(T value) {
          if (sizeof(T) <= sizeof(long)) {
              if (std::is_signed<T>::value)
                  m_ptr = PyLong_FromLong((long) value);
              else
                  m_ptr = PyLong_FromUnsignedLong((unsigned long) value);
          } else {
              if (std::is_signed<T>::value)
                  m_ptr = PyLong_FromLongLong((long long) value);
              else
                  m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value);
          }
      }
  
      template <typename T,
                typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
      operator T() const {
          if (sizeof(T) <= sizeof(long)) {
              if (std::is_signed<T>::value)
                  return (T) PyLong_AsLong(m_ptr);
              else
                  return (T) PyLong_AsUnsignedLong(m_ptr);
          } else {
              if (std::is_signed<T>::value)
                  return (T) detail::PyLong_AsLongLong(m_ptr);
              else
                  return (T) detail::PyLong_AsUnsignedLongLong(m_ptr);
          }
      }
  };
  
  class float_ : public object {
  public:
      PYBIND11_OBJECT_DEFAULT(float_, object, PyFloat_Check)
      float_(float value) : object(PyFloat_FromDouble((double) value), false) { }
      float_(double value) : object(PyFloat_FromDouble((double) value), false) { }
      operator float() const { return (float) PyFloat_AsDouble(m_ptr); }
      operator double() const { return (double) PyFloat_AsDouble(m_ptr); }
  };
  
  class slice : public object {
  public:
      PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check)
      slice(ssize_t start_, ssize_t stop_, ssize_t step_) {
          int_ start(start_), stop(stop_), step(step_);
          m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr());
      }
      bool compute(ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step, ssize_t *slicelength) const {
          return PySlice_GetIndicesEx(
  #if PY_MAJOR_VERSION >= 3
                  m_ptr,
  #else
                  (PySliceObject *) m_ptr,
  #endif
                  length, start, stop, step, slicelength) == 0;
      }
  };
  
  class capsule : public object {
  public:
      PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact)
      capsule(PyObject *obj, bool borrowed) : object(obj, borrowed) { }
      capsule(void *value, void (*destruct)(PyObject *) = nullptr) : object(PyCapsule_New(value, nullptr, destruct), false) { }
      template <typename T> operator T *() const {
          T * result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, nullptr));
          if (!result) throw std::runtime_error("Unable to extract capsule contents!");
          return result;
      }
  };
  
  class tuple : public object {
  public:
      PYBIND11_OBJECT_DEFAULT(tuple, object, PyTuple_Check)
      tuple(size_t size) : object(PyTuple_New((Py_ssize_t) size), false) { }
      size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
      detail::tuple_accessor operator[](size_t index) { return detail::tuple_accessor(ptr(), index); }
  };
  
  class dict : public object {
  public:
      PYBIND11_OBJECT(dict, object, PyDict_Check)
      dict() : object(PyDict_New(), false) { }
      size_t size() const { return (size_t) PyDict_Size(m_ptr); }
      detail::dict_iterator begin() { return (++detail::dict_iterator(ptr(), 0)); }
      detail::dict_iterator end() { return detail::dict_iterator(); }
      void clear() { PyDict_Clear(ptr()); }
  };
  
  class list : public object {
  public:
      PYBIND11_OBJECT(list, object, PyList_Check)
      list(size_t size = 0) : object(PyList_New((ssize_t) size), false) { }
      size_t size() const { return (size_t) PyList_Size(m_ptr); }
      detail::list_accessor operator[](size_t index) { return detail::list_accessor(ptr(), index); }
      void append(const object &object) { PyList_Append(m_ptr, (PyObject *) object.ptr()); }
  };
  
  class set : public object {
  public:
      PYBIND11_OBJECT(set, object, PySet_Check)
      set() : object(PySet_New(nullptr), false) { }
      size_t size() const { return (size_t) PySet_Size(m_ptr); }
      void add(const object &object) { PySet_Add(m_ptr, (PyObject *) object.ptr()); }
      void clear() { PySet_Clear(ptr()); }
  };
  
  class function : public object {
  public:
      PYBIND11_OBJECT_DEFAULT(function, object, PyFunction_Check)
  
      bool is_cpp_function() {
          PyObject *ptr = detail::get_function(m_ptr);
          return ptr != nullptr && PyCFunction_Check(ptr);
      }
  };
  
  class buffer : public object {
  public:
      PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer)
  
      buffer_info request(bool writable = false) {
          int flags = PyBUF_STRIDES | PyBUF_FORMAT;
          if (writable) flags |= PyBUF_WRITABLE;
          view = new Py_buffer();
          if (PyObject_GetBuffer(m_ptr, view, flags) != 0)
              throw error_already_set();
          std::vector<size_t> shape(view->ndim), strides(view->ndim);
          for (int i=0; i<view->ndim; ++i) {
              shape[i] = (size_t) view->shape[i];
              strides[i] = (size_t) view->strides[i];
          }
          return buffer_info(view->buf, view->itemsize, view->format,
                             view->ndim, shape, strides);
      }
      ~buffer() { if (view) { PyBuffer_Release(view); delete view; } }
  private:
      Py_buffer *view = nullptr;
  };
  
  NAMESPACE_BEGIN(detail)
  inline internals &get_internals() {
      static internals *internals_ptr = nullptr;
      if (internals_ptr)
          return *internals_ptr;
      handle builtins(PyEval_GetBuiltins());
      capsule caps(builtins["__pybind11__"]);
      if (caps.check()) {
          internals_ptr = caps;
      } else {
          internals_ptr = new internals();
          builtins["__pybind11__"] = capsule(internals_ptr);
      }
      return *internals_ptr;
  }
  
  inline std::string error_string() {
      std::string errorString;
      PyThreadState *tstate = PyThreadState_GET();
      if (tstate == nullptr)
          return "";
  
      if (tstate->curexc_type) {
          errorString += (const char *) handle(tstate->curexc_type).str();
          errorString += ": ";
      }
      if (tstate->curexc_value)
          errorString += (const char *) handle(tstate->curexc_value).str();
  
      return errorString;
  }
  
  inline handle get_object_handle(const void *ptr) {
      auto instances = get_internals().registered_instances;
      auto it = instances.find(ptr);
      if (it == instances.end())
          return handle();
      return it->second;
  }
  
  inline handle get_type_handle(const std::type_info &tp) {
      auto instances = get_internals().registered_types;
      auto it = instances.find(&tp);
      if (it == instances.end())
          return handle();
      return handle((PyObject *) it->second.type);
  }
  
  NAMESPACE_END(detail)
  NAMESPACE_END(pybind11)
  