Python C/C++APIネタ

# Python CAPIのPyObjectの読み込み方備忘録

C/C++初心者から始まり、
なんとなくわかってきたので、PyObjectをとにかく読み込んでCの処理に入れてしまいたいためのクラスを作った。

沢山あると思ったが精査していくとPyObjectはC言語の4種類の型に集約可能だと思い固めてみた。
C++しんどかったけど、面白くてなってきてちょっとハマり中。

納得いっていないのが、「kind == 1 ~ kind == 8」あたりの処理ごとに入ってしまう。

テンプレートや関数ポインタとやらでダイナミックメタプログラミングにトライしてみたが、
処理速度の観点で試してみ田中ではこれが一番早かった。今のところ、、

```c++
#pragma once
#ifndef PYYOU_H
#define PYYOU_H

#define PY_SSIZE_T_CLEAN
#include <Python.h>

std::size_t error_n = (std::size_t)(-1);

class pyview {
   public:
    PyObject* py = NULL;
    int kind = 0;
    uint8_t* data_8 = nullptr;
    uint16_t* data_16 = nullptr;
    uint32_t* data_32 = nullptr;
    uint64_t* data_64 = nullptr;

   protected:
    std::size_t size_ = (std::size_t)-1;
    bool be_hash_clear = false;
    bool be_ref_clear = false;
    bool is_sequence = true;

   public:
    pyview() {}
    pyview(nullptr_t) : py(NULL), kind(0) {}

    pyview(PyObject*& o) : py(o) {
        size_ = (std::size_t)PyObject_Length(o);
        PyErr_Clear();
        open();
    }
    pyview(PyObject*& o, std::size_t len) : py(o), size_(len) { open(); }

    const void open() {
        PyObject* o = py;
        if(size_ == error_n || size_ == 1) {
            be_hash_clear = true;
            if(PyNumber_Check(py) || PyBool_Check(py) || py == Py_None) {
                data_64 = new uint64_t[1];
                data_64[0] = (uint64_t)PyObject_Hash(py);
                is_sequence = false;
                return;
            } else if(size_ == error_n) {
                py = PySequence_Tuple(py);
                size_ = (std::size_t)PyObject_Length(py);
                be_ref_clear = true;
            }
        } else {
            if PyUnicode_Check(o) {
#if PY_MAJOR_VERSION >= 3
                kind = (int)PyUnicode_KIND(o);  // ucs1,2,4
                if(kind == 1) {
                    data_8 = PyUnicode_1BYTE_DATA(o);
                    data_64 = (uint64_t*&)data_8;
                } else if(kind == 2) {
                    data_16 = PyUnicode_2BYTE_DATA(o);
                    data_64 = (uint64_t*&)data_16;
                } else if(kind == 4) {
                    data_32 = PyUnicode_4BYTE_DATA(o);
                    data_64 = (uint64_t*&)data_32;
                }
#else
                kind = 2;  // unicode
                data_16 = (uint16_t*)PyUnicode_AsUnicode(py);
                data_64 = (uint64_t*&)data_16;
#endif
                return;
            }
            if(PyBytes_Check(o)) {
                kind = 1;  // byte
                data_8 = (uint8_t*)PyBytes_AsString(py);
                data_64 = (uint64_t*&)data_8;
                return;
            }
            if(PyByteArray_Check(o)) {
                kind = 1;  // byte
                data_8 = (uint8_t*)PyByteArray_AsString(py);
                data_64 = (uint64_t*&)data_8;
                return;
            }
        }
        kind = 8;
        if(size_ == 0)
            return;

        be_hash_clear = true;
        data_64 = new uint64_t[size_];
        for(std::size_t i = 0; i < size_; i++) {
            PyObject* item = PySequence_ITEM(py, (Py_ssize_t)i);
            if((data_64[i] = (uint64_t)PyObject_Hash(item)) == -1) {
                PyErr_Clear();
                Py_DECREF(item);
                item = PySequence_Tuple(item);
                data_64[i] = (uint64_t)PyTuple_Type.tp_hash(item);
            }
            Py_DECREF(item);
        }
    }


    ~pyview() {
        if(size_ != -1)
            close();
    }
    void close() {
        if(be_ref_clear) {
            Py_CLEAR(py);
            be_ref_clear = false;
            py = NULL;
        }
        if(be_hash_clear && size_ != error_n) {
            if(kind == 1) {
                if(*(data_8 + size_ - 1)) {
                    for(size_t i = 0; i < size_; i++)
                        *(data_8 + i) = NULL;
                    delete[] data_8;
                }
            } else if(kind == 2) {
                if(*(data_16 + size_ - 1)) {
                    for(size_t i = 0; i < size_; i++)
                        *(data_16 + i) = NULL;
                    delete[] data_16;
                }

            } else if(kind == 4) {
                if(*(data_32 + size_ - 1)) {
                    for(size_t i = 0; i < size_; i++)
                        *(data_32 + i) = NULL;
                    delete[] data_32;
                }

            } else {
                if(*(data_64 + size_ - 1)) {
                    for(size_t i = 0; i < size_; i++)
                        *(data_64 + i) = NULL;
                    delete[] data_64;
                }
            }

            be_hash_clear = false;
        }
        size_ = error_n;
    }

    constexpr PyObject* getitem(size_t index) const noexcept {
        return (size() == 0 || is_sequence == false) ? py
               : (size() > 0 && index < size())      ? PySequence_GetItem(py, (Py_ssize_t)index)
                                                     : NULL;
    }
    constexpr uint64_t const* data() const noexcept { return data_64; }
    constexpr uint64_t*& data() noexcept { return data_64; }

    template <typename T>
    constexpr uint64_t const operator[](T pos) const noexcept {
        return (kind == 1 ? data_8[pos] : kind == 2 ? data_16[pos] : kind == 8 ? data_64[pos] : data_32[pos]);
    }
    template <typename T>
    uint64_t operator[](T pos) noexcept {
        return (kind == 1 ? data_8[pos] : kind == 2 ? data_16[pos] : kind == 8 ? data_64[pos] : data_32[pos]);
    }

    pyview& operator=(const pyview& other) noexcept {
        memcpy(this, &other, sizeof(pyview));
        return *this;
    }

    inline constexpr std::size_t size() const noexcept { return size_; }
    inline constexpr std::size_t length() const noexcept { return size_; }

    pyview& operator++() {
        if(kind == 1)
            ++data_8;
        else if(kind == 2)
            ++data_16;
        else if(kind == 8)
            ++data_64;
        else
            ++data_32;
        return *this;
    }
    pyview& operator++(int) {
        if(kind == 1)
            data_8++;
        else if(kind == 2)
            data_16++;
        else if(kind == 8)
            data_64++;
        else
            data_32++;
        return *this;
    }

    pyview& operator--() {
        if(kind == 1)
            --data_8;
        else if(kind == 2)
            --data_16;
        else if(kind == 8)
            --data_64;
        else
            --data_32;
        return *this;
    }
    pyview& operator--(int) {
        if(kind == 1)
            data_8--;
        else if(kind == 2)
            data_16--;
        else if(kind == 8)
            data_64--;
        else
            data_32--;
        return *this;
    }

    bool operator==(PyObject*& rpy) { return (bool)PyObject_RichCompareBool(this->py, rpy, Py_EQ); }
    bool operator!=(PyObject*& rpy) { return (bool)PyObject_RichCompareBool(this->py, rpy, Py_NE); }
    constexpr bool operator==(const pyview& rhs) const noexcept {
        return this->data_64 == rhs.data_64;
    }
    constexpr bool operator!=(const pyview& rhs) const noexcept {
        return this->data_64 != rhs.data_64;
    }
    constexpr bool operator<(const pyview& rhs) const noexcept {
        return (kind == 1   ? this->data_8 < rhs.data_8
                : kind == 2 ? this->data_16 < rhs.data_16
                : kind == 8 ? this->data_64 < rhs.data_64
                            : this->data_32 < rhs.data_32);
    }
    constexpr bool operator<=(const pyview& rhs) const noexcept {
        return (kind == 1   ? this->data_8 <= rhs.data_8
                : kind == 2 ? this->data_16 <= rhs.data_16
                : kind == 8 ? this->data_64 <= rhs.data_64
                            : this->data_32 <= rhs.data_32);
    }
    constexpr bool operator>(const pyview& rhs) const noexcept {
        return (kind == 1   ? this->data_8 > rhs.data_8
                : kind == 2 ? this->data_16 > rhs.data_16
                : kind == 8 ? this->data_64 > rhs.data_64
                            : this->data_32 > rhs.data_32);
    }
    constexpr bool operator>=(const pyview& rhs) const noexcept {
        return (kind == 1   ? this->data_8 >= rhs.data_8
                : kind == 2 ? this->data_16 >= rhs.data_16
                : kind == 8 ? this->data_64 >= rhs.data_64
                            : this->data_32 >= rhs.data_32);
    }

    constexpr uint64_t* begin() noexcept {
        return data_64;
    }
    constexpr uint64_t* end() noexcept {
        return (kind == 1   ? (uint64_t*)(data_8 + size_)
                : kind == 2 ? (uint64_t*)(data_16 + size_)
                : kind == 8 ? (data_64 + size_)
                            : (uint64_t*)(data_32 + size_));
    }
    constexpr uint64_t const* cbegin() noexcept { return begin(); }
    constexpr uint64_t const* cend() noexcept { return end(); }

    std::reverse_iterator<uint64_t const*> rbegin() noexcept { return std::reverse_iterator<uint64_t const*>(end()); }
    std::reverse_iterator<uint64_t const*> rend() noexcept { return std::reverse_iterator<uint64_t const*>(begin()); }
    std::reverse_iterator<uint64_t const*> crbegin() noexcept { return rbegin(); }
    std::reverse_iterator<uint64_t const*> crend() noexcept { return rend(); }
};

#endif /* !defined(PYYOU_H) */

```

# 使い方

```c++
#include <iostream>

int main() {
    Py_Initialize();
    PyObject* a = PyUnicode_FromString("cafe");
    PyObject* b = PyUnicode_FromString("coffee");
    pyview seq1(a);
    pyview seq2(b);
    if (seq1 == seq2)
        std::cout << "onaji" << std::endl;
    if (seq1[0] == seq2[0])
        std::cout << "saisho onaji" << std::endl;
    std::cout << "a = " << seq1.size() << std::endl;
    std::cout << "b = " << seq2.size() << std::endl;
}
```