morynicz
morynicz

Reputation: 2332

How to pass list of shared pointers to c++ object in pybind11

I'm building a ml project which will use tensorflow, game rule set controlled by c++ library and python players.

I decided to use pybind as translation layer between game rules engine and control layer with players.

Here are classes that I tried to wrap with pybind:

class Player
{
public:
  Player() = default;
  virtual PlayerId getId() const = 0;
  virtual void playTurn(Turn&) = 0;
  virtual void takeHint(std::list<CardId>, Color) = 0;
  virtual void takeHint(std::list<CardId>, Value) = 0;
  virtual ~Player() = default;
};

using Players = std::list<std::shared_ptr<Player>>;

class Game
{
(...)
public:
  Game(const Game&) = delete;
  Game(Game&&) = delete;
  Game(const Players& players, const Cards&);
  void turn();
  int getScore() const;
};

And here is how I wrapped:

PYBIND11_MODULE(hanabi_py, m)
{
  m.doc() = "hanabi-cpp wrapper for python";
  py::class_<Game>(m, "Game")
    .def(py::init<const Players&, const Cards&>())
    .def("turn", &Game::turn)
    .def("getScore", &Game::getScore);

  py::class_<Player, std::shared_ptr<Player>, PyPlayer>(m, "Player")
    .def(py::init<>())
    .def("getId", &Player::getId, "Get id")
    .def("playTurn", &Player::playTurn, "Play turn", py::arg("turn"))
    .def("takeHint",
         py::overload_cast<std::list<CardId>, Color>(&Player::takeHint),
         "take color hint")
    .def("takeHint",
         py::overload_cast<std::list<CardId>, Value>(&Player::takeHint),
         "take value hint");

(...)
}

Full code available at: https://github.com/morynicz/hanabi-cpp

When trying to create game with:

import hanabi_py as h

class PyPlayer(h.Player):
    def __init__(self, id):
        h.Player.__init__(self)
        self.id = id

    def getId(self):
        return self.id

    def playTurn(self,turn):
        pass

    def takeHint(self, ids, arg):
        pass

def attempt():
        p = PyPlayer(1)
        c = h.Card
        c.id = 0
        c.color = h.Color.Red
        c.value = h.Value.One
        g = h.Game([p],[c])

I get:

TypeError: init(): incompatible constructor arguments. The following argument types are supported: 1. hanabi_py.Game(arg0: List[Player], arg1: List[Card])

The Problem How to wrap python object of python class derived from C++ class into a shared_ptr before putting it into a list?

I know that I could try add some function to wrap the python object with shared_ptr, but keeping in mind that there are already shared_ptr holders in use this seems like a pretty bad idea.

EDIT: It appears that the issue does not lie in the shared_ptr, but in the list. For some reason it is not properly resolved. I tried making an opaque class for players, but it does not work

TypeError: init(): incompatible constructor arguments. The following argument types are supported: 1. hanabi_py.Game(players: std::__cxx11::list, std::allocator > >, cards: List[Card])

So currently the problem is: How to pass list of python objects as std::list> to C++ object from python?

Upvotes: 0

Views: 1465

Answers (1)

Wim Lavrijsen
Wim Lavrijsen

Reputation: 3778

This line:

c = h.Card

makes c the Card type. You need to add a constructor .def(py::init<>()) to the construction of py::class_<Card>(m, "Card"), then do c = h.Card().

Upvotes: 2

Related Questions