SWIG-генерируемый Python с полиморфными смарт-указателями C ++ - PullRequest
0 голосов
/ 20 марта 2019

У меня есть иерархия классов C ++, которую я представляю Python через SWIG. Представляемые классы обернуты std::shared_ptr.

У меня проблема в том, что Python не предоставляет методы для производных объектов, возвращаемых как базовые std::shared_ptr. Это удивительно, потому что среда выполнения может вывести, что объект имеет тип, производный std::shared_ptr.

Я могу видеть производные методы, когда производная std::shared_ptr возвращается явно.

Вопрос здесь показывает, что то, что я пытаюсь сделать, может быть достигнуто с помощью необработанных указателей с объявлением карты типов.

MyProg.h:

#pragma once

#include <memory>
#include <vector>
#include <exception>

namespace MyProg
{
    enum SubTypes
    {
        BaseType,
        DerivedAType,
        DerivedBType
    };

    class Base
    {
    public:
        virtual SubTypes SubType()
        {
            return BaseType;
        }

        virtual ~Base() {}

    protected:
        Base() {}
    };

    class DerivedA : public Base
    {
    public:
        friend class MyProg;

        int CallDerivedA()
        {
            return 1;
        }

        SubTypes SubType()
        {
            return DerivedAType;
        }

    private:
        DerivedA() : Base() {}
    };

    class DerivedB : public Base
    {
    public:

        friend class MyProg;

        int CallDerivedB()
        {
            return 2;
        }

        SubTypes SubType()
        {
            return DerivedBType;
        }

    private:
        DerivedB() : Base()
        {}
    };

    typedef std::shared_ptr<Base> BasePtr;
    typedef std::shared_ptr<DerivedA> DerivedAPtr;
    typedef std::shared_ptr<DerivedB> DerivedBPtr;

    class MyProg
    {
    public:

        MyProg()
        {
            // Create instances
            m_derivedA = std::shared_ptr<Base>(new DerivedA());
            m_derivedB = std::shared_ptr<Base>(new DerivedB());
        }

        DerivedAPtr GetDerivedA()
        {
            return std::dynamic_pointer_cast<DerivedA>(m_derivedA);
        }

        DerivedBPtr GetDerivedB()
        {
            return std::dynamic_pointer_cast<DerivedB>(m_derivedB);
        }

        BasePtr GetDerived(const SubTypes subType)
        {
            if (subType == DerivedAType)
            {
                return m_derivedA;
            }
            else if (subType == DerivedBType)
            {
                return m_derivedB;
            }
            throw std::exception("Unsupported type requested");
        }

    private:
        BasePtr m_derivedB;
        BasePtr m_derivedA;
    };
}

Интерфейс SWIG:

%module MyProg_Python

%include "typemaps.i"
%include <cpointer.i>
%include <std_shared_ptr.i>

%shared_ptr(MyProg::Base);
%shared_ptr(MyProg::DerivedA);
%shared_ptr(MyProg::DerivedB);

// I created this typemap based on the code generated for the GetDerivedA and GetDerivedB functions
%typemap(out) MyProg::Base {

    if (($1)->SubType() == MyProg::DerivedAType)
    {
        std::shared_ptr< MyProg::DerivedA > myptr = std::dynamic_pointer_cast< MyProg::DerivedA >(result);
        std::shared_ptr< MyProg::DerivedA > *smartresult = result ? &myptr : 0;
        $result = SWIG_NewPointerObj(SWIG_as_voidptr(smartresult), SWIGTYPE_p_std__shared_ptrT_MyProg__DerivedA_t, SWIG_POINTER_OWN);
    }
    else if (($1)->SubType() == MyProg::DerivedBType)
    {
        std::shared_ptr< MyProg::DerivedB > myptr = std::dynamic_pointer_cast< MyProg::DerivedB >(result);
        std::shared_ptr< MyProg::DerivedB > *smartresult = result ? &myptr : 0;
        $result = SWIG_NewPointerObj(SWIG_as_voidptr(smartresult), SWIGTYPE_p_std__shared_ptrT_MyProg__DerivedB_t, SWIG_POINTER_OWN);
    }
    else
    {
      // error
    }
}

%{
#include "MyProg.h"

using namespace std;
using namespace MyProg;
%}

%include "..\MyProg\MyProg.h"

namespace MyProg
{
    typedef std::shared_ptr<Base> BasePtr;
    typedef std::shared_ptr<DerivedA> DerivedAPtr;
    typedef std::shared_ptr<DerivedB> DerivedBPtr;
}

Клиент Python:

from MyProg_Python import *
prog = MyProg()
a = prog.GetDerivedA()
result = a.CallDerivedA() # returns 1
b = prog.GetDerivedB()
result = b.CallDerivedB() # returns 2
afrombase = prog.GetDerived(DerivedAType)

# The following line doesn't resolve, even though runtime indicates afrombase  is of type MyProg_Python.DerivedA (proxy of <Swig Object of type 'std::shared_ptr<MyProg::DerivedA> * ' at 0x...>)
afrombase.CallDerivedA()

Это ограничение для shared_ptr - нужно ли использовать необработанные указатели? Или есть объявление карты типов, которое позволяет мне раскрыть полиморфное поведение в Python?

...