In [142]:
import sys
sys.path.insert(0,'/Users/jon/DEV/clang-llvm-git/llvm/tools/clang/bindings/python/')
import clang.cindex
if not clang.cindex.Config.library_file:
    clang.cindex.Config.set_library_file('/opt/local/libexec/llvm-3.7/lib/libclang.dylib')

In [143]:
import uuid
testfile='/tmp/{}.cpp'.format(uuid.uuid1())

In [207]:
%%writefile $testfile

#define C_API __attribute__((annotate("GENERATE_C_API")))

struct Shape
{
  virtual ~Shape() {}
  virtual double area(double r) const = 0;
  virtual double perimeter(double r) const = 0;
} C_API;

static const double pi = 3.14159265359;

class Circle : public Shape
{
  const double radius_;

public:
  double area() const override { return pi * radius_ * radius_; }

  double perimeter() const override { return 2 * pi * radius_; }

  Circle(double r) : radius_(r) {}
};

class Square : public Shape
{
  const double side_;

public:
  double area() const override { return side_ * side_; }

  double perimeter() const override { return 4.0 * side_; }

  Square(double s) : side_(s) {}
};


Overwriting /tmp/fe4a4919-ba3a-11e4-83e3-3c15c2be45ee.cpp

In [208]:
def get_annotations(node):
    return [c.displayname for c in node.get_children()
            if c.kind == clang.cindex.CursorKind.ANNOTATE_ATTR]

class MemberData:
    def __repr__(self):
        return str(self.type)+":\""+str(self.name)+"\""
    
    def __init__(self,cursor):
        self.name = cursor.spelling
        self.type = cursor.type.spelling

class FunctionArgument:
    def __repr__(self):
        return str(self.type)+":\""+str(self.name)+"\""
    
    def __init__(self, type, name):
        self.type = type
        self.name = name
        
class Function(object):
    
    def __repr__(self):
        return "Function:"+str(self.name)
    
    def __init__(self, cursor):
        self.function_cursor = cursor
        self.name = cursor.spelling
        arguments = [x.spelling for x in cursor.get_arguments()]
        argument_types = [x.spelling for x in cursor.type.argument_types()]
        self.type = cursor.type.spelling
        self.arguments = []
        self.annotations = get_annotations(cursor)
        for t,n in zip(argument_types,arguments):
            self.arguments.append(FunctionArgument(t,n))
        
class Class(object):
    
    def __repr__(self):
        return "Class:%s"%str(self.name)
    
    def __init__(self, cursor):
        self.name = cursor.spelling
        self.functions = []
        self.members = []
        self.annotations = get_annotations(cursor)
        self.base_classes = []
        
        for c in cursor.get_children():
            if (c.kind == clang.cindex.CursorKind.FIELD_DECL):
                m = MemberData(c)
                self.members.append(m)
            elif (c.kind == clang.cindex.CursorKind.CXX_METHOD):
                f = Function(c)
                self.functions.append(f)
            elif (c.kind == clang.cindex.CursorKind.CONSTRUCTOR):
                f = Function(c)
                self.functions.append(f)
            elif (c.kind == clang.cindex.CursorKind.CXX_BASE_SPECIFIER):
                self.base_classes.append(c.type.spelling)
        
        self.constructors = [x for x in self.functions if x.name == self.name]
                
def build_classes(cursor):
    result = []
    for c in cursor.get_children():
        if c.kind == clang.cindex.CursorKind.CLASS_DECL:
            a_class = Class(c)
            result.append(a_class)
        elif c.kind == clang.cindex.CursorKind.STRUCT_DECL:
            a_class = Class(c)
            result.append(a_class)
        elif c.kind == clang.cindex.CursorKind.NAMESPACE:
            child_classes = build_classes(c)
            result.extend(child_classes)

    return result

In [209]:
index = clang.cindex.Index.create()
translation_unit = index.parse(testfile, ['-x', 'c++', '-std=c++11'])
classes = build_classes(translation_unit.cursor)

In [210]:
from django.template import Context, Template
import django

In [211]:
if not django.conf.settings.configured : django.conf.settings.configure()

In [235]:
t_api = Template("""  {{ class.name }}_delete(const void* my{{class.name}})
  {
    delete ((const {{class.name}}*)(my{{class.name}}));
  }{% for function in class.functions %}
  
  {{ class.name }}_{{function.name}}(const void* my{{class.name}}{% for arg in function.arguments %}, {{arg.type}} {{arg.name}}{% endfor %})
  {
    return ((const {{class.name}}*)(my{{class.name}}))->{{function.name}}({% for arg in function.arguments %}{% if not forloop.first %}, {% endif %}{{arg.type}} {{arg.name}}{% endfor %});
  }{% endfor %}""")

In [236]:
t_obj = Template("""{% for function in class.constructors %}
  const void* {{base}}_{{ class.name }}_new({% for arg in function.arguments %}{% if not forloop.first %}, {% endif %}{{arg.type}} {{arg.name}}{% endfor %})
  {
    return new (std::nothrow) {{ class.name }}({% for arg in function.arguments %}{% if not forloop.first %}, {% endif %}{{arg.name}}{% endfor %});
  }{% endfor %}""")

In [237]:
api_classes = []
api_classes.extend([c for c in classes if 'GENERATE_C_API' in c.annotations])
obj_classes = []
for c in classes:
    for b in c.base_classes:
        if b in [x.name for x in api_classes]:
            obj_classes.append((c,b))

In [238]:
print """extern "C"
{"""
for c in api_classes:
    print(t_api.render(Context({"class": c})))
for c,b in obj_classes:
    print(t_obj.render(Context({"class": c, "base": b})))
print "}"


extern "C"
{
  Shape_delete(const void* myShape)
  {
    delete ((const Shape*)(myShape));
  }
  
  Shape_area(const void* myShape, double r)
  {
    return ((const Shape*)(myShape))->area(double r);
  }
  
  Shape_perimeter(const void* myShape, double r)
  {
    return ((const Shape*)(myShape))->perimeter(double r);
  }

  const void* Shape_Circle_new(double r)
  {
    return new (std::nothrow) Circle(r);
  }

  const void* Shape_Square_new(double s)
  {
    return new (std::nothrow) Square(s);
  }
}