[C++-sig] pyplusplus howto?

Allen Bierbaum abierbaum at gmail.com
Tue Feb 14 06:34:24 CET 2006


I have attached a first simple attempt to implement some of my ideas
for a "simple" interface on top of pyplusplus.

The interface is only a starting point for more ideas, but right now
it allows for:

- Simplified (ie. hidden) control of parser, creator, and filtering
   - provide minimal interface for getting started but still allow
power when needed
   - I would like to extend this to add support for caching and multi-file I/O
- Looking up namespaces, classes, and methods
   - by name  (with regex support)
   - by signature  (with regex support)
- Accessing specific versions of overloaded methods (based on index,
name lookup, or signature lookup).  This is one of the major limits I
found with Pyste.
- Recursively hidding/exposing namespaces, classes, etc

There is still quite a bit that needs done (see the todo's), but it
gives and idea of the interface I am testing.

Here is an example of using the interface in a file:

--------------------------------------
mod = pp.Module()
mod.parse(headers)

# Lookup class
# - Ignore method based on name
# - Ignore method using []'s to lookup
test_me_c = mod.Class("TestMe")
test_me_c.Method("method3").ignore()
test_me_c["method2"].ignore()

# Lookup set of methods using regex
methods = test_me_c.Method("method.*")
methods.ignore()

# Find set of methods and then use []'s to access one of them
overloads = test_me_c.Method("overloadA")
overloads[1].ignore()

test_sig1 = test_me_c.Method("testSig1")
test_sig2 = test_me_c.Method("testSig2")

# Look up method using signature
overloadB_float_ref = test_me_c.Method("overloadB", retval='float',
args=['float &',])
overloadB_float_ref.ignore()

# Create binding
mod.createModule("test_mod", "test_mod_bindings.cpp")
--------------------------------------


Comments?

-Allen

On 2/13/06, Matthias Baas <baas at ira.uka.de> wrote:
> Allen Bierbaum wrote:
> > To help out I setup a temporary location for a pyplusplus faq.  Take a
> > look at: https://realityforge.vrsource.org/view/PyOpenSG/PyPlusPlusFaq
> > .  I haven't copied over all the questions that I have seen on the
> > list yet but it is a start.  I would appreciate it if other people
> > that are interested in using pyplusplus could help out by either
> > migrating some of the questions/answers from the list or adding new
> > questions and entries of their own.  If you want to ask questions or
> > contribute to the faq you can register for the wiki at
> > https://realityforge.vrsource.org/view/TWiki/TWikiRegistration .
>
> Actually, instead of providing solutions using the current pyplusplus
> API I'd rather like to discuss a new/modified API that is already more
> straightforward to use.
>
> As you've noticed from that other thread I'm currently evaluating
> pyplusplus by trying to wrap the SDK of a commercial software package.
> I'm not finished with that yet as I don't have a working version of the
> bindings yet (but I think I'm close), so I don't have any final
> conclusions yet, but so far it boils down to this: I like the concept
> and the functionality of pyplusplus, but I don't like the API (or
> rather: there is no API I could like or dislike, instead I have to mess
> right with the internals of pyplusplus). But then, it's still at version
> 0.6.1, so it's ok if there's no absolutely stable and mature API. At
> least, so far the low level API has been flexible enough to deal with
> the problems I had and Roman has always been helpful with providing a
> solution (or fixing bugs). And I still believe that a higher level API
> could just be built on top of the existing package.
>
> The question now is, what would an "ideal API" look like?
>
> What I don't like about pyplusplus' way of filtering is that it
> encourages "messy" coding style ("spaghetti code"). You basically have
> one big filter function that must contain all rules and exceptions there
> are. That's fine when you have some global rules that apply to all
> classes, but when you need to do individual modifications to a
> particular class you end up with a lot of long winded tests. Then it's
> the same with the function that assigns call policies (i.e. the code
> dealing with a particular class is spread over your script whereas I
> would like to keep it together in one place). For such "specialized"
> changes that only affect one particular class or method, the API that
> Pyste offers is much more suited in my opinion (that's why I added my
> own functions).
>
> One thing I didn't fully understand is the rationale behind the
> make_flatten() function. Why should this be used by a user of
> pyplusplus? The declarations are supposed to be seen as a tree, right?
> Then why don't we leave it what it is and provide an appropriate
> iterator instead? For example, in my script I defined the following
> generator:
>
> # iterdecls
> def iterdecls(rootdecl):
>      """Iterate over one or more declaration trees.
>
>      rootdecl can be either a single declaration, a list of declarations
>      or None. A declaration must be an object derived from the declaration_t
>      class.
>      """
>      if rootdecl==None:
>          return
>
>      if type(rootdecl) is not list:
>          rootdecl = [rootdecl]
>
>      for root in rootdecl:
>          yield root
>          childs = getattr(root, "declarations", None)
>          for node in iterdecls(childs):
>              yield node
>
>
> Note that list(iterdecls(decl)) is equivalent to calling
> make_flatten(decl). But when you just want to iterate over the
> declarations you don't have to create a flattened list at all but
> instead write:
>
> for decl in iterdecls(rootdecl):
>    ...
>
> With a slightly different interface the tree traversal can be guided so
> that parts of the tree are pruned and not traversed at all (something
> you cannot do with a flattened list).
>
> The I defined a find_class() function that looks like this:
>
> # find_class
> def find_class(name):
>      """Search for a class with a particular name.
>      """
>      global decls
>
>      if name[:2]!="::":
>          name = "::"+name
>
>      res = declarations.find_first_declaration(decls,
> type=declarations.class_t, fullname=name)
>      if res==None:
>          raise RuntimeError, "Class '%s' not found"%name
>      return res
>
> The global decls variable was the root of the entire declaration tree
> (maybe it would be nice if pyplusplus would already store this).
> Basically, the function is equivalent to find_first_declaration(), but
> an important difference is that an exception is generated when the class
> was not found. You could argue that you would sooner or later also get
> an exception as find_first_declaration() returns None when it doesn't
> find the declaration, but this would hide the "true" source of the error
> and you wouldn't know which (missing) class was responsible for the error.
> Then by defining an alias Class=find_class you already have something
> that looks familiar to Pyste users:
>
> Foo = Class("Foo")
>
> In this case, Foo is the declaration object corresponding to the class
> "Foo". Here, it would be nice if Foo would provide some convenience
> methods such as finding declarations of methods, etc. I did that with a
> function instead:
>
> # find_method
> def find_method(classdecl, name, retval=None, args=None):
>      """Search for a method with a particular name.
>
>      classdecl is the declaration object of the class where the method
> should
>      be searched. name is the method name, retval the type of the return
>      value and args a list of argument types. All types (retval and args)
>      are given as strings.
>      """
>      res = []
>      for decl in iterdecls(classdecl):
>          if not isinstance(decl, declarations.member_calldef_t):
>              continue
>          if decl.name!=name:
>              continue
>          if retval!=None:
>              if decl.return_type==None or
> decl.return_type.decl_string!=retval:
>                  continue
>          if args!=None:
>              if len(args)!=len(decl.arguments):
>                  continue
>              cont_flag = False
>              for arg,argument in zip(args, decl.arguments):
>                  if arg!=argument.type.decl_string:
>                      cont_flag = True
>                      break
>              if cont_flag:
>                  continue
>
>          res.append(decl)
>      if res==[]:
>          raise RuntimeError, "method '%s::%s' not
> found"%(declarations.full_name(classdecl), name)
>      return res
>
> This function returns a list of matching declarations. For example, you
> can invoke it like this:
>
> methods = find_method(Foo, "bar", retval="bool", args=["int", "float"]) )
>
> The next function I defined is the ignore function:
>
> # ignore
> def ignore(decls):
>      """Mark one or more declarations as being ignored.
>      """
>      if type(decls) is not list:
>          decls = [decls]
>
>      for decl in decls:
>          decl._ignore_flag = True
>
> This just adds the attribute _ignore_flag to the declarations which
> indicates that this declaration should not appear in the bindings.
> The filter could then just look like this:
>
> def filter(decl):
>      """Filter function.
>      """
>      # Check if the declaration was explicitly ignored
>      if getattr(decl, "_ignore_flag", False):
>          return False
>
>      # default behavior
>      return True
>
> With this scheme (i.e. keeping the declaration tree and only mark the
> nodes that should be included/excluded) you can work with the binding in
> several ways. For example, you could either mark everything as being
> ignored and then selectively add the methods you want or just work the
> other way and include everything and then ignore some particular methods.
> A similar scheme could be devised for assigning call policies, etc.
>
> The bottom line: My suggestion would be to make the declaration tree
> more "feature rich" and make it the main data structure that the user
> will interact with. Each node should have a specialized API to
> manipulate the way that a binding for this particular object is created.
> The code creators that are later instantiated could get their parameters
> from the declaration tree.
>
> - Matthias -
>
> _______________________________________________
> C++-sig mailing list
> C++-sig at python.org
> http://mail.python.org/mailman/listinfo/c++-sig
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: gen_python_module_new.py
Type: text/x-python
Size: 1470 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20060213/ed152d65/attachment.py>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Base.h
Type: text/x-chdr
Size: 454 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20060213/ed152d65/attachment.h>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pypp_api.py
Type: text/x-python
Size: 15829 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20060213/ed152d65/attachment-0001.py>


More information about the Cplusplus-sig mailing list