Click here to Skip to main content
15,894,017 members
Articles / Desktop Programming / MFC

Embedding Python In Your C++ Application

Rate me:
Please Sign up or sign in to vote.
4.58/5 (19 votes)
22 May 2006BSD4 min read 108.3K   2.7K   72  
Wrapper classes to embed the Python interpreter in Standard C++ code
/**
 * @file pyembed.cpp
 * Defines a wrapper to the Python C API.
 * @see pyembed.h
 * Copyright (C) 2006 Mindteck.
 * @author Vijay Mathew Pandyalakal
 * @date 08 May, 2006
 */

#include <iostream>
using std::cout;
#include <sstream>

#include "pyembed.h"

// a generic function to raise an exception when a python
// function call fails.
static void raise_func_failed_exception(const std::string& func_name)  
  throw (pyembed::Python_exception);

// functions to convert complex python data structures to their C++ 
// counterparts
static void make_list_from_tuple(PyObject* tuple, pyembed::String_list& out);
static void make_list_from_list(PyObject* list, pyembed::String_list& out);
static void make_map_from_dict(PyObject* dict, pyembed::String_map& out);
static std::string to_string(PyObject* val);
// :~

// class Python_env

pyembed::Python_env::Python_env(int argc, char** argv)
{
  Py_Initialize();    
  PySys_SetArgv(argc, argv);
}

pyembed::Python_env::~Python_env()
{
  Py_Finalize();
}

// class Python_exception : public std::exception

pyembed::Python_exception::Python_exception(const std::string& m)
{
  message = m;
}

pyembed::Python_exception::~Python_exception() throw()
{
}

const char* 
pyembed::Python_exception::what() throw()
{
  return message.c_str();
}

// class Python

pyembed::Python::Python(int argc, char** argv) 
{
  env = new Python_env(argc, argv);
  module = 0;
}
pyembed::Python::~Python()
{
  if (module)
    Py_DECREF(module);
  if (env)
    delete env;
}

void 
pyembed::Python::run_string(const std::string& s)
  throw (pyembed::Python_exception)
{
  int ret = PyRun_SimpleString(s.c_str());
  if (ret)
    throw Python_exception("There was a error in the script.");
}

void 
pyembed::Python::run_file(const std::string& f)
  throw (pyembed::Python_exception)
{
  FILE* file = fopen(f.c_str(), "r");
  if (!file)
    {
      std::ostringstream oss;
      oss << "Failed to open file <" << f << '>';
      throw Python_exception(oss.str());
    }
  int ret = PyRun_SimpleFile(file, f.c_str());
  fclose(file);
  if (ret)
    throw Python_exception("There was a error in the script.");
}

void 
pyembed::Python::load(const std::string& module_name)
  throw (pyembed::Python_exception)
{
  PyObject* name = PyString_FromString(module_name.c_str());
  module = PyImport_Import(name);    
  Py_DECREF(name);
  if (!module)
    {
      std::ostringstream oss;
      oss << "Failed to load module <" << module_name << ">";
      throw Python_exception(oss.str());
    }
}

void 
pyembed::Python::call(const std::string& func_name)			       
  throw (pyembed::Python_exception)
{
  Arg_map args;
  PyObject* ret_val = make_call(func_name, args);
  if (ret_val)
    Py_DECREF(ret_val);
}

void 
pyembed::Python::call(const std::string& func_name, 
			       const pyembed::Arg_map& args)
  throw (pyembed::Python_exception)
{
  PyObject* ret_val = make_call(func_name, args);
  if (ret_val)
    Py_DECREF(ret_val);
}

void 
pyembed::Python::call(const std::string& func_name, 
			       const pyembed::Arg_map& args,
			       long& ret
			       )
  throw (pyembed::Python_exception)
{
  PyObject* ret_val = make_call(func_name, args);
  if (ret_val)
    {
      if (!PyInt_Check(ret_val))
	{
	  Py_DECREF(ret_val);
	  throw Python_exception("Not an integer value in return.");
	}
      ret = PyInt_AsLong(ret_val);
      Py_DECREF(ret_val);
    }
  else
    raise_func_failed_exception(func_name);
}

