Boost Python return reference to cast input parameter not working with virtual function











up vote
0
down vote

favorite












I wrap the template classes A and B through boost python, and try to cast between them. I implement the toB function and wrap it with return_internal_reference<> to achieve this. However, it doesn't return the B object in python, but returns the "dead" A object, of which member function can't be used anymore.



I narrow down the problem, which is the virtual function in the base class, Bar. The toB function correctly returns the B object referencing to A object if I remove the virtual keyword for hi(). But, what is the correct way to do this? Why does the virtual function in the base class affect the outcome? On the other hand, is there a way to cast boost python wrapping classes directly in Python?



#include <boost/python.hpp>
#include <iostream>
using namespace boost::python;

struct Bar
{
virtual void hi() { std::cout << "Hi Bar." << std::endl; }
virtual ~Bar() {}
};

template<typename T>
struct Foo : Bar
{
void set(T v) { val = v; }
T get() { return val; }
private:
T val;
};

using A = Foo<unsigned short>;
using B = Foo<short>;

B & toB(A & a) { return *reinterpret_cast<B*>(&a);}

BOOST_PYTHON_MODULE(example)
{
class_<A>("A")
.def("set", &A::set)
.def("get", &A::get)
;
class_<B>("B")
.def("set", &B::set)
.def("get", &B::get)
;
def("toB", &toB, return_internal_reference<>());
}



Python execution



>>> from example import *
>>> a = A()
>>> toB(a)
<example.A object at 0x7f7139cd27c0>
>>> toB(a).get()
Traceback (most recent call last):
File "<stdin>", line 1 in <module>
Boost.Python.ArgumentError: Python argument types in
A.get(A)
did not match C++ signature:
get(Foo<unsigned int> {lvalue})



It works well if I remove the virtual keywords in Bar.



struct Bar
{
void hi() { std::cout << "Hi Bar." << std::endl; }
};





>>> from example import *
>>> a = A()
>>> toB(a)
<example.B object at 0x7f511000c7c0>
>>> toB(a).get()
>>> 4.484155085839415e-44









share|improve this question
























  • Use a dynamic_cast, not a reinterpret_cast. You also forgot about the virtual destructors.
    – Matthieu Brucher
    Nov 6 at 14:56












  • B is not the base class of A. The user defined conversion from A to B is not defined. I am not sure if it's possible to defined a conversion so that the static_cast from a reference to A to a reference to B can happen.
    – Alex Pai
    Nov 6 at 15:43










  • Oh, actually, it's even worse... Basically, you are trying to read your unsigned as a float?? They may not even have the same size.
    – Matthieu Brucher
    Nov 6 at 15:45










  • Sorry, that's just an example. The real case is to casting between two very large template classes with same data size and different member functions. Updated the template type to short and unsigned short. But I think that's not related to the boost python issue.
    – Alex Pai
    Nov 6 at 15:52












  • Still a very bad idea, use a union instead.
    – Matthieu Brucher
    Nov 6 at 18:10















up vote
0
down vote

favorite












I wrap the template classes A and B through boost python, and try to cast between them. I implement the toB function and wrap it with return_internal_reference<> to achieve this. However, it doesn't return the B object in python, but returns the "dead" A object, of which member function can't be used anymore.



I narrow down the problem, which is the virtual function in the base class, Bar. The toB function correctly returns the B object referencing to A object if I remove the virtual keyword for hi(). But, what is the correct way to do this? Why does the virtual function in the base class affect the outcome? On the other hand, is there a way to cast boost python wrapping classes directly in Python?



#include <boost/python.hpp>
#include <iostream>
using namespace boost::python;

struct Bar
{
virtual void hi() { std::cout << "Hi Bar." << std::endl; }
virtual ~Bar() {}
};

template<typename T>
struct Foo : Bar
{
void set(T v) { val = v; }
T get() { return val; }
private:
T val;
};

using A = Foo<unsigned short>;
using B = Foo<short>;

B & toB(A & a) { return *reinterpret_cast<B*>(&a);}

BOOST_PYTHON_MODULE(example)
{
class_<A>("A")
.def("set", &A::set)
.def("get", &A::get)
;
class_<B>("B")
.def("set", &B::set)
.def("get", &B::get)
;
def("toB", &toB, return_internal_reference<>());
}



Python execution



>>> from example import *
>>> a = A()
>>> toB(a)
<example.A object at 0x7f7139cd27c0>
>>> toB(a).get()
Traceback (most recent call last):
File "<stdin>", line 1 in <module>
Boost.Python.ArgumentError: Python argument types in
A.get(A)
did not match C++ signature:
get(Foo<unsigned int> {lvalue})



