0

I have some class functions that takes in parameters by pointer. However, when I expose and override these functions in boost::python, any changes I make to the parameters are not reflected back to the original, meaning it isn't being passed by pointer as I expected. For an example of what I mean, here's some of my code with unimportant functions and details omitted.

class StatusEffect
{
public:
      virtual void TickCharacter(Character::BaseCharacter* character, Battles::BattleField *field, int ticks = 1);
}
//In my Scripting Code header file

struct StatusEffectWrapper : Game::StatusEffects::StatusEffect
{
    virtual void TickCharacter(Game::Character::BaseCharacter* character, Game::Battles::BattleField *field, int ticks = 1);
    virtual void TickCharacterDefault(Game::Character::BaseCharacter* character, Game::Battles::BattleField *field, int ticks = 1);
}
//In the actual scripting code
    void StatusEffectWrapper::TickCharacter(Game::Character::BaseCharacter* character, Game::Battles::BattleField *field, int ticks)
    {
        call_method<void>(self, "TickCharacter", character, field, ticks);
    }
    void StatusEffectWrapper::TickCharacterDefault(Game::Character::BaseCharacter* character, Game::Battles::BattleField *field, int ticks)
    {
        this->StatusEffect::TickCharacter(character, field, ticks);
    }

    class_<StatusEffects::StatusEffect, boost::shared_ptr<StatusEffectWrapper> >("StatusEffect")
    .def("TickCharacter", &StatusEffect::TickCharacter, &StatusEffectWrapper::TickCharacterDefault)

and finally in my Python file, I try and do something like this

class StatCreepEffect(StatusEffect):
    #rate = 0.075
    #stat = Stat.HP
    def __init__(self, name, stat_, rate_, descript, uid, turns, prior, canrefresh, poseffect, cats, conds, flgs, sep, pers):
        StatusEffect.__init__(self, name, descript, uid, turns, prior, canrefresh, poseffect, cats, conds, flgs, sep, pers)
        self.rate = rate_
        self.stat = stat_
    def TickCharacter(self, Character, Field, Ticks):
        if (self.stat == Stat.HP):
            Character.SetHP(int(round(Character.GetHP() + Character.GetInBattleStat(self.stat) * self.rate)))
            print("HP Regenerated on character " + Character.GetName() + " New HP: " + str(Character.GetHP()))
        elif (self.stat == Stat.MP):
            Character.SetMP(int(round(Character.GetMP() + Character.GetInBattleStat(self.stat) * self.rate)))
        elif (self.stat == Stat.SP):
            Character.SetSP(int(round(Character.SetSP() + Character.GetInBattleStat(self.stat) * self.rate)))

Regen = StatCreepEffect("Regen", Stat.HP, 0.05, "Regenerates a small amount of HP every turn", PrimaryEngine.GetUID(), 14, 0, True, True, StatusCategories.SC_Regen, 0, StatusFlags.TickEffect, True, StatusPersistence.SP_Timer_Cure)

SELibrary.AddPythonEffect(Regen);

I call the effect like so in my C++ code

StatusEffect* regen = game.GetSELibrary().GetStatusEffect(std::string("Regen"));
while(character.GetHP() < 600)
    {
regen->TickCharacter(&character, &field, 1);
        std::cout << "Character HP: " << character.GetHP() << std::endl;
    }

I created this code following the example here http://wiki.python.org/moin/boost.python/OverridableVirtualFunctions

The script compiles and the effect is added correctly. The print line in the python script prints the correct value. The character object starts with 400 HP and the print line outputs 430. However, the std::cout line in my C++ code always outputs 400, meaning the extra 30 HP added by the script is not reflected back in the original object. For clarity, here's the console output from the above C++ code fragment:

HP Regenerated on character Arian New HP: 430
Character HP: 400

I initially had everything as pass-by-reference instead of pointers, but I hoped switching to pointers would solve this problem. It didn't.

Can someone help me figure out how to make this work correctly? It'd be greatly appreciated.

Thanks

EDIT:

If it matters, the AddPythonEffect is defined as follows:

void StatusEffectsLibrary::AddPythonEffect(boost::shared_ptr<StatusEffect> effect)
{
    if (effect.get() != nullptr)
    {
        NameToSEMap[effect->GetName()] = effect;
        IDToSEMap[effect->GetUID()] = effect;
    }
}

2 Answers 2

2

The behavior you're seeing is because Boost Python copies the C++ instance and would then be disconnected from the instance you hold in your C++ lib. This is customizable by changing the "HeldType" in the class_<T, Bases, HeldType, NonCopyable> instantiation. See the Boost documentation for more information. Note that the default type for HeldType is T, so your type would be held by value (copied).

It's easiest, specifically from a life-time management perspective, if HeldType is some sort of smart pointer (either boost::shared_ptr or boost::intrusive_ptr). I believe you can use a raw pointer, but life-time management becomes exceedingly tricky (when does Python know your C++ object has been destroyed, and likewise, when does your C++ lib otherwise know when Python no longer holds a reference to it?).

Sign up to request clarification or add additional context in comments.

1 Comment

I hate to bother you further, but I'm still unclear as to what exactly I need to do. StatusEffectWrapped as acting as the held type right? How do I need to modify it to make this work? I wrapped it into a shared_ptr but with no effect. Do I need to expose the shared_ptr? Sorry, I'm just a bit confused
0

I think the problem is actually in declaring the class and pointer type differently in

class_<StatusEffects::StatusEffect, boost::shared_ptr<StatusEffectWrapper> >("StatusEffect")

I would suggest class_<StatucEffectsWrapper,boost::shared_ptr<StatusEffectWrapper> >("StatusEffect"), at least that's what I did in my code which works (the objects are not passed via shared_ptr in this case, but I think it will not make a difference here).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.