    // Copyright (C) 2013  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_SERIALIZE_PiCKLE_Hh_
    #define DLIB_SERIALIZE_PiCKLE_Hh_

    #include <dlib/serialize.h>
    #include <pybind11/pybind11.h>
    #include <sstream>
    #include <dlib/vectorstream.h>

    namespace py = pybind11;

    namespace dlib
    {

        template<typename T>
        py::tuple getstate(const T& item)
        {
            using namespace dlib;
            std::vector<char> buf;
            buf.reserve(5000);
            vectorstream sout(buf);
            serialize(item, sout);
            return py::make_tuple(py::handle(
                    PyBytes_FromStringAndSize(buf.size()?&buf[0]:0, buf.size())));
        }

        template<typename T>
        T setstate(py::tuple state)
        {
            using namespace dlib;
            if (len(state) != 1)
            {
                PyErr_SetObject(PyExc_ValueError,
                    py::str("expected 1-item tuple in call to __setstate__; got {}").format(state).ptr()
                );
                throw py::error_already_set();
            }

            // We used to serialize by converting to a str but the boost.python routines for
            // doing this don't work in Python 3.  You end up getting an error about invalid
            // UTF-8 encodings.  So instead we access the python C interface directly and use
            // bytes objects.  However, we keep the deserialization code that worked with str
            // for backwards compatibility with previously pickled files.
            T item;
            py::object obj = state[0];
            if (py::isinstance<py::str>(obj))
            {
                py::str data = state[0].cast<py::str>();
                std::string temp = data;
                std::istringstream sin(temp);
                deserialize(item, sin);
            }
            else if(PyBytes_Check(py::object(state[0]).ptr()))
            {
                py::object obj = state[0];
                char* data = PyBytes_AsString(obj.ptr());
                unsigned long num = PyBytes_Size(obj.ptr());
                std::istringstream sin(std::string(data, num));
                deserialize(item, sin);
            }
            else
            {
                throw error("Unable to unpickle, error in input file.");
            }

            return item;
        }

    }

    #endif // DLIB_SERIALIZE_PiCKLE_Hh_