#include <string>
#include <vector>
#include <list>
#include <map>
#include <typeinfo>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
#ifdef _DEBUG
# undef _DEBUG
# include <Python.h>
# define _DEBUG
#else
# include <Python.h>
#endif
#include "MemoryMapping.h"
#include "GL/PythonTests.h"
namespace { // Avoid cluttering the global namespace.
using namespace std;
class Shape
{
public:
int x;
};
bool operator==(const Shape& a, const Shape& b) {
return (a.x == b.x);
}
typedef std::vector<int> IntList;
typedef std::vector<Shape> Geometry;
typedef std::map<std::string,Geometry> Layer;
// https://wiki.python.org/moin/boost.python/StlContainers
// http://stackoverflow.com/questions/3240971/does-boost-python-support-a-function-returning-a-vector-by-ref-or-value
void IndexError() { PyErr_SetString(PyExc_IndexError, "Index out of range"); }
template<class T>
struct std_item
{
typedef typename T::value_type V;
static V& get(T& x, int i)
{
static V nothing;
if( i<0 ) i+=x.size();
if( i>=0 && i<(int)x.size() ) return x[i];
IndexError();
return nothing;
}
static void set(T& x, int i, V const& v)
{
if( i<0 ) i+=x.size();
if( i>=0 && i<(int)x.size() ) x[i]=v;
else IndexError();
}
static void del(T& x, int i)
{
if( i<0 ) i+=x.size();
if( i>=0 && i<(int)x.size() ) x.erase(x.begin() + i);
else IndexError();
}
static void add(T& x, V const& v)
{
x.push_back(v);
}
static bool in(T const& x, V const& v)
{
return find_eq(x.begin, x.end, v) != x.end();
}
static int index(T const& x, V const& v)
{
int i=0;
for(typename T::const_iterator it=x.begin(); it!=x.end(); ++it,++i)
if( *it == v ) return i;
return -1;
}
};
void KeyError() { PyErr_SetString(PyExc_KeyError, "Key not found"); }
template<class T>
struct map_item
{
typedef typename T::key_type K;
typedef typename T::mapped_type V;
static V& get(T& x, K const& i)
{
static V nothing;
if( x.find(i) != x.end() ) return x[i];
KeyError();
return nothing;
}
static void set(T& x, K const& i, V const& v)
{
x[i]=v; // use map autocreation feature
}
static void del(T& x, K const& i)
{
if( x.find(i) != x.end() ) x.erase(i);
else KeyError();
}
static bool in(T const& x, K const& i)
{
return x.find(i) != x.end();
}
static int index(T const& x, K const& k)
{
int i=0;
for(typename T::const_iterator it=x.begin(); it!=x.end(); ++it,++i)
if( it->first == k ) return i;
return -1;
}
static std::list<K> keys(T const& x)
{
std::list<K> t;
for(T::const_iterator it=x.begin(); it!=x.end(); ++it)
t.push_back(it->first);
return t;
}
static std::list<V> values(T const& x)
{
std::list<V> t;
for(T::const_iterator it=x.begin(); it!=x.end(); ++it)
t.push_back(it->second);
return t;
}
static std::list<std::pair<K,V> > items(T const& x)
{
std::list<std::pair<K,V> > t;
for(T::const_iterator it=x.begin(); it!=x.end(); ++it)
t.push_back(make_pair(it->first,it->second));
return t;
}
};
// A friendly class.
class hello
{
public:
typedef std::map<std::string,std::string> StrStrMap;
hello(const std::string& a_country)
{
m_country = a_country;
m_countryCodeMap["Australia"] = "au";
m_countryCodeMap["Great Britian"] = "gb";
m_countryCodeMap["America"] = "us";
m_countryCodeMap["France"] = "fr";
m_countryCodeMap["Japan"] = "jp";
}
std::string greet() const { return "Hello from " + m_country; }
StrStrMap& getMap() { return m_countryCodeMap; }
void setMap(StrStrMap& newMap) { m_countryCodeMap = newMap; }
void setMap2(boost::python::dict& pyMap) { }
std::string debugMap()
{
std::string ret;
for (auto it = m_countryCodeMap.begin(); it != m_countryCodeMap.end(); ++it)
ret += (*it).first + " = " + (*it).second + "\n";
return ret;
}
private:
std::string m_country;
StrStrMap m_countryCodeMap;
};
// A function taking a hello object as an argument.
std::string invite(const hello& w)
{
return w.greet() + "! Please come soon!";
}
// boost::python::dict dict;
#define PyExposeStruct(cls) class_<cls>(#cls)
#define PyExposeVector(cls) class_<cls>(#cls).def(vector_indexing_suite<cls>())
#define PyExposeMap(cls) class_<cls>(#cls).def(map_indexing_suite<cls>())
BOOST_PYTHON_MODULE(hello_ext)
{
using namespace boost::python;
// def("greet", greet);
//def("invite", invite);
/*
class_<Shape>("Shape");
class_<Geometry>("Geometry")
.def("__len__", &Geometry::size)
.def("clear", &Geometry::clear)
.def("append", &std_item<Geometry>::add,
with_custodian_and_ward<1,2>()) // to let container keep value
.def("__getitem__", &std_item<Geometry>::get,
return_value_policy<copy_non_const_reference>())
.def("__setitem__", &std_item<Geometry>::set,
with_custodian_and_ward<1,2>()) // to let container keep value
.def("__delitem__", &std_item<Geometry>::del)
.def("__index", &std_item<Geometry>::index)
.def("__iter__", boost::python::iterator<Geometry>())
//.def("__iter__", std::iterator<Geometry>())
;
class_<Layer>("Layer")
.def("__len__", &Layer::size)
.def("clear", &Layer::clear)
.def("__getitem__", &map_item<Layer>::get,
return_value_policy<copy_non_const_reference>())
.def("__setitem__", &map_item<Layer>::set,
with_custodian_and_ward<1,2>()) // to let container keep value
.def("__delitem__", &map_item<Layer>::del)
.def("__contains__", &map_item<Layer>::in)
.def("__index", &map_item<Layer>::index)
.def("keys", &map_item<Layer>::keys)
.def("values", &map_item<Layer>::values)
.def("items", &map_item<Layer>::items)
;
class_<hello::StrStrMap>("hello::StrStrMap")
.def("__len__", &hello::StrStrMap::size)
.def("clear", &hello::StrStrMap::clear)
.def("__getitem__", &map_item<hello::StrStrMap>::get,
return_value_policy<copy_non_const_reference>())
.def("__setitem__", &map_item<hello::StrStrMap>::set,
with_custodian_and_ward<1,2>()) // to let container keep value
.def("__delitem__", &map_item<hello::StrStrMap>::del)
.def("__contains__", &map_item<hello::StrStrMap>::in)
.def("__index", &map_item<hello::StrStrMap>::index)
.def("keys", &map_item<hello::StrStrMap>::keys)
.def("values", &map_item<hello::StrStrMap>::values)
.def("items", &map_item<hello::StrStrMap>::items)
;
*/
PyExposeStruct(Shape);
PyExposeVector(Geometry);
PyExposeMap(Layer);
PyExposeMap(hello::StrStrMap);
class_<hello>("hello", init<std::string>())
// Add a regular member function.
.def("greet", &hello::greet)
// Add invite() as a member of hello!
.def("invite", invite)
.def("getMap", &hello::getMap, return_value_policy<copy_non_const_reference>())
.def("setMap", &hello::setMap2)
.def("debugMap", &hello::debugMap)
;
// Also add invite() as a regular function to the module.
def("invite", invite);
}
}
BEGIN_NAMESPACE
static int numargs = 0;
long rawFunctionTest(int a, char *buf, int c)
{
return a + 10 + c; // 16
}
template<typename T>
const char* rawTypeName(T const& t)
{
return typeid(T).raw_name();
}
template<typename T>
const char* typeName(T const& t)
{
return typeid(T).name();
/*
std::cout
<< pVarName
<< ": type: " <<
<< ", raw type: " << typeid(t).raw_name()
<< std::endl;
*/
}
// Test for embedding a method in python
/* Return the number of arguments of the application command line */
static PyObject* emb_numargs(PyObject *self, PyObject *args)
{
int a = 0, c = 0;
char *buf;
// These 4 ways work, even with RTTI disabled!!!
const char* signature1 = typeName(rawFunctionTest);
const char* signature2 = rawTypeName(rawFunctionTest);
const char* signature3 = typeid(rawFunctionTest).name();
const char* signature4 = typeid(rawFunctionTest).raw_name();
//typedef std::type_info const* base_id_t;
//base_id_t m_base_type;
//std::type_info::name(rawFunctionTest);
if (!PyArg_ParseTuple(args, "i|s|i:numargs", &a, &buf, &c))
return NULL;
return PyLong_FromLong(numargs);
}
template<typename T>
static PyObject* pyCallWrapper(PyObject *self, PyObject *args)
{
int a = 0, c = 0;
char *buf;
const char* signature = typeid(rawFunctionTest).name();
//typedef std::type_info const* base_id_t;
//base_id_t m_base_type;
//std::type_info::name(rawFunctionTest);
if (!PyArg_ParseTuple(args, "i|s|i:numargs", &a, &buf, &c))
return NULL;
return PyLong_FromLong(numargs);
}
#define PREPROCESSOR_CONCAT(a, b) a ## b
#define DECLARE_PYTHON_FUNCTION3(name, argTypes, argT0, argT1, argT2) \
static PyObject* PREPROCESSOR_CONCAT(python_embedded_, name)(PyObject *self, PyObject *pyArgs) \
{ \
void* args[5]; \
if (!PyArg_ParseTuple(pyArgs, argTypes ":" #name, &args[0], &args[1], &args[2], &args[3], &args[4], &args[5])) \
return NULL; \
return PyLong_FromLong(name((argT0)args[0], (argT1)args[1], (argT2)args[2])); \
}
DECLARE_PYTHON_FUNCTION3(rawFunctionTest, "i|s|i", int, char*, int)
template<typename T>
std::string getArgTypesFromFunc()
{
std::string s = typeid(T).name();
// TODO: some processing
return s;
}
/*
template<typename retT, typename func, typename argT0, typename argT1, typename argT2>
static PyObject* python_embedded_generic_3args(PyObject *self, PyObject *pyArgs)
{
void* args[3];
std::string argTypes = getArgTypesFromFunc<func>() + ":";
if (!PyArg_ParseTuple(pyArgs, argTypes + #name, &args[0], &args[1], &args[2]))
return NULL;
return PyLong_FromLong(name((argT0)args[0], (argT1)args[1], (argT2)args[2]));
}
*/
class Python_Method_Collection
{
std::vector<PyMethodDef> m_methods;
public:
void addMethod(const char* a_funcName, PyCFunction a_funcPtr, const char* a_desc)
{
PyMethodDef newMethod = { a_funcName, a_funcPtr, METH_VARARGS, a_desc };
m_methods.push_back(newMethod);
}
PyMethodDef* finalize()
{
PyMethodDef nullMethod = { NULL, NULL, 0, NULL };
m_methods.push_back(nullMethod);
return m_methods.data();
}
};
/*
class Python_Module_Base
{
void preInit();
static PyObject* Create(const char* a_moduleName, Python_Method_Collection& a_methods)
{
static PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT, a_moduleName, NULL, -1, a_methods.finalize(),
NULL, NULL, NULL, NULL
};
return PyModule_Create(&moduledef);
}
public:
Python_Module_Base(const char* a_moduleName)
{
preInit();
PyImport_AppendInittab(a_moduleName, &Python_Module_Base::Create);
}
};
Python_Module_Base s_run_once_init_module_name;
void Python_Module_Base::preInit()
*/
#define PYTHON_MODULE(name) \
class PREPROCESSOR_CONCAT(python_module_, name) { \
static Python_Method_Collection s_methods; \
static void preInit(); \
static PyObject* init() { \
static PyModuleDef moduledef = { \
PyModuleDef_HEAD_INIT, #name, NULL, -1, s_methods.finalize(), \
NULL, NULL, NULL, NULL \
}; \
return PyModule_Create(&moduledef); \
} \
static void addMethod(const char* a_funcName, PyCFunction a_funcPtr, const char* a_desc) { \
s_methods.addMethod(a_funcName, a_funcPtr, a_desc); \
} \
public: \
PREPROCESSOR_CONCAT(python_module_, name)() { \
preInit();\
PyImport_AppendInittab(#name, &PREPROCESSOR_CONCAT(python_module_, name)::init); \
} \
}; \
Python_Method_Collection PREPROCESSOR_CONCAT(python_module_, name)::s_methods; \
PREPROCESSOR_CONCAT(python_module_, name) PREPROCESSOR_CONCAT(g_python_module_, name); \
void PREPROCESSOR_CONCAT(python_module_, name)::preInit()
PYTHON_MODULE(emb)
{
addMethod("numargs", emb_numargs, "Return the number of arguments received by the process.");
std::string functionSig = typeid(rawFunctionTest).name();
std::string classSig = typeid(hello).name();
//std::string classSig2 = typeid(hello::greet).name();
std::string classSig3 = typeid(&hello::greet).name();
/*
class_<hello>("hello", init<std::string>())
// Add a regular member function.
.def("greet", &hello::greet)
// Add invite() as a member of hello!
.def("invite", invite)
;
// Also add invite() as a regular function to the module.
def("invite", invite);
*/
}
/*
static PyMethodDef EmbMethods[] = {
{"numargs", emb_numargs, METH_VARARGS, "Return the number of arguments received by the process."},
{NULL, NULL, 0, NULL}
};
static PyModuleDef EmbModule = {
PyModuleDef_HEAD_INIT, "emb", NULL, -1, EmbMethods,
NULL, NULL, NULL, NULL
};
static PyObject* PyInit_emb(void)
{
return PyModule_Create(&EmbModule);
}
*/
void PythonTests()
{
numargs = 2;
PyImport_AppendInittab("hello_ext", &PyInit_hello_ext);
Py_SetProgramName(L"Blah Prog Name"); /* optional but recommended */
//Py_Main(0,0);
Py_Initialize();
MemoryMapping mmf("test.py");
char* text = (char*)mmf.address();
// Here we make a copy of the contents just so we can append a null at the end to be safe
std::vector<char> buf;
buf.reserve((size_t)mmf.size() + 1);
memcpy(buf.data(), mmf.address(), (size_t)mmf.size());
buf.data()[mmf.size()] = '\0';
text = buf.data();
PyRun_SimpleString(text);
/*
"import emb\n"
"test = 5\n"
"import hello_ext\n"
"obj = hello_ext.hello(\"me\")\n"
"print(obj.greet())\n"
"print(hello_ext.invite(obj))\n"
"print(obj.debugMap())\n"
"print(\"Number of arguments\", emb.numargs(1, \"blah\", test))\n"
"from time import time,ctime\n"
"print('Today is', ctime(time()))\n");
*/
Py_Finalize();
getchar();
//exit(-1);
}
END_NAMESPACE