<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=US-ASCII">
<META NAME="Generator" CONTENT="MS Exchange Server version 5.5.2653.12">
<TITLE>AW: [C++-sig] Wrapping opaque pointers returned from API functions</TITLE>
</HEAD>
<BODY>
<P><FONT SIZE=2>Hi Dave,</FONT>
</P>
<P><FONT SIZE=2>I knew it was something simple (although not entirely obvious :-))</FONT>
</P>
<P><FONT SIZE=2>Thanks a lot,</FONT>
</P>
<P><FONT SIZE=2>Gottfried</FONT>
</P>
<P><FONT SIZE=2>> -----Ursprungliche Nachricht-----</FONT>
<BR><FONT SIZE=2>> Von: David Abrahams [<A HREF="mailto:dave@boost-consulting.com">mailto:dave@boost-consulting.com</A>]</FONT>
<BR><FONT SIZE=2>> Gesendet: Donnerstag, 9. Januar 2003 18:59</FONT>
<BR><FONT SIZE=2>> An: c++-sig@python.org</FONT>
<BR><FONT SIZE=2>> Betreff: Re: [C++-sig] Wrapping opaque pointers returned from API</FONT>
<BR><FONT SIZE=2>> functions</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> Gottfried.Ganssauge@HAUFE.DE writes:</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> > I am trying to wrap an API like the following:</FONT>
<BR><FONT SIZE=2>> > typedef struct workspace_ *workspace;</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > workspace osr_init (const char *) { return 0; }</FONT>
<BR><FONT SIZE=2>> > void osr_exit (workspace ws) { std::cerr << ws << std::endl; }</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > My first attempt at wrapping that API was</FONT>
<BR><FONT SIZE=2>> > BOOST_PYTHON_MODULE(_pyOSR)</FONT>
<BR><FONT SIZE=2>> > {</FONT>
<BR><FONT SIZE=2>> > def ("osr_init", &::osr_init, </FONT>
<BR><FONT SIZE=2>> return_value_policy<return_by_value>());</FONT>
<BR><FONT SIZE=2>> > def ("osr_exit", &::osr_exit);</FONT>
<BR><FONT SIZE=2>> > }</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > That compiled fine, but (of course) failed miserably when </FONT>
<BR><FONT SIZE=2>> run with the</FONT>
<BR><FONT SIZE=2>> > following test script:</FONT>
<BR><FONT SIZE=2>> > import _pyOSR</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > ws = _pyOSR.osr_init(".")</FONT>
<BR><FONT SIZE=2>> > print ws</FONT>
<BR><FONT SIZE=2>> > _pyOSR.osr_exit(ws)</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > Here is the traceback:</FONT>
<BR><FONT SIZE=2>> > Traceback (most recent call last):</FONT>
<BR><FONT SIZE=2>> > File</FONT>
<BR><FONT SIZE=2>> > </FONT>
<BR><FONT SIZE=2>> "C:\daten\devel\sourcecode-cd\OptiSearch\src\pyOSR\msvc\Releas</FONT>
<BR><FONT SIZE=2>> e\test.py",</FONT>
<BR><FONT SIZE=2>> > line 3, in ?</FONT>
<BR><FONT SIZE=2>> > ws = _pyOSR.osr_init(".")</FONT>
<BR><FONT SIZE=2>> > TypeError: No to_python (by-value) converter found for C++ </FONT>
<BR><FONT SIZE=2>> type: struct workspace_ *</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> Right. You asked it to dereference the workspace_ pointer and return</FONT>
<BR><FONT SIZE=2>> a copy of the result inside a Python object.</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> > Now I thought: ok, let's register a converter</FONT>
<BR><FONT SIZE=2>> > extern "C" void</FONT>
<BR><FONT SIZE=2>> > dealloc(PyObject* self) {</FONT>
<BR><FONT SIZE=2>> > PyObject_Del(self);</FONT>
<BR><FONT SIZE=2>> > }</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > struct workspace_wrapper : PyObject { workspace x; };</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > PyTypeObject workspaceType = {</FONT>
<BR><FONT SIZE=2>> > PyObject_HEAD_INIT(NULL)</FONT>
<BR><FONT SIZE=2>> > 0,</FONT>
<BR><FONT SIZE=2>> > "workspace",</FONT>
<BR><FONT SIZE=2>> > sizeof(workspace_wrapper),</FONT>
<BR><FONT SIZE=2>> > 0,</FONT>
<BR><FONT SIZE=2>> > dealloc</FONT>
<BR><FONT SIZE=2>> > };</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > struct p_from_workspace {</FONT>
<BR><FONT SIZE=2>> > static workspace &execute (workspace_wrapper &p_) {</FONT>
<BR><FONT SIZE=2>> ^^^^^^^^^^^</FONT>
<BR><FONT SIZE=2>> Should be workspace_&</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> > return p_.x;</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> Likewise, should be return *p_.x;</FONT>
<BR><FONT SIZE=2>> [see caveat below]</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> > }</FONT>
<BR><FONT SIZE=2>> > };</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > struct pworkspace_to_python</FONT>
<BR><FONT SIZE=2>> > : to_python_converter<workspace, pworkspace_to_python></FONT>
<BR><FONT SIZE=2>> > {</FONT>
<BR><FONT SIZE=2>> > static PyObject* convert(workspace x) {</FONT>
<BR><FONT SIZE=2>> > workspace_wrapper *o =</FONT>
<BR><FONT SIZE=2>> > PyObject_New (workspace_wrapper, &workspaceType);</FONT>
<BR><FONT SIZE=2>> > o->x = x;</FONT>
<BR><FONT SIZE=2>> > return ((PyObject*) o);</FONT>
<BR><FONT SIZE=2>> > }</FONT>
<BR><FONT SIZE=2>> > };</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > BOOST_PYTHON_MODULE(_pyOSR)</FONT>
<BR><FONT SIZE=2>> > {</FONT>
<BR><FONT SIZE=2>> > pworkspace_to_python();</FONT>
<BR><FONT SIZE=2>> > lvalue_from_pytype<p_from_workspace, &workspaceType>();</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > def ("osr_init", &::osr_init, </FONT>
<BR><FONT SIZE=2>> return_value_policy<return_by_value>());</FONT>
<BR><FONT SIZE=2>> > def ("osr_exit", &::osr_exit);</FONT>
<BR><FONT SIZE=2>> > }</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > This time the output changed:</FONT>
<BR><FONT SIZE=2>> > <workspace object at 0x00768EA0></FONT>
<BR><FONT SIZE=2>> > Traceback (most recent call last):</FONT>
<BR><FONT SIZE=2>> > File</FONT>
<BR><FONT SIZE=2>> > </FONT>
<BR><FONT SIZE=2>> "C:\daten\devel\sourcecode-cd\OptiSearch\src\pyOSR\msvc\Releas</FONT>
<BR><FONT SIZE=2>> e\test.py",</FONT>
<BR><FONT SIZE=2>> > line 5, in ?</FONT>
<BR><FONT SIZE=2>> > _pyOSR.osr_exit(ws)</FONT>
<BR><FONT SIZE=2>> > TypeError: bad argument type for built-in operation</FONT>
<BR><FONT SIZE=2>> ></FONT>
<BR><FONT SIZE=2>> > The call to osr_init() succeeded and produced the expected result: a</FONT>
<BR><FONT SIZE=2>> > workspace object.</FONT>
<BR><FONT SIZE=2>> > Unfortunately the library is unable to find the proper converter for</FONT>
<BR><FONT SIZE=2>> > converting that object back to my workspace pointer. I'm </FONT>
<BR><FONT SIZE=2>> sure, there is</FONT>
<BR><FONT SIZE=2>> > something obvious I'm doing wrong, but what?</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> I'm impressed with how close you got, especially considering that most</FONT>
<BR><FONT SIZE=2>> of the tools you had to use are undocumented!</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> When converting from_python to a C++ pointer type, the library looks</FONT>
<BR><FONT SIZE=2>> for an lvalue converter for the *pointee*. In other words, to convert</FONT>
<BR><FONT SIZE=2>> to a Foo*, the source Python object must contain a Foo.</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> Obviously, wrapping opaque pointers is a common thing to want to do,</FONT>
<BR><FONT SIZE=2>> and the library should provide a better facility for it.</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> Caveat: The code I've suggested above technically invokes undefined</FONT>
<BR><FONT SIZE=2>> behavior if p_.x is 0, but I'm not aware of any platform which will</FONT>
<BR><FONT SIZE=2>> have a problem with it.</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> -- </FONT>
<BR><FONT SIZE=2>> David Abrahams</FONT>
<BR><FONT SIZE=2>> dave@boost-consulting.com * <A HREF="http://www.boost-consulting.com" TARGET="_blank">http://www.boost-consulting.com</A></FONT>
<BR><FONT SIZE=2>> Boost support, enhancements, training, and commercial distribution</FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> </FONT>
<BR><FONT SIZE=2>> _______________________________________________</FONT>
<BR><FONT SIZE=2>> C++-sig mailing list</FONT>
<BR><FONT SIZE=2>> C++-sig@python.org</FONT>
<BR><FONT SIZE=2>> <A HREF="http://mail.python.org/mailman/listinfo/c++-sig" TARGET="_blank">http://mail.python.org/mailman/listinfo/c++-sig</A></FONT>
<BR><FONT SIZE=2>> </FONT>
</P>
</BODY>
</HTML>