You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The set-up is simple.
For a game, I have a class Universe instantiating directly a class RuleSet which itself instantiate, through a shared_ptr, a VictoryRule. All three classes are exposed to a python module with pybind11. All three of them use std::shared_ptr as the holder type.
Now VictoryRule is a class meant to be subclassed in python, with it own trampoline class PyVictoryRule. A
universe built by default a RuleSet and has a read-only property rule_set to access it. RuleSet can get and set its VictoryRule with a getter get_victory_rule and a setter set_victory_rule. The setter keeps alive the VictoryRule with a decorator pybind11::keep_alive<1, 2>().
When ignoring the universe, everything is fine. Here is some working code:
rs = RuleSet()
rs.register_victory_rule(
print("Winners with RuleSet:", rs.get_victory_rule().winners())
Simple enough, it pretty much matches one-to-one with the example in the documentation. But, if we add a level of
indirection by using Universe, this crumbles and fails:
u = Universe()
u.rule_set.register_victory_rule(CustomVictoryRule())
print("Winners with Universe:", u.rule_set.get_victory_rule().winners())
Here, the result of u.rule_set is purged once the second line is completed, and so is the CustomVictoryRule() and then the third
line cannot call the method of CustomVictoryRule and throws a RuntimeError (Tried to call pure virtual function
"VictoryRule::winners"). What I want here, is that the result of Universe.rule_set is kept alive in python as long as Universe
is kept alive, and that we return always that same result. I naively tried to add keep_alive<1, 0> to Universe.rule_set, to do that but it didn't work...
More generally, what I'd like to to is a keep_alive<Universe, VictoryRule> in RuleSet.set_victory_rule but I cannot
index the containing Universe from RuleSet. How can I achieve to keep CustomVictoryRule() alive, itself set with a shared pointer inside a RuleSet inside a Universe through direct instantiation (I tried to use a shared_ptr in Universe at some point, but it didn't help fixing my issue) ?
Maybe I can create a forced trampoline for Universe and do shenanigans in it to keep a python variable in it and always return it, but I have no idea how to create such a variable or even the type it should have.
Below is the source code "Minimal Failing Product", or "MFP", which when compiled and run, ends with the error.
Build with gcc 12.1.1 and pybind11v2.9.2 for python 3.10.5 on Fedora Linux.
#include <memory>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
using team_t = int;
class VictoryRule {
public:
//! @brief Check if game is won by a team.
//! @return the teamno of the team that won or team_t::no_team if nobody won.
virtual std::vector<team_t> winners() = 0;
//! @brief Default virtual destructor.
virtual ~VictoryRule() = default;
};
class PyVictoryRule : public VictoryRule {
public:
std::vector<team_t> winners() override {
PYBIND11_OVERRIDE_PURE(std::vector<team_t>, VictoryRule, winners, );
}
};
class RuleSet {
public:
std::shared_ptr<VictoryRule> get_victory_rule() {
return victory_rule_;
}
void register_victory_rule(std::shared_ptr<VictoryRule> victory_rule) {
victory_rule_ = std::move(victory_rule);
}
private:
std::shared_ptr<VictoryRule> victory_rule_;
};
class Universe {
public:
Universe() = default;
RuleSet& rule_set() { return rule_set_; }
private:
RuleSet rule_set_;
};
PYBIND11_MODULE(pymfppp, m) {
pybind11::class_<VictoryRule, PyVictoryRule, std::shared_ptr<VictoryRule>>(
m, "VictoryRule")
.def(pybind11::init())
.def("winners", &VictoryRule::winners);
pybind11::class_<RuleSet, std::shared_ptr<RuleSet>>(m, "RuleSet")
.def(pybind11::init())
.def("get_victory_rule", &RuleSet::get_victory_rule, pybind11::return_value_policy::copy)
.def("register_victory_rule", &RuleSet::register_victory_rule,
pybind11::arg("victory_rule"), pybind11::keep_alive<1, 2>());
pybind11::class_<Universe, std::shared_ptr<Universe>>(m, "Universe")
.def(pybind11::init<>())
.def_property_readonly(
"rule_set",
&Universe::rule_set,
pybind11::return_value_policy::reference_internal);
}
pymfp.py
from pymfppp import VictoryRule, RuleSet, Universe
class CustomVictoryRule(VictoryRule):
def __init__(self):
VictoryRule.__init__(self)
def winners(self):
return [1]
def main():
# OK
rs = RuleSet()
rs.register_victory_rule(CustomVictoryRule())
print("Winners with RuleSet:", rs.get_victory_rule().winners())
# NOT OK
u = Universe()
u.rule_set.register_victory_rule(CustomVictoryRule())
print("Winners with Universe:", u.rule_set.get_victory_rule().winners())
if __name__ == '__main__':
main()
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
The set-up is simple.
For a game, I have a class
Universe
instantiating directly a classRuleSet
which itself instantiate, through ashared_ptr
, aVictoryRule
. All three classes are exposed to a python module with pybind11. All three of them usestd::shared_ptr
as the holder type.Now
VictoryRule
is a class meant to be subclassed in python, with it own trampoline classPyVictoryRule
. Auniverse built by default a RuleSet and has a read-only property
rule_set
to access it.RuleSet
can get and set itsVictoryRule
with a getterget_victory_rule
and a setterset_victory_rule
. The setter keeps alive theVictoryRule
with a decoratorpybind11::keep_alive<1, 2>()
.When ignoring the universe, everything is fine. Here is some working code:
Simple enough, it pretty much matches one-to-one with the example in the documentation. But, if we add a level of
indirection by using Universe, this crumbles and fails:
Here, the result of
u.rule_set
is purged once the second line is completed, and so is theCustomVictoryRule()
and then the thirdline cannot call the method of CustomVictoryRule and throws a
RuntimeError
(Tried to call pure virtual function"VictoryRule::winners"). What I want here, is that the result of
Universe.rule_set
is kept alive in python as long asUniverse
is kept alive, and that we return always that same result. I naively tried to add
keep_alive<1, 0>
toUniverse.rule_set
, to do that but it didn't work...More generally, what I'd like to to is a
keep_alive<Universe, VictoryRule>
inRuleSet.set_victory_rule
but I cannotindex the containing Universe from
RuleSet
. How can I achieve to keep CustomVictoryRule() alive, itself set with a shared pointer inside a RuleSet inside a Universe through direct instantiation (I tried to use a shared_ptr in Universe at some point, but it didn't help fixing my issue) ?Maybe I can create a forced trampoline for Universe and do shenanigans in it to keep a python variable in it and always return it, but I have no idea how to create such a variable or even the type it should have.
Below is the source code "Minimal Failing Product", or "MFP", which when compiled and run, ends with the error.
Build with gcc 12.1.1 and pybind11v2.9.2 for python 3.10.5 on Fedora Linux.
CMakeLists.txt
pymfppp.cpp
pymfp.py
Beta Was this translation helpful? Give feedback.
All reactions