[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