Creating a Python Wrapper for C++ Classes
This is for Python folks who are not C or C++ experts but need to use a C++ library in their code. This article will show you how to create C++ objects from your Python code and call it's methods as needed.
1. Write an "extern" block for the Classes and methods you want accessible from Python
At the bottom of your C++ code add an extern "C" block.
The code is heavily commented. Here's the lowdown:
- Each function in the
externblock exposes a C++ class constructor or a method to the outside world (in our case to some python code). We will use python's
ctypesmodule to call this exposed classes and methods.
ctypesonly supports C data types and therefore the functions in this block must receive and return C data types.
- To expose class constructors we write a function that returns a pointer to the object.
- To expose methods of an object: we need to take the pointer to the object it belongs to as the first parameter and then add its regular parameters. We then call the method on that object pointer and return its result.
- If the returned results or the parameters are not C data types, we need to convert them. For example, typically C++ methods will return string data type as opposed to a character pointer, if that is the case we will need the exposing function to convert the string to a character pointer with code like this:
std::strcpy (new char [str.length()+1], str.c_str());
Build the library that will be shared across the two languages
This is system dependent. The following has been tested in a Mac.
Compile and create an object file named flashcards.o
g++ -c -fPIC flashcards.cpp -o flashcards.o -std=c++17
Create a shared library fc.so
g++ -shared -W1,-soname,fc.so -o fc.so flashcards.o
That is all from the C++ side. Now lets move to the Python side.
Load the library and create Python wrapper
Add the following to the top of your python code.
lib = ctypes.cdll.LoadLibrary('./fc.so')
Now all the functions that you have exposed in C++ are accessible via the lib variable.
For example we can now refer to the exposed FlashCards_getSequence_export function in python as
The Wrapper Class
__init__ of the Python class add
restypes attributes for every exposed methods' return and argument types.
argtypes should be a list (i.e.
This is a must.
At last line of the
self.obj = lib.FlashCards_export(fn) which is the call to the original C++ constructor. This will add the object pointer from C++ to the python code.
For each methods we call the C++ exposed function and return the value. If the C++ function returns a character pointer we need to decode it to make it a Python string.
That is it !!!. You can now write something like:
Decide which methods and objects you want to be accessible
Unless you are writing a full wrapper of a C++ code base, you do not need to bring in all the code to Python. If you need to do that, handcrafting the wrapper is not a good idea. You should find or write a wrapper generator. In most cases your app will need only a few methods and their classes from the C++ code base. To simplify the handcrafting process you can try not to bring in the element classes of a objects that have containers. For example if you have a FlashCards object that stores and manipulates bunch of Card objects you may have C++ code that does something like the following:
In situations like this instead of bringing in two classes and writing complex wrappers that models interaction between
two class pointers, you should add a FlashCards method like
getNextQ () which will execute getNext and then getQ of the getNext and return the Q.
Now wrap getNextQ;
Calling C code
- We create a shared object file of our c source code.
- Then use Python ctypes package load the shared object file and call the function we need to call.
To create a shared object file type the following
gcc -shared -o c_called.so -fPIC called.c
We will have a shared file name c_called.so in the current diretory
Calling C++ code
Since ctypes can only talk to C functions, you need to provide those declaring them as extern
To create a shared object file type the following
// g++ -shared -o cpp_called.so -fPIC called.cpp
We will have a shared file name cpp_called.so in the current diretory
The c and the cpp code above are very similar. We are not showing any C++ object oriented feature. There is a good description in SO for that.
It is quite straight forward but there are several steps that one needs to follow:
Packages you will need to import
- Create a ScriptEngineManager object
- Call the object's
- Cast the engine object as Invocable
- Call the invocable engine object's
- Received the result in an Object variable and then cast it to the expected object.
Perl is a stunningly customizable language. Its original motto "There's more than one way to do it" (TMTOWTDI or TIMTOWTDI, pronounced Tim Toady) makes it a great language for programmers to write very creative code. The flip side of this is that it makes it very hard for someone else to read them. This post thankfully is not about the pros and cons of this motto. Here I show you some code that demonstrates how versatile perl code can be!
In perl you can write a word without any single or double quotes around it and assign it to a variable.
As long as
TheBar is not a declared variable (or file handle or keywords, etc.) perl will assume it is a string (an english word to be precise). These are called
barewords in perl lingo. Of course if you think this is not a good idea perl gives you the flexibility to turn the feature on and off as you please with
use strict q(subs) and
no strict q(subs) respectively.
In perl a quote can be a quote or anything else you want it to be!
Suppose, you want to put three words (e.g. unix commands) in a array. In most languages the code will look something like
commands = ['ls', 'pwd', 'who']; In perl there are many ways of doing this:
- No quotes around the words, less typing.
- No commas separating the words even less typing!
()- more common array notation
We can do get it all for the price of that little
Enter the q operator
Using the q operator you can decide what your quotes for string literals should be and how do you want perl to interpret the literal.
And yes we can be both creative and confusing! Can you guess what will be in $output?
TIMTOWTDI motto is opposite of Zen of Python "There should be one — and preferably only one — obvious way to do it".
Python is good with lists. As spreadsheet and relational databases are the two primary workhorses of many information systems, we have an abundance of data in tabular format. Since tables are just collection of lists, python works quite well with tabular data.
Here's some tabular data about cats and dogs.
We can store the data in a two dimensional array. I am skipping the header for this example.
To extract the names (the first entry of every row) and store them in an array we can use python's list comprehension as follows:
We can get fancy and choose some columns and make a list of tuples.
How about getting all the data of the heaviest animal?
A few myth busting first
- MongoDB is preinstalled in your workspace. Nope it is not.
sudo apt-get install -y mongodb-orgmethod mentioned all over the Web and in mongodb page as written will work. Nope. It won’t.
What you - need to do
- Create a fresh node workspace. It is best not to start from a old workspace because cloud9's older workspaces have different software setup.
- Open a terminal and type
cd ~This command will take you one directory above your workspace. This is actually your true home directory - in the unix file system sense. If you type pwd
You will see
/home/ubuntuWe will do most of the work from this directory and not workspace directory as you are used to.
- From home directory and NOT from workspace directory type the following
curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.4.2.tgzIt will take few seconds to complete and get you back to the prompt. What you are doing is using curl to fetch a zipped version of the mongo db code.
lsto make sure the mongodb-linux-x86_64-3.4.2.tgz file is in your home directory.
- Now type
tar -zxvf mongodb-linux-x86_64-3.4.2.tgzThis unzips and untars the file creating all the directories.
- Once the command completes type
lsagain and you will see that the file has created the directories. Make sure you are still in your home (/home/ubuntu) directory.
- Now we need to modify the .bashrc file. It is a hidden file. So an ls will not show you. Type
ls -a(from your home directory :-)) you will see the .bashrc file along with several other hidden files.
vim .bashrcAt the very top, there should be three lines, each preceded by a ‘#’ character. Create a blank line under the third ‘#’ and enter the following:
export PATH=/home/ubuntu/mongodb-linux-x86_64-3.4.2/bin:$PATHWhat you are doing is adding the mongo-db to your path manually
- Press 'esc' to get into the command mode. Type
:wqin the command mode. Do not forget the ‘:’ -- the : switches vim to edit mode and wq says write and quit. If you forget the : you will not be able to get out of text entry mode and your .bashrc file will get wrong data. If that happens hit : again and type q without the the w. You will quit without changes. Then start back again.
- You are almost there. Type
. .bashrcThere are two dots. One dot then space then .bashrc (that is the filename)
- If there are no errors. You are probably doing great
which mongodyou should see the following : /home/ubuntu/mongodb-linux-x86_64-3.4.2/bin/mongod
mongod & disownThis will start the mongo daemon and get you back to the prompt. You may have to hit enter after a few seconds.
- If you type
mongonow - you will be in the database :-)
This is a quick and dirty setup.
- I did not show you how to start the database in a proper way
- I do not show you how to shut the daemon in a clean fashion.
You can do the following:
ps - awill show the processes. Write down process number that says mongod
kill -9 process num
Before putting real data you must solve these.
This link may be reliable, but this one said mongodb was pre-installed!!
Sorting (placing a collection of items in order) is one of the most common operations in information processing. Arrays are the most common data structure that holds objects we want to sort (collections). So naturally, most modern programming languages provide built-in sort methods to sort arrays. And they are usually very easy and intuitive to use.
In Python for example,
will print out the sorted array [1,9,21,100]
In Swift, the code can be even a bit "cute" with emojis!
will printout [ 1, 100, 21, 9 ]
A big oops
- It must have two parameters (say a and b)
- It must return a value that is either less than 0, or greater than 0 or equal to 0
- It must always return the same value when given a specific pair of elements a and b as its two argument
The override behavior
TL;DR: i.e. the code
Therefore by the override behavior described above a will be indexed lower than b if a < b, a will be indexed higher than b if a > b. Thus the sort will work for numbers.
Here is an excellent MDN article explaining the details.
Because we are dealing with numbers, returning the value of a-b has the same effect as spelling out the three cases a > b, a < b and a == b as we have done above. We can make our compare function anonymous and use the a-b pattern to make it a bit more compact like the following:
The good that comes out of this "pain"
OK, that is a lot to go through to sort a few numbers. We agree! But once you get used to the pattern it will become second nature. Being able to provide a customized compare function to override sort is a very powerful programming tool. We will see one example of it in action in this blog.