[C++-sig] Patches and complete pyste replacement prototype for pyplusplus
Matthias Baas
baas at ira.uka.de
Fri Feb 24 19:20:56 CET 2006
Allen Bierbaum wrote:
> Roman and Matthias: I have attempted to implement some solution or
> method for every topic we have talked about so far on the e-mail list.
> If there was anything I missed please let me know. This API has
> evolved from my original proposal and thus differs in implementation
> from the interface Matthais has proposed. I read his proposal though
> and tried to incorporate the ideas where possible. If I haven't used
> an idea it is either because I found another way to handle the issue
> or I found issues with either the use of implementation.
Well, as I have already seen your first version and even used that as a
basis for my own version, it shouldn't come as a surprise that I
actually do prefer some things as they are implemented in my proposal
(otherwise I wouldn't have changed them in the first place). ;)
But first, I'd like to note that the overall principle of our two
versions is actually the same and because of some minor details it just
appears to be different.
Allen has a class called "Module" that is used to control the internals
of pyplusplus. In my version, this class is called "Pipeline" (as in my
opinion, this class actually represents the pyplusplus "core" I find the
name "Module" a bit misleading. But some might say this is nitpicking... ;)
In Allen's version, the user always explicitly creates an instance of
that class himself, in my version this instance is created internally
and each method is also available as function which internally calls the
corresponding method of the global instance (if desired the user could
also create an instance himself).
In Allen's version there are three main "control methods": parse(),
createCreators(), createModule(). In my version, I have the three
methods parse(), codeCreators() and writeFiles() which serve the same
purpose (as said above, these methods are also available as functions).
In both versions, the second step (creating the code creators) is done
internally if it wasn't done explicitly by the user (in my version I
also applied that rule for the parse() step, but I admit that probably
everyone has to do that step manually anyway (but it's a nice feature
for a "Hello World" example :) ).
In both versions, there are methods Class, Method, Function, etc. to
select one or more particular declarations that can then be decorated to
customize the final bindings. In Allen's version, these function either
return a DeclWrapper or MultiDeclWrapper object (depending on whether
the selection contains one or more declarations). In my version, the
return value is an IDecl object (that always acts like a MultiDeclWrapper).
Decorating the declarations also looks almost the same in both versions.
So far, both versions are almost identical. However, at the moment, a
big difference is the expressiveness of the declaration selection
methods (Class, Method, Function,...) and the exact semantics of the
declaration wrappers. And here I actually prefer my version where
obtaining an IDecl object is like doing a database query to retrieve a
set of declarations that meet certain requirements. The resulting IDecl
object can reference an arbitrary number of declarations scattered all
over the declaration tree. It can even be empty and still provide the
decoration interface. Such a "database query" can be further refined by
calling the Class, Method, Function,... methods again on a IDecl object
(this is a feature that the MultiDeclWrapper in Allen's version
currently does not allow). Each individual query can also be composed of
several filters where different filter types are concatenated with AND
and filters of the same type are concatenated with OR (see
http://i31www.ira.uka.de/~baas/pypp/classpyppapi_1_1decl_1_1_i_decl.html#063f1880bb6bc164d2f0ecd5fc92a3c1).
In Allen's version, all queries are based on the declaration type and
name. Queries for methods can optionally use the arguments or return
type as well (but the name is still mandatory). As this is a subset of
the query filters in my version I think it wouldn't be a problem to
elevate Allen's version to the same expressiveness as my version.
I tried to convert my current project to Allen's API but as I have used
my "multiple selection" feature quite often I didn't translate all of
it. Here are some examples:
In my version I'm ignoring all protected methods of all classes like this:
Method(accesstype=PROTECTED).ignore()
Here, I don't know what the corresponding code would look like in
Allen's version (but I suppose that it would also be the Method() query
that would provide a similar argument, right? But this shows already
that the 'name' argument shouldn't be mandatory).
Then I ignore all ()-operators that return a reference to a float or
double by the following line:
Method("operator()", retval=["float &", "double &"]).ignore()
Again, this addresses several classes and several methods at once. There
are four filters (and three filter types) involved in this query:
- A "type" filter because I was using the Method() function
(alternatively I could have used the generic Decl() function together
with the type=METHOD filter (which is what happens internally))
- A "name" filter (in my API I'm using the convention that the first
argument is guaranteed to be the 'name' filter. All other filters must
be specified by keyword arguments)
- Two "return value" filters (which are concatenated with OR)
When I translated my project to Allen's API the above line became:
for cls in classes:
Cls = mod.Class(cls)
try:
op = Cls.Method("operator\(\)", retval="float &|double &")
op.ignore()
except RuntimeError:
pass
(classes is a list of class names that should be exposed. I have that
list anyway, so it was no problem to use that one here)
I had to check for the RuntimeError exception because the Method() query
could be empty which is actually ok in my case. This is an example that
shows that it can be ok that a query produces an empty result.
Another thing that was confusing is that the name is *always* treated as
a regular expression. In my first attempt, I was just searching for
"operator()" and didn't get the expected results. After looking at the
API code I noticed that the string is treated as a regular expression
which means the brackets already have a special meaning and have to be
escaped. This is the reason why I suggested to mark regular expressions
explicitly (in my version by enclosing it between two '/' (maybe this
should be another character as the slash could actually be part of a
path name)).
I have more such examples, but I think they won't highlight any further
issues, so I'll leave them out.
I can't comment on the features where Allen is clearly ahead of my
version (such as templates) as I was focusing on the stuff that I need
for wrapping my SDK (which doesn't have any templates).
- Matthias -
More information about the Cplusplus-sig
mailing list