[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/misusage/Programs-Scripts/master/pywrapper/pywrapper.h [Back]  [Original]

/*      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, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 *      
 *      Author: 
 *      Matias Fontanini
 * 
 */
 
#ifndef PYWRAPPER_H
#define PYWRAPPER_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


namespace Python {
    // Deleter that calls Py_XDECREF on the PyObject parameter.
    struct PyObjectDeleter {
        void operator()(PyObject *obj) {
            Py_XDECREF(obj);
        } 
    };
    // unique_ptr that uses Py_XDECREF as the destructor function.
    typedef std::unique_ptr pyunique_ptr;
    
    // ------------ Conversion functions ------------
    
    // Convert a PyObject to a std::string.
    bool convert(PyObject *obj, std::string &val);
    // Convert a PyObject to a std::vector.
    bool convert(PyObject *obj, std::vector &val);
    // Convert a PyObject to a bool value.
    bool convert(PyObject *obj, bool &value);
    // Convert a PyObject to any integral type.
    template
    bool convert(PyObject *obj, T &val) {
        if(!PyInt_Check(obj))
            return false;
        val = PyInt_AsLong(obj);
        return true;
    }
    // Convert a PyObject to an float.
    bool convert(PyObject *obj, double &val);
    
    template
    typename std::enable_if::type 
    add_to_tuple(PyObject *obj, std::tuple &tup) {
        return convert(PyTuple_GetItem(obj, n), std::get(tup));
    }
    
    template
    typename std::enable_if::type 
    add_to_tuple(PyObject *obj, std::tuple &tup) {
        add_to_tuple(obj, tup);
        return convert(PyTuple_GetItem(obj, n), std::get(tup));
    }
    
    template
    bool convert(PyObject *obj, std::tuple &tup) {
        if(!PyTuple_Check(obj) || 
            PyTuple_Size(obj) != sizeof...(Args))
            return false;
        return add_to_tuple(obj, tup);
    }
    // Convert a PyObject to a std::map
    template
    bool convert(PyObject *obj, std::map &mp) {
        if(!PyDict_Check(obj))
            return false;
        PyObject *py_key, *py_val;
        Py_ssize_t pos(0);
        while (PyDict_Next(obj, &pos, &py_key, &py_val)) {
            K key;
            if(!convert(py_key, key))
                return false;
            V val;
            if(!convert(py_val, val))
                return false;
            mp.insert(std::make_pair(key, val));
        }
        return true;
    }
    // Convert a PyObject to a generic container.
    template
    bool convert_list(PyObject *obj, C &container) {
        if(!PyList_Check(obj))
            return false;
        for(Py_ssize_t i(0); i < PyList_Size(obj); ++i) {
            T val;
            if(!convert(PyList_GetItem(obj, i), val))
                return false;
            container.push_back(std::move(val));
        }
        return true;
    }
    // Convert a PyObject to a std::list.
    template bool convert(PyObject *obj, std::list &lst) {
        return convert_list(obj, lst);
    }
    // Convert a PyObject to a std::vector.
    template bool convert(PyObject *obj, std::vector &vec) {
       return convert_list(obj, vec);
    }
    
    template bool generic_convert(PyObject *obj, 
      const std::function &is_obj,
      const std::function &converter,
      T &val) {
        if(!is_obj(obj))
            return false;
        val = converter(obj);
        return true;
    }
    
    // -------------- PyObject allocators ----------------
    
    // Generic python list allocation
    template static PyObject *alloc_list(const T &container) {
        PyObject *lst(PyList_New(container.size()));
            
        Py_ssize_t i(0);
        for(auto it(container.begin()); it != container.end(); ++it)
            PyList_SetItem(lst, i++, alloc_pyobject(*it));
        
        return lst;
    }
    // Creates a PyObject from a std::string
    PyObject *alloc_pyobject(const std::string &str);
    // Creates a PyObject from a std::vector
    PyObject *alloc_pyobject(const std::vector &val, size_t sz);
    // Creates a PyObject from a std::vector
    PyObject *alloc_pyobject(const std::vector &val);
    // Creates a PyObject from a const char*
    PyObject *alloc_pyobject(const char *cstr);
    // Creates a PyObject from any integral type(gets converted to PyInt)
    template
    PyObject *alloc_pyobject(T num) {
        return PyInt_FromLong(num);
    }
    // Creates a PyObject from a bool
    PyObject *alloc_pyobject(bool value);
    // Creates a PyObject from a double
    PyObject *alloc_pyobject(double num);
    // Creates a PyObject from a std::vector
    template PyObject *alloc_pyobject(const std::vector &container) {
        return alloc_list(container);
    }
    // Creates a PyObject from a std::list
    template PyObject *alloc_pyobject(const std::list &container) {
        return alloc_list(container);
    }
    // Creates a PyObject from a std::map
    template PyObject *alloc_pyobject(
      const std::map &container) {
        PyObject *dict(PyDict_New());
            
        for(auto it(container.begin()); it != container.end(); ++it)
            PyDict_SetItem(dict, 
                alloc_pyobject(it->first),
                alloc_pyobject(it->second)
            );
        
        return dict;
    }
    
    void initialize();
    void finalize();
    void print_error();
    void clear_error();
    void print_object(PyObject *obj);
    
    /**
     * \class Object
     * \brief This class represents a python object.
     */
    class Object {
    public:
        /**
         * \brief Constructs a default python object
         */
        Object();
        
        /**
         * \brief Constructs a python object from a PyObject pointer.
         * 
         * This Object takes ownership of the PyObject* argument. That 
         * means no Py_INCREF is performed on it. 
         * \param obj The pointer from which to construct this Object.
         */
        Object(PyObject *obj);
        
        /**
         * \brief Calls the callable attribute "name" using the provided
         * arguments.
         * 
         * This function might throw a std::runtime_error if there is
         * an error when calling the function.
         * 
         * \param name The name of the attribute to be called.
         * \param args The arguments which will be used when calling the
         * attribute.
         * \return Python::Object containing the result of the function.
         */
        template
        Object call_function(const std::string &name, const Args&... args) {
            pyunique_ptr func(load_function(name));
            // Create the tuple argument
            pyunique_ptr tup(PyTuple_New(sizeof...(args)));
            add_tuple_vars(tup, args...);
            // Call our object
            PyObject *ret(PyObject_CallObject(func.get(), tup.get()));
            if(!ret)
                throw std::runtime_error("Failed to call function " + name);
            return {ret};
        }
        
        /**
         * \brief Calls a callable attribute using no arguments.
         * 
         * This function might throw a std::runtime_error if there is
         * an error when calling the function.
         * 
         * \sa Python::Object::call_function.
         * \param name The name of the callable attribute to be executed.
         * \return Python::Object containing the result of the function.
         */
        Object call_function(const std::string &name);
        
        /**
         * \brief Finds and returns the attribute named "name".
         * 
         * This function might throw a std::runtime_error if an error
         * is encountered while fetching the attribute.
         * 
         * \param name The name of the attribute to be returned.
         * \return Python::Object representing the attribute.
         */
        Object get_attr(const std::string &name);
        
        /**
         * \brief Checks whether this object contains a certain attribute.
         * 
         * \param name The name of the attribute to be searched.
         * \return bool indicating whether the attribute is defined.
         */
        bool has_attr(const std::string &name);
          
        /**
         * \brief Returns the internal PyObject*.
         * 
         * No reference increment is performed on the PyObject* before
         * returning it, so any DECREF applied to it without INCREF'ing
         * it will cause undefined behaviour.
         * \return The PyObject* which this Object is representing.
         */
        PyObject *get() const { return py_obj.get(); }
        
        template
        bool convert(T ¶m) {
            return Python::convert(py_obj.get(), param);
        }
        
        /**
         * \brief Constructs a Python::Object from a script.
         * 
         * The returned Object will be the representation of the loaded
         * script. If any errors are encountered while loading this 
         * script, a std::runtime_error is thrown.
         * 
         * \param script_path The path of the script to be loaded.
         * \return Object representing the loaded script.
         */
        static Object from_script(const std::string &script_path);
    private:
        typedef std::shared_ptr pyshared_ptr;
    
        PyObject *load_function(const std::string &name);
        
        pyshared_ptr make_pyshared(PyObject *obj);
    
        // Variadic template method to add items to a tuple
        template 
        void add_tuple_vars(pyunique_ptr &tup, const First &head, const Rest&... tail) {
            add_tuple_var(
                tup, 
                PyTuple_Size(tup.get()) - sizeof...(tail) - 1, 
                head
            );
            add_tuple_vars(tup, tail...);
        }
        
        
        void add_tuple_vars(pyunique_ptr &tup, PyObject *arg) {
            add_tuple_var(tup, PyTuple_Size(tup.get()) - 1, arg);
        }
        
        // Base case for add_tuple_vars
        template 
        void add_tuple_vars(pyunique_ptr &tup, const Arg &arg) {
            add_tuple_var(tup, 
              PyTuple_Size(tup.get()) - 1, alloc_pyobject(arg)
            );
        }
        
        // Adds a PyObject* to the tuple object
        void add_tuple_var(pyunique_ptr &tup, Py_ssize_t i, PyObject *pobj) {
            PyTuple_SetItem(tup.get(), i, pobj);
        }
        
        // Adds a PyObject* to the tuple object
        template void add_tuple_var(pyunique_ptr &tup, Py_ssize_t i, 
          const T &data) {
            PyTuple_SetItem(tup.get(), i, alloc_pyobject(data));
        }
        
        pyshared_ptr py_obj;
    };
};

#endif // PYWRAPPER_H

Web Proxy Viewer  |  New URL  |  Original Page