Search code examples
pythonc++pybind11std-variant

Expressing a Python class as a type in std::variant in C++ using pybind11


The working example below returns, a vector of type variant consisting of float and int64_t, to Python. The intent (illustrated by the commented lines of code **) is to add further functionality to this by enabling a Decimal (python class), constructed in C++, to be passed back to Python in the same structure.

#include <pybind11/pybind11.h>
#include <vector>
#include <variant>
#include <string>
#include <pybind11/stl.h>
#include <pybind11/complex.h>
#include <pybind11/functional.h>
#include <pybind11/chrono.h>
namespace py = pybind11;

// Importing Decimal class, as shown here
// https://pybind11.readthedocs.io/en/stable/advanced/pycpp/object.html?highlight=Decimal(#accessing-python-libraries-from-c
py::object Decimal = py::module_::import("decimal").attr("Decimal");

//typedef std::variant<float, int64_t, Decimal> Listtypes;  // **
typedef std::variant<float, int64_t> Listtypes;

std::vector<ListTypes> returnList() {

    std::vector<ListTypes> list(3);
    
    int64_t integerVal = 987654321;
    float floatVal = 1.01;

    // Constructing python object, as shown here 
   //https://pybind11.readthedocs.io/en/stable/advanced/pycpp/object.html#callingpythonfunctions
    py::object pi = Decimal("3.14159");
    
    list[0] = integerVal;
    list[1] = floatVal;
    //list[2] = pi;        // **

    return list;
}

PYBIND11_MODULE(pBind, m) {
    m.def("returnList", &returnList, "Function to return list of differing types");
}

So to go about this, can the Decimal be expressed as part of the std::variant so that it can be passed back to Python with the vector, or is the solution not so simple?


Solution

  • you cannot just add the pi variable as is to your std::variant vector because the type of it is py::object. You could add it to your ListTypes so just changing the line

    typedef std::variant<float, int64_t, py::object> Listtypes;

    but then your list on the Python side would be [987654321, 1.0099999904632568, Decimal(3.14159)]

    I think you'd rather use the casting offered by pybind to convert your pi variable to float so that your code becomes

    #include <pybind11/pybind11.h>
    #include <vector>
    #include <variant>
    #include <string>
    #include <pybind11/stl.h>
    #include <pybind11/complex.h>
    #include <pybind11/functional.h>
    #include <pybind11/chrono.h>
    namespace py = pybind11;
    
    // Importing Decimal class, as shown here
    // https://pybind11.readthedocs.io/en/stable/advanced/pycpp/object.html?highlight=Decimal(#accessing-python-libraries-from-c
    py::object Decimal = py::module_::import("decimal").attr("Decimal");
    
    //typedef std::variant<float, int64_t, Decimal> Listtypes;  // **
    typedef std::variant<float, int64_t> Listtypes;
    
    std::vector<ListTypes> returnList() {
    
        std::vector<ListTypes> list(3);
        
        int64_t integerVal = 987654321;
        float floatVal = 1.01;
    
        // Constructing python object, as shown here 
       //https://pybind11.readthedocs.io/en/stable/advanced/pycpp/object.html#callingpythonfunctions
        py::object pi = Decimal("3.14159");
        
        list[0] = integerVal;
        list[1] = floatVal;
        list[2] = pi.cast<float>();        // we cast to float
    
        return list;
    }
    
    PYBIND11_MODULE(pBind, m) {
        m.def("returnList", &returnList, "Function to return list of differing types");
    }