It works well if I remove the virtual keywords in Bar.



struct Bar
{
void hi() { std::cout << "Hi Bar." << std::endl; }
};





>>> from example import *
>>> a = A()
>>> toB(a)
<example.B object at 0x7f511000c7c0>
>>> toB(a).get()
>>> 4.484155085839415e-44









share|improve this question
























  • Use a dynamic_cast, not a reinterpret_cast. You also forgot about the virtual destructors.
    – Matthieu Brucher
    Nov 6 at 14:56












  • B is not the base class of A. The user defined conversion from A to B is not defined. I am not sure if it's possible to defined a conversion so that the static_cast from a reference to A to a reference to B can happen.
    – Alex Pai
    Nov 6 at 15:43










  • Oh, actually, it's even worse... Basically, you are trying to read your unsigned as a float?? They may not even have the same size.
    – Matthieu Brucher
    Nov 6 at 15:45










  • Sorry, that's just an example. The real case is to casting between two very large template classes with same data size and different member functions. Updated the template type to short and unsigned short. But I think that's not related to the boost python issue.
    – Alex Pai
    Nov 6 at 15:52












  • Still a very bad idea, use a union instead.
    – Matthieu Brucher
    Nov 6 at 18:10













up vote
0
down vote

favorite









up vote
0
down vote

favorite











I wrap the template classes A and B through boost python, and try to cast between them. I implement the toB function and wrap it with return_internal_reference<> to achieve this. However, it doesn't return the B object in python, but returns the "dead" A object, of which member function can't be used anymore.



I narrow down the problem, which is the virtual function in the base class, Bar. The toB function correctly returns the B object referencing to A object if I remove the virtual keyword for hi(). But, what is the correct way to do this? Why does the virtual function in the base class affect the outcome? On the other hand, is there a way to cast boost python wrapping classes directly in Python?



#include <boost/python.hpp>
#include <iostream>
using namespace boost::python;

struct Bar
{
virtual void hi() { std::cout << "Hi Bar." << std::endl; }
virtual ~Bar() {}
};

template<typename T>
struct Foo : Bar
{
void set(T v) { val = v; }
T get() { return val; }
private:
T val;
};

using A = Foo<unsigned short>;
using B = Foo<short>;

B & toB(A & a) { return *reinterpret_cast<B*>(&a);}

BOOST_PYTHON_MODULE(example)
{
class_<A>("A")
.def("set", &A::set)
.def("get", &A::get)
;
class_<B>("B")
.def("set", &B::set)
.def("get", &B::get)
;
def("toB", &toB, return_internal_reference<>());
}



Python execution



>>> from example import *
>>> a = A()
>>> toB(a)
<example.A object at 0x7f7139cd27c0>
>>> toB(a).get()
Traceback (most recent call last):
File "<stdin>", line 1 in <module>
Boost.Python.ArgumentError: Python argument types in
A.get(A)
did not match C++ signature:
get(Foo<unsigned int> {lvalue})



It works well if I remove the virtual keywords in Bar.



struct Bar
{
void hi() { std::cout << "Hi Bar." << std::endl; }
};





>>> from example import *
>>> a = A()
>>> toB(a)
<example.B object at 0x7f511000c7c0>
>>> toB(a).get()
>>> 4.484155085839415e-44









share|improve this question















I wrap the template classes A and B through boost python, and try to cast between them. I implement the toB function and wrap it with return_internal_reference<> to achieve this. However, it doesn't return the B object in python, but returns the "dead" A object, of which member function can't be used anymore.



I narrow down the problem, which is the virtual function in the base class, Bar. The toB function correctly returns the B object referencing to A object if I remove the virtual keyword for hi(). But, what is the correct way to do this? Why does the virtual function in the base class affect the outcome? On the other hand, is there a way to cast boost python wrapping classes directly in Python?



#include <boost/python.hpp>
#include <iostream>
using namespace boost::python;

struct Bar
{
virtual void hi() { std::cout << "Hi Bar." << std::endl; }
virtual ~Bar() {}
};

template<typename T>
struct Foo : Bar
{
void set(T v) { val = v; }
T get() { return val; }
private:
T val;
};

using A = Foo<unsigned short>;
using B = Foo<short>;

B & toB(A & a) { return *reinterpret_cast<B*>(&a);}

BOOST_PYTHON_MODULE(example)
{
class_<A>("A")
.def("set", &A::set)
.def("get", &A::get)
;
class_<B>("B")
.def("set", &B::set)
.def("get", &B::get)
;
def("toB", &toB, return_internal_reference<>());
}



