Friday, January 31, 2025

Connecting C++ to Python using Boost.Python...


Gyan.... Knowledge...Wisdom... my wife Reema reciting her own poem...

Many modern C++ applications provide a Python interface because it combines performance (C++) with ease of use (Python). Here’s why this trend has become so popular:

✔ It allows rapid prototyping.
✔ It combines speed (C++) with flexibility (Python).
✔ Python is popular in AI, ML, and scientific computing.
✔ Users can extend software via Python scripting.

 Boost.Python is a powerful library that enables seamless integration between C++ and Python. It allows C++ functions and classes to be exposed to Python, enabling direct interaction between the two languages.

As I was playing around with Boost.Python, I made my existing Factory Pattern project written in C++ to expose itself to the python using Boost.Python library.

The most important part is the following lines of code. This is kind of self explanatory - how we expose the C++ classes and methods to Python code.

// Expose Singleton Factory to Python

BOOST_PYTHON_MODULE(FactoryPattern) {

using namespace boost::python;


class_<Food, boost::noncopyable>("Food", no_init)

.def("getName", pure_virtual(&Food::getName));


class_<Biscuit, bases<Food>>("Biscuit")

.def("getName", &Biscuit::getName); // Ensure Biscuit has getName


class_<Chocolate, bases<Food>>("Chocolate")

.def("getName", &Chocolate::getName); // Ensure Chocolate has getName


class_<Factory, boost::noncopyable>("Factory", no_init)

.def("getInstance", &Factory::getInstance, return_value_policy<reference_existing_object>())

.staticmethod("getInstance")

.def("makeFood", &Factory::makeFood, return_value_policy<manage_new_object>());

}

Here's the complete source code of the C++ Factory Pattern... We will have to make a shared object (so) from this C++ project.

/*

* Food.h

*

* Created on: Mar 10, 2021

* Author: som

*/


#ifndef FOOD_H_

#define FOOD_H_

#include <string>


using namespace std;


class Food {

public:

virtual string getName() = 0;


virtual ~Food(){


}

};



#endif /* FOOD_H_ */




/*

* Chocolate.h

*

* Created on: Mar 10, 2021

* Author: som

*/


#ifndef CHOCOLATE_H_

#define CHOCOLATE_H_


#include <iostream>

#include "Food.h"


class Chocolate: public Food {

public:

Chocolate();

virtual ~Chocolate();

string getName();

};



#endif /* CHOCOLATE_H_ */




/*

* Chocolate.cpp

*

* Created on: Mar 10, 2021

* Author: som

*/


#include "Chocolate.h"



Chocolate::Chocolate() {

// TODO Auto-generated constructor stub

cout<<"Chocolate is made..."<<endl;


}


Chocolate::~Chocolate() {

// TODO Auto-generated destructor stub

}


string Chocolate::getName(){

return "It's a Chocolate";

}



/*

* Biscuit.h

*

* Created on: Mar 10, 2021

* Author: som

*/


#ifndef BISCUIT_H_

#define BISCUIT_H_


#include "Food.h"


class Biscuit: public Food {

public:

Biscuit();

string getName();

~Biscuit();

};


#endif /* BISCUIT_H_ */



/*

* Biscuit.cpp

*

* Created on: Mar 10, 2021

* Author: som

*/

#include <iostream>

#include "Biscuit.h"

using namespace std;



Biscuit::Biscuit() {

// TODO Auto-generated constructor stub

cout<<"Biscuit is made..."<<endl;


}


Biscuit::~Biscuit(){}



string Biscuit::getName(){

return "It's a Biscuit";

}




/*

* Factory.h

*

* Created on: Mar 10, 2021

* Author: som

*/


#ifndef FACTORY_H_

#define FACTORY_H_


#include <boost/python.hpp>

#include <iostream>

#include <string>


#include "Biscuit.h"

#include "Chocolate.h"


using namespace std;


class Factory{

public:

static Factory* instance;

static Factory* getInstance();


Food* makeFood(const string& type);


private:

Factory(){}


// Delete copy constructor & assignment operator (Singleton pattern)

Factory(const Factory&) = delete;

Factory& operator=(const Factory&) = delete;

};




#endif /* FACTORY_H_ */




/*

* Factory.cpp

*

* Created on: Jan 30, 2025

* Author: som

*/

#include "Factory.h"

Factory* Factory::instance = NULL;


Factory* Factory:: getInstance(){

if(Factory::instance == NULL){

Factory::instance = new Factory();

}

return Factory::instance;

}


Food* Factory::makeFood(const string& type){

if(type.compare("bi") == 0){

return new Biscuit();

}

if(type.compare("ch") == 0){

return new Chocolate();

}


return NULL;

}


// Expose Singleton Factory to Python

BOOST_PYTHON_MODULE(FactoryPattern) {

using namespace boost::python;


class_<Food, boost::noncopyable>("Food", no_init)

.def("getName", pure_virtual(&Food::getName));


class_<Biscuit, bases<Food>>("Biscuit")

.def("getName", &Biscuit::getName); // Ensure Biscuit has getName


class_<Chocolate, bases<Food>>("Chocolate")

.def("getName", &Chocolate::getName); // Ensure Chocolate has getName


class_<Factory, boost::noncopyable>("Factory", no_init)

.def("getInstance", &Factory::getInstance, return_value_policy<reference_existing_object>())

.staticmethod("getInstance")

.def("makeFood", &Factory::makeFood, return_value_policy<manage_new_object>());

}


Here's the project settings for the C++ code...








And here's the python script to access the C++ methods.


import FactoryPattern

factory = FactoryPattern.Factory.getInstance()

biscuit = factory.makeFood("bi")
print(biscuit.getName()) # Expected: "Biscuit"

chocolate = factory.makeFood("ch")
print(chocolate.getName()) # Expected: "Chocolate"


Here's a short video showing the output in a Python workspace...



Jai Hind... Jai Bharat...

By the way... there is another way to connect your Python code

to C++ - that is SWIG...


I explored SWIG sometimes back.


Here we go...



No comments: