Well, I was attempting to rewrite the property interface to make it a
little more generic… and I accidentally obviated the need for SWIG.
– Interesting… go on…
OK, say you are a user, you are making a nice fast c++ block, and you
intend to use it in python. This is like the most common GR use case.
Scratch the following things off your TODO list, because you no longer
need to do them: public headers, symbol exports, SWIG wrappers, Python
files, install rules for all these files…
– How simple can it be?
Write your cpp source, compile it to a loadable module. Install module
to prefix/lib/gras/modules, install grc xml wrapper to
prefix/share/gnuradio/grc. Boom your done, your block is now usable in
C++, python, and GRC frameworks.
– Give me an example!
This example c++ source file registers the block and its methods, and it
can be created and accessed in python. Its all in the unit tests folder.
Also, check the CMakeLists for module compilation calls:
Sorry for the crummy unit test example. Soon I will switch GrExtras over
to the new interface. So there will be a complete project making use of
this new feature. Now I just have to figure out where to put the
documentation without public headers!
------ gory details below -----
So, the property interface gave us setter and getter registration. My
goal was to extend the concept so blocks could register arbitrary
methods, with arbitrary arguments into the system. For lack of a better
term, its called the “callable interface”. And here are the docs:
– Why register methods?
Calling methods through this interface is inherently thread-safe. The
underlying methods are guaranteed to not be called during an execution
of the work() method. This means users do not have to sprinkle
boost::mutex calls around their code to get thread safety for things
SWIG’ing class methods, and dealing with the complications of SWIG is
unnecessary. Basically, the framework was SWIG’d once to export the
ability to call registered methods. Once a user registers a class
method, its simply just available when the block is instantiated in
Registering methods gives them programmatic access. Blocks can find
each other in the topology and make function calls on each other for
configuration purposes. Blocks dont have to know anything about each
other’s types and class declarations to do this.
Every argument and return value can be serialized. This opens the
door for deploying and configuring a block on a remote network location
and eventually network distributing the topology. Also, there is
currently JSON access for the block’s registered methods for future web
– Block factory:
The callable interface eliminates the need for SWIG’ing the individual
methods into python, but we still would need to swig the factory
function. So the companion feature is the block factory. Users can
register factory functions for their block in C++ and then instantiate
the block in python. Just like the callable interface, the factory
interface has python bindings provided by the framework, so the user
need not lift a finger.
– When to use SWIG?
Python bindings are hard, and SWIG makes this part easy. Using SWIG to
bind the framework is great. But most users don’t have to worry about
that, since framework bindings are a one time development effort. The
only time I can think of that a user might want to use SWIG is to bind a
new type into python. PMC gives us translation between native python and
C++ types, but any new types have to be bound and registered. PMC is
highly extensible though: https://github.com/guruofquality/PMC/wiki
– Too lazy to compile?
Why bother compiling at all? Lets have the framework compile my c++
code: There is already a Clang JIT block if GrExtras. Perhaps soon users
can install the cpp files and let the framework manage a compilation
cache for object files. I just need another long weekend
Thoughts, feedback, questions?