Python execution



>>> from example import *
>>> a = A()
>>> toB(a)
<example.A object at 0x7f7139cd27c0>
>>> toB(a).get()
Traceback (most recent call last):
File "<stdin>", line 1 in <module>
Boost.Python.ArgumentError: Python argument types in
A.get(A)
did not match C++ signature:
get(Foo<unsigned int> {lvalue})



It works well if I remove the virtual keywords in Bar.



struct Bar
{
void hi() { std::cout << "Hi Bar." << std::endl; }
};





>>> from example import *
>>> a = A()
>>> toB(a)
<example.B object at 0x7f511000c7c0>
>>> toB(a).get()
>>> 4.484155085839415e-44






python c++ boost boost-python






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 6 at 20:41

























asked Nov 6 at 14:54









Alex Pai

437




437












  • Use a dynamic_cast, not a reinterpret_cast. You also forgot about the virtual destructors.
    – Matthieu Brucher
    Nov 6 at 14:56












  • B is not the base class of A. The user defined conversion from A to B is not defined. I am not sure if it's possible to defined a conversion so that the static_cast from a reference to A to a reference to B can happen.
    – Alex Pai
    Nov 6 at 15:43










  • Oh, actually, it's even worse... Basically, you are trying to read your unsigned as a float?? They may not even have the same size.
    – Matthieu Brucher
    Nov 6 at 15:45










  • Sorry, that's just an example. The real case is to casting between two very large template classes with same data size and different member functions. Updated the template type to short and unsigned short. But I think that's not related to the boost python issue.
    – Alex Pai
    Nov 6 at 15:52












  • Still a very bad idea, use a union instead.
    – Matthieu Brucher
    Nov 6 at 18:10


















  • Use a dynamic_cast, not a reinterpret_cast. You also forgot about the virtual destructors.
    – Matthieu Brucher
    Nov 6 at 14:56












  • B is not the base class of A. The user defined conversion from A to B is not defined. I am not sure if it's possible to defined a conversion so that the static_cast from a reference to A to a reference to B can happen.
    – Alex Pai
    Nov 6 at 15:43










  • Oh, actually, it's even worse... Basically, you are trying to read your unsigned as a float?? They may not even have the same size.
    – Matthieu Brucher
    Nov 6 at 15:45










  • Sorry, that's just an example. The real case is to casting between two very large template classes with same data size and different member functions. Updated the template type to short and unsigned short. But I think that's not related to the boost python issue.
    – Alex Pai
    Nov 6 at 15:52












  • Still a very bad idea, use a union instead.
    – Matthieu Brucher
    Nov 6 at 18:10
















Use a dynamic_cast, not a reinterpret_cast. You also forgot about the virtual destructors.
– Matthieu Brucher
Nov 6 at 14:56






Use a dynamic_cast, not a reinterpret_cast. You also forgot about the virtual destructors.
– Matthieu Brucher
Nov 6 at 14:56














B is not the base class of A. The user defined conversion from A to B is not defined. I am not sure if it's possible to defined a conversion so that the static_cast from a reference to A to a reference to B can happen.
– Alex Pai
Nov 6 at 15:43




B is not the base class of A. The user defined conversion from A to B is not defined. I am not sure if it's possible to defined a conversion so that the static_cast from a reference to A to a reference to B can happen.
– Alex Pai
Nov 6 at 15:43












Oh, actually, it's even worse... Basically, you are trying to read your unsigned as a float?? They may not even have the same size.
– Matthieu Brucher
Nov 6 at 15:45




Oh, actually, it's even worse... Basically, you are trying to read your unsigned as a float?? They may not even have the same size.
– Matthieu Brucher
Nov 6 at 15:45












Sorry, that's just an example. The real case is to casting between two very large template classes with same data size and different member functions. Updated the template type to short and unsigned short. But I think that's not related to the boost python issue.
– Alex Pai
Nov 6 at 15:52






Sorry, that's just an example. The real case is to casting between two very large template classes with same data size and different member functions. Updated the template type to short and unsigned short. But I think that's not related to the boost python issue.
– Alex Pai
Nov 6 at 15:52














Still a very bad idea, use a union instead.
– Matthieu Brucher
Nov 6 at 18:10




Still a very bad idea, use a union instead.
– Matthieu Brucher
Nov 6 at 18:10












1 Answer
1






active

oldest

votes

















up vote
0
down vote