void 
pyembed::Python::call(const std::string& func_name, 
				    const pyembed::Arg_map& args,
				    double& ret
				    )
  throw (pyembed::Python_exception)
{
  PyObject* ret_val = make_call(func_name, args);
  if (ret_val)
    {
      if (!PyFloat_Check(ret_val))
	{
	  Py_DECREF(ret_val);
	  throw Python_exception("Not a real value in return.");
	}
      ret = PyFloat_AsDouble(ret_val);
      Py_DECREF(ret_val);
    }
  else
    raise_func_failed_exception(func_name);
}

void 
pyembed::Python::call(const std::string& func_name, 
			       const pyembed::Arg_map& args,
			       std::string& ret
			       )
  throw (pyembed::Python_exception)
{
  PyObject* ret_val = make_call(func_name, args);
  if (ret_val)
    {
      if (!PyString_Check(ret_val))
	{
	  Py_DECREF(ret_val);            
	  throw Python_exception("Not a string value in return.");
	}
      ret = PyString_AsString(ret_val);
      Py_DECREF(ret_val);            
    }
  else
    raise_func_failed_exception(func_name);
}

void 
pyembed::Python::call(const std::string& func_name, 
			       const pyembed::Arg_map& args,
			       pyembed::String_list& ret
			       )
  throw (pyembed::Python_exception)
{
  PyObject* ret_val = make_call(func_name, args);
  if (ret_val)
    {
      if (PyTuple_Check(ret_val))
	make_list_from_tuple(ret_val, ret);
      else if (PyList_Check(ret_val))
	make_list_from_list(ret_val, ret);
      else
	{
	  Py_DECREF(ret_val);
	  throw Python_exception("Not a tuple or list in return.");
	}
      Py_DECREF(ret_val);            
    }
  else
    raise_func_failed_exception(func_name);
}

void 
pyembed::Python::call(const std::string& func_name, 
			       const pyembed::Arg_map& args,
			       pyembed::String_map& ret
			       )
  throw (pyembed::Python_exception)
{
  PyObject* ret_val = make_call(func_name, args);
  if (ret_val)
    {
      if (!PyDict_Check(ret_val))
	{
	  Py_DECREF(ret_val);            
	  throw Python_exception("Not a dictionary object in return.");
	}
      make_map_from_dict(ret_val, ret);
      Py_DECREF(ret_val);            
    }
  else
    raise_func_failed_exception(func_name);
}

// private functions

PyObject* 
pyembed::Python::get_function_object(const std::string& func_name)
  throw (pyembed::Python_exception)
{
  if (!module)
    throw Python_exception("No module loaded.");
  PyObject* func = PyObject_GetAttrString(module, (char*)func_name.c_str());
  if (!func || !PyCallable_Check(func))
    {
      std::ostringstream oss;
      oss << '<' << func_name << "> is not a function or is not callable.";
      throw Python_exception(oss.str());
    }
  return func;
}

PyObject* 
pyembed::Python::create_args(const Arg_map& args)
  throw (pyembed::Python_exception)
{
  if (!module)
    throw Python_exception("No module loaded.");
  size_t sz = args.size();  
  if (!sz)
    return 0;
  PyObject* tuple = PyTuple_New(sz);
  size_t i = 0;
  PyObject* val = 0;
  Arg_map::const_iterator it;
  for (it = args.begin(); it != args.end(); it++)
    {
      switch (it->second)
	{
	case Py_long:
	  {
	    long l = strtol(it->first.c_str(), 0, 10);
	    val = PyInt_FromLong(l);
	    break;
	  }
	case Py_real:
	  {
	    double d = strtod(it->first.c_str(), 0);	    
	    val = PyFloat_FromDouble(d);
	    break;
	  }
	case Py_string:
	  {
	    val = PyString_FromString(it->first.c_str());
	    break;
	  }
	default:
	  {
	    Py_DECREF(tuple);	    
	    throw Python_exception("Not a supported type for argument passing.");
	  }
	}
      if (!val)
	{
	  Py_DECREF(tuple);	  
	  std::ostringstream oss;
	  oss << "Failed to convert <" << it->second << "> to PyObject.";
	  throw Python_exception(oss.str());
	}      
      PyTuple_SetItem(tuple, i++, val);
      Py_DECREF(val);      
    }
  return tuple;
}

