[C++-sig] how to wrap this handle/body design?

FLETCHER, Shayne, FM Shayne.FLETCHER at rbos.com
Sun Mar 2 14:18:57 CET 2003


Hi All,

Apologies in advance for the length of the post. I have been trying to work
out the details of how to reflect a handle/body with a clone semantics type
design but am running in to diffuculties...

The idea in the code that follows is that "functors" wrap polymorphic
"functor implementations". Existing functor implementations are in C++ but
we should like to be able to design new functor implementations in Python,
wrap them up in C++ functors and use them both from C++ and Python. Functor
implementations are created by "functor constructor" objects which are
registered with a "functor factory".

The basic problem is 'dangling references':

  // boost/libs/python/src/converter/from_python.cpp

  void* lvalue_result_from_python(
      PyObject* source
      , registration const& converters
      , char const* ref_type)
  {
      handle<> holder(source);
      if (source->ob_refcnt <= 2)
      {
          handle<> msg(
              ::PyString_FromFormat(
                  "Attempt to return dangling %s to object of type: %s"
                  , ref_type
                  , converters.target_type.name()));
          
          PyErr_SetObject(PyExc_ReferenceError, msg.get());

          throw_error_already_set();
      }
      
      void* result = get_lvalue_from_python(source, converters);
      if (!result)
          (throw_no_lvalue_from_python)(source, converters, ref_type);
      return result;
  }
  
}

We are on boost 1.29.0. Most of the program follows - the troublesome
methods are marked with "//???" (the most notable is the
'functor_constructor_wrapper::create_functor()' method). Any assistance will
be most gratefully received:

  class functor_impl
  {
  public:
    virtual ~functor_impl(){}
    virtual void Attach() const = 0;
    virtual void Release() const = 0;
    virtual void perform() const = 0;
    virtual functor_impl* clone() const = 0;
  };

  template<class T>
  inline T* cloned_impl_ptr(T** pp, T* lp)
  {
    T* pTemp = 0;
    if(lp)
    {
      pTemp = lp->clone();
      pTemp->Attach();
    }
    if(*pp)
      (*pp)->Release();
    *pp = pTemp;

    return pTemp;
  }

  template<class T>
  class cloned_impl_ptr
  {
  public:
    T* p;
   
   cloned_impl_ptr()
   {
    p = 0L;
   }
   cloned_impl_ptr(T* lp) // takes ownership - does not clone()!
   {
     p = lp;
     if(p)
     {
       p->Attach();
     } 
   }
   cloned_impl_ptr(const cloned_impl_ptr<T>& lp)
   {
     if(lp.p)
     {
       p = lp.p->clone();
       p->Attach();
     }
     else p = lp.p;
   }
   ~cloned_impl_ptr()
   {
      if(p)
        p->Release();
   }
   T* operator->() const
   {
     assert(p!=0L);
     return p;
   }
   T* operator=(const cloned_impl_ptr<T>& lp)
   {
     return cloned_impl_ptr_assign(&p, lp.p);
   }
  };

  class functor
  {
    cloned_impl_ptr<functor_impl> m_pimpl;
  public:
    functor()
      : m_pimpl(0)
    {}
    functor(functor_impl* pimpl)
      : m_pimpl(pimpl)
    {}
    void perform() const
    {
      m_pimpl->perform();
    }
  };

  class functor_constructor
  {
  public:
    virtual functor create_functor() = 0;
    virtual functor_impl* create_functor_impl() = 0;
  };

  class functor_factory
  {
  private:
    std::map<std::string, functor_constructor*> m_ctors;
  
  public:
    static functor_factory& instance()
    {
      static functor_factory ff;
      return ff;
    }
    void register_functor(std::string const& name
                          , functor_constructor* p_functor_constructor)
    {
      m_ctors[name] = p_functor_constructor;
    }
    functor create_functor(std::string const& name)
    {
      functor_constructor* p_functor_constructor = m_ctors[name];
      return p_functor_constructor->create_functor();
    }
    functor_impl* create_functor_impl(std::string const& name)
    {
      functor_constructor* p_functor_constructor = m_ctors[name];
      return p_functor_constructor->create_functor_impl();
    }
  };
  
  struct functor_impl_wrapper : functor_impl
  {
    PyObject* m_self;

    functor_impl_wrapper(PyObject* self)
      : m_self(self)
    {}
    functor_impl_wrapper(PyObject* self
                         , functor_impl const& rhs)
      : m_self(self), functor_impl(rhs)
    {}
    void Attach() const
    {
      Py_INCREF(m_self);
    }
    void Release() const
    {
      Py_DECREF(m_self);
    }
    void perform() const
    {
      boost::python::call_method(m_self
        , "perform"
        , (boost::type<void>*)0);
    }

    functor_impl* clone() const
    {
	// ???
      // how should this be implemented?
    }

  };

  functor* bind_functor_to_impl(functor_impl* p_impl)
  {
    return new functor(p_impl);
  }
 
  struct functor_constructor_wrapper : functor_constructor
  {
    PyObject* m_self;
  
    functor_constructor_wrapper(PyObject* self)
      : m_self(self)
    {}
    functor_constructor_wrapper(PyObject* self
                                , functor_constructor const& rhs)
      : m_self(self), functor_constructor(rhs)
    {}

    functor_impl* create_functor_impl()
    {
	// how should this be implemented?
    }

    functor create_functor()
    {
      // ???
	// how should this be implemented?

      // first cut was,
      //  return boost::python::call_method(m_self
      //  ,"create_functor"
      //  , (boost::type<functor>*)0));     
      // but this gives rise to runtime errors due to
      // attempt to return a dangling reference
    }
  };

  void invoke_functor_perform(functor const& f)
  {
    f.perform();
  }

  void create_functor_and_perform(std::string const& which)
  {
    functor f = functor_factory::instance().create_functor(which);
    f.perform();
  }

BOOST_PYTHON_MODULE(isle)
{
  class_<functor_impl
    , functor_impl_wrapper
    , boost::noncopyable>("functor_impl", init<>())
    .def(init<functor_impl const&>())
    ;
  class_<functor_constructor
    , functor_constructor_wrapper
    , boost::noncopyable>("functor_constructor", init<>())
    ;
  class_<functor>("functor", init<>())
   .def(init<functor_impl*>())
   .def(init<functor const&>())
   .def("perform", &functor::perform)
   ;
  def("bind_functor_to_impl"
     , &bind_functor_to_impl
     , return_value_policy<manage_new_object>())
     ;
  class_<functor_factory, boost::noncopyable>("functor_factory", no_init)
     .def("register_functor", &functor_factory::register_functor)
     .def("create_functor", &functor_factory::create_functor)
     ;
  def("functor_factory_instance"
     , &functor_factory::instance
     , return_value_policy<reference_existing_object>())
     ;
  def("invoke_functor_perform"
     , &invoke_functor_perform)
     ;
  def("create_functor_and_perform"
     , &create_functor_and_perform)
     ;
}

Then in Python,

import unittest
import isle

class my_functor_impl(isle.functor_impl):
    def __init__(self, value=0):
        self.__value = value
        isle.functor_impl.__init__(self)
    def perform(self):
        print "      invocation: my_functor_impl.perform ", self.__value
    def clone(self):
        print "      invocation: my_functor_impl.clone"
        mfi = my_functor_impl()
        isle.functor_impl.__init__(mfi, self)
        mfi.__value = self.__value + 1
        return mfi

class my_functor_constructor(isle.functor_constructor):
    def __init__(self):
        isle.functor_constructor.__init__(self)
    def create_functor(self):
        return isle.bind_functor_to_impl(self.create_functor_impl())
    def create_functor_impl(self):
        mfi  = my_functor_impl(0)
        return mfi

mfc = my_functor_constructor()
isle.functor_factory_instance().register_functor("my_functor", mfc)

print "\nTesting functors..."

class functor_test_case(unittest.TestCase):
    """Tests for functors

    """
    def setUp(self):
        """Initialize test case"""
        pass

    def tearDown(self):
        """Uninitialize test case"""
        pass

    def test(self):
        """Test functor creation & invocation of functors."""
        print "\n  Testing creation & invocation of python defined
functor..."
        functor =
isle.functor_factory_instance().create_functor("my_functor")
        print "\n    Invoking perform on functor from python..."
        functor.perform()
        print "\n    Invoking perform on functor from C++..."
        isle.invoke_functor_perform(functor)
        print "\n   Invoking create & perform..."
        isle.create_functor_and_perform("my_functor")
    
    def testFunctor(self):
        pass



***********************************************************************
      Visit our Internet site at http://www.rbsmarkets.com

This e-mail is intended only for the addressee named above.
As this e-mail may contain confidential or privileged information,
if you are not the named addressee, you are not authorised to
retain, read, copy or disseminate this message or any part of it.
The Royal Bank of Scotland plc is registered in Scotland No 90312
Registered Office: 36 St Andrew Square, Edinburgh EH2 2YB 
Regulated by the Financial Services Authority
***********************************************************************




More information about the Cplusplus-sig mailing list