Exposed classes A and B don't provide all necessary information about their structure to python. Specifically that they derive from class Bar.
To fix the problem expose also class Bar to python and inform Boost.Python of the inheritance relationship between Bar and A and between Bar and B:



BOOST_PYTHON_MODULE(example)
{
class_<Bar>("Bar")
.def("hi", &Bar::hi)
;
class_<A, bases<Bar>>("A")
.def("set", &A::set)
.def("get", &A::get)
;
class_<B, bases<Bar>>("B")
.def("set", &B::set)
.def("get", &B::get)
;
def("toB", &toB, return_internal_reference<>());
}


After that is done everything should work as expected:



>>> from example import *
>>> a=A()
>>> b=toB(a)
>>> b.get()
0





share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














     

    draft saved


    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53174370%2fboost-python-return-reference-to-cast-input-parameter-not-working-with-virtual-f%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    0
    down vote













    Exposed classes A and B don't provide all necessary information about their structure to python. Specifically that they derive from class Bar.
    To fix the problem expose also class Bar to python and inform Boost.Python of the inheritance relationship between Bar and A and between Bar and B:



    BOOST_PYTHON_MODULE(example)
    {
    class_<Bar>("Bar")
    .def("hi", &Bar::hi)
    ;
    class_<A, bases<Bar>>("A")
    .def("set", &A::set)
    .def("get", &A::get)
    ;
    class_<B, bases<Bar>>("B")
    .def("set", &B::set)
    .def("get", &B::get)
    ;
    def("toB", &toB, return_internal_reference<>());
    }


    After that is done everything should work as expected:



    >>> from example import *
    >>> a=A()
    >>> b=toB(a)
    >>> b.get()
    0





    share|improve this answer

























      up vote
      0
      down vote













      Exposed classes A and B don't provide all necessary information about their structure to python. Specifically that they derive from class Bar.
      To fix the problem expose also class Bar to python and inform Boost.Python of the inheritance relationship between Bar and A and between Bar and B:



      BOOST_PYTHON_MODULE(example)
      {
      class_<Bar>("Bar")
      .def("hi", &Bar::hi)
      ;
      class_<A, bases<Bar>>("A")
      .def("set", &A::set)
      .def("get", &A::get)
      ;
      class_<B, bases<Bar>>("B")
      .def("set", &B::set)
      .def("get", &B::get)
      ;
      def("toB", &toB, return_internal_reference<>());
      }


      After that is done everything should work as expected:



      >>> from example import *
      >>> a=A()
      >>> b=toB(a)
      >>> b.get()
      0





      share|improve this answer























        up vote
        0
        down vote










        up vote
        0
        down vote









        Exposed classes A and B don't provide all necessary information about their structure to python. Specifically that they derive from class Bar.
        To fix the problem expose also class Bar to python and inform Boost.Python of the inheritance relationship between Bar and A and between Bar and B:



        BOOST_PYTHON_MODULE(example)
        {
        class_<Bar>("Bar")
        .def("hi", &Bar::hi)
        ;
        class_<A, bases<Bar>>("A")
        .def("set", &A::set)
        .def("get", &A::get)
        ;
        class_<B, bases<Bar>>("B")
        .def("set", &B::set)
        .def("get", &B::get)
        ;
        def("toB", &toB, return_internal_reference<>());
        }


        After that is done everything should work as expected:



        >>> from example import *
        >>> a=A()
        >>> b=toB(a)
        >>> b.get()
        0





        share|improve this answer












        Exposed classes A and B don't provide all necessary information about their structure to python. Specifically that they derive from class Bar.
        To fix the problem expose also class Bar to python and inform Boost.Python of the inheritance relationship between Bar and A and between Bar and B:



        BOOST_PYTHON_MODULE(example)
        {
        class_<Bar>("Bar")
        .def("hi", &Bar::hi)
        ;
        class_<A, bases<Bar>>("A")
        .def("set", &A::set)
        .def("get", &A::get)
        ;
        class_<B, bases<Bar>>("B")
        .def("set", &B::set)
        .def("get", &B::get)
        ;
        def("toB", &toB, return_internal_reference<>());
        }


        After that is done everything should work as expected:



        >>> from example import *
        >>> a=A()
        >>> b=toB(a)
        >>> b.get()
        0






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 8 at 11:01









        doqtor

        6,3851927




        6,3851927






























             

            draft saved


            draft discarded



















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53174370%2fboost-python-return-reference-to-cast-input-parameter-not-working-with-virtual-f%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Schultheiß

            Liste der Kulturdenkmale in Wilsdruff

            Android Play Services Check