[Updated Links and API use on 2018-01-25]
Write a C++ class out to a file in the current working directory
In [1]:
outputfile = "Shape.h"
In [2]:
%%file $outputfile
#include <stdexcept>
#include <string>
#ifdef __clang__
#define C_API __attribute__((annotate("GENERATE_C_API")))
#else
#define C_API
#endif
#include <ffig/attributes.h>
struct FFIG_EXPORT Shape
{
virtual ~Shape() = default;
virtual double area() const = 0;
virtual double perimeter() const = 0;
virtual const char* name() const = 0;
} __attribute__((annotate("GENERATE_C_API")));
static const double pi = 4.0;
class Circle : public Shape
{
const double radius_;
public:
double area() const override
{
return pi * radius_ * radius_;
}
double perimeter() const override
{
return 2 * pi * radius_;
}
const char* name() const override
{
return "Circle";
}
Circle(double radius) : radius_(radius)
{
if ( radius < 0 )
{
std::string s = "Circle radius \"" + std::to_string(radius_) + "\" must be non-negative.";
throw std::runtime_error(s);
}
}
};
Compile our header to check it's valid C++
In [3]:
%%sh
clang++-3.8 -x c++ -fsyntax-only -std=c++14 -I../ffig/include Shape.h
Read the code using libclang
In [4]:
import sys
sys.path.insert(0,'..')
import ffig.clang.cindex
index = ffig.clang.cindex.Index.create()
translation_unit = index.parse(outputfile, ['-x', 'c++', '-std=c++14', '-I../ffig/include'])
In [5]:
import asciitree
def node_children(node):
return (c for c in node.get_children() if c.location.file.name == outputfile)
print asciitree.draw_tree(translation_unit.cursor,
lambda n: [c for c in node_children(n)],
lambda n: "%s (%s)" % (n.spelling or n.displayname, str(n.kind).split(".")[1]))
Turn the AST into some easy to manipulate Python classes
In [6]:
from ffig import cppmodel
In [7]:
model = cppmodel.Model(translation_unit)
In [8]:
model
Out[8]:
In [9]:
[f.name for f in model.functions][-5:]
Out[9]:
In [10]:
[c.name for c in model.classes][-5:]
Out[10]:
In [11]:
shape_class = [c for c in model.classes if c.name=='Shape'][0]
In [12]:
["{}::{}".format(shape_class.name,m.name) for m in shape_class.methods]
Out[12]:
Look at the templates the generator uses
In [13]:
%cat ../ffig/templates/json.tmpl
Run the code generator
In [14]:
%%sh
cd ..
python -m ffig -b json.tmpl rb.tmpl python -m Shape -i demos/Shape.h -o demos/
See what it created
In [15]:
%ls
In [16]:
%cat Shape.json
Build some bindings with the generated code.
In [17]:
%%file CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 14)
add_library(Shape_c SHARED Shape_c.cpp)
target_include_directories(Shape_c PRIVATE ../ffig/include)
In [18]:
%%sh
cmake .
cmake --build .
In [19]:
%%python2
import shape
c = shape.Circle(8)
print "A {} with radius {} has area {}".format(c.name(), 8, c.area())
In [20]:
%%script pypy
import shape
c = shape.Circle(8)
print "A {} with radius {} has area {}".format(c.name(), 8, c.area())
In [21]:
%%ruby
load "Shape.rb"
c = Circle.new(8)
puts("A #{c.name()} with radius #{8} has area #{c.area()}")