static bool cleared = false;

PyObject*
pyembed::Python::make_call(const std::string& func_name,
			   const pyembed::Arg_map& args)
  throw (pyembed::Python_exception)
{
  PyObject* func = get_function_object(func_name);
  PyObject* args_tuple = create_args(args);
  Py_XINCREF(args_tuple);
  PyObject* ret = PyObject_CallObject(func, args_tuple);
  Py_XDECREF(args_tuple);
  Py_DECREF(func);  
  return ret;
}

// local functions

void
raise_func_failed_exception(const std::string& func_name)
  throw (pyembed::Python_exception)
{
  std::ostringstream oss;
  oss << "Call to function <" << func_name << "> failed.";
  throw pyembed::Python_exception(oss.str());
}

void 
make_list_from_tuple(PyObject* tuple, pyembed::String_list& out)
{
  if (tuple)
    {
      int size = PyTuple_Size(tuple);
      for (int i=0; i<size; i++)
	{
	  PyObject* val = PyTuple_GetItem(tuple, i);
	  out.push_back(to_string(val));
	}
    }
}

void 
make_list_from_list(PyObject* list, pyembed::String_list& out)
{
  if (list)
    {
      int size = PyList_Size(list);
      for (int i=0; i<size; i++)
	{
	  PyObject* val = PyList_GetItem(list, i);
	  out.push_back(to_string(val));
	}
    }
}

void 
make_map_from_dict(PyObject* dict, pyembed::String_map& out)
{
  if (dict)
    {
      PyObject* key;
      PyObject* value;
      int pos = 0;
      while (PyDict_Next(dict, &pos, &key, &value)) 
	out[to_string(key)] = to_string(value);
    }
}

std::string 
to_string(PyObject* val)
{
  if (val)
    {
      std::ostringstream oss;
      if (PyInt_Check(val))
	oss << PyInt_AsLong(val);
      else if (PyLong_Check(val))
	oss << PyLong_AsLong(val);
      else if (PyFloat_Check(val))
	oss << PyFloat_AsDouble(val);
      else if (PyString_Check(val))
	oss << '\"' << PyString_AsString(val) << '\"';
      else if (PyTuple_Check(val))
	{
	  oss << '(';
	  pyembed::String_list ret;
	  make_list_from_tuple(val, ret);
	  size_t sz = ret.size();
	  for (size_t i=0; i<sz; i++)
	    {
	      oss << ret[i];
	      if (i != (sz - 1))
		oss << ',';
	    }
	  oss << ')';
	}
      else if (PyList_Check(val))
	{
	  oss << '[';
	  pyembed::String_list ret;
	  make_list_from_list(val, ret);
	  size_t sz = ret.size();
	  for (size_t i=0; i<sz; i++)
	    {
	      oss << ret[i];
	      if (i != (sz - 1))
		oss << ',';
	    }
	  oss << ']';
	}
      else if (PyDict_Check(val))
	{
	  oss << '{';
	  pyembed::String_map ret;
	  make_map_from_dict(val, ret);
	  size_t sz = ret.size();
	  size_t i = 0;
	  pyembed::String_map::const_iterator it;
	  for (it=ret.begin(); it != ret.end(); it++)
	    {
	      oss << it->first << ':' << it->second;
	      if (i != (sz - 1))
		oss << ',';
	      i++;
	    }
	  oss << '}';
	}
      else
	return "";
      return oss.str();
    }
  else
    return "";
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions