diff options
author | John Wiegley <johnw@newartisans.com> | 2007-05-04 11:41:58 +0000 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2008-04-13 03:38:38 -0400 |
commit | 96684b72ca367bfd4dbe2e45a9a66c56204eb533 (patch) | |
tree | 3e218d1048d5941baa1d1064bc2539ef2df55a45 | |
parent | 93096b77f3c03b826c8857e4817ccd1bca52f9ee (diff) | |
download | fork-ledger-96684b72ca367bfd4dbe2e45a9a66c56204eb533.tar.gz fork-ledger-96684b72ca367bfd4dbe2e45a9a66c56204eb533.tar.bz2 fork-ledger-96684b72ca367bfd4dbe2e45a9a66c56204eb533.zip |
Added code for converting ledger::string and boost::date_time to their
respective Python counterparts.
-rw-r--r-- | Makefile.am | 11 | ||||
-rw-r--r-- | Makefile.in | 65 | ||||
-rwxr-xr-x | acprep | 16 | ||||
-rw-r--r-- | src/TODO | 3 | ||||
-rw-r--r-- | src/py_times.cc | 99 | ||||
-rw-r--r-- | src/py_utils.cc | 46 | ||||
-rw-r--r-- | src/pyledger.cc | 4 | ||||
-rw-r--r-- | src/pyutils.h | 22 | ||||
-rw-r--r-- | src/tuples.hpp | 250 | ||||
-rw-r--r-- | src/utils.cc | 3 | ||||
-rw-r--r-- | src/utils.h | 10 |
11 files changed, 504 insertions, 25 deletions
diff --git a/Makefile.am b/Makefile.am index 46d27666..28165969 100644 --- a/Makefile.am +++ b/Makefile.am @@ -95,6 +95,8 @@ libpyledger_la_CPPFLAGS = $(libledger_la_CPPFLAGS) libpyledger_la_LDFLAGS = -release 3.0 libpyledger_la_SOURCES = \ + src/py_utils.cc \ + src/py_times.cc \ src/py_amount.cc @@ -168,7 +170,11 @@ CLEANFILES += ledger.so clean-local: rm -fr build -ledger_so_SOURCES = src/pyledger.cc src/py_amount.cc +ledger_so_SOURCES = \ + src/pyledger.cc \ + src/py_utils.cc \ + src/py_times.cc \ + src/py_amount.cc ledger_so_DEPENDENCIES = libledger.la gdtoa/libgdtoa.la libpyledger.la PYLIBS = pyledger ledger gdtoa gmp boost_date_time \ @@ -184,9 +190,6 @@ if HAVE_LIBOFX PYLIBS += ofx endif -PYLEDGER_SRC = src/pyledger.cc \ - src/py_amount.cc - ledger.so: $(ledger_so_SOURCES) $(ledger_so_DEPENDENCIES) CFLAGS="$(CPPFLAGS) -I$(srcdir) $(libledger_la_CPPFLAGS)" \ LDFLAGS="$(LDFLAGS) -L. -L.libs -Lgdtoa -Lgdtoa/.libs" \ diff --git a/Makefile.in b/Makefile.in index 8175b693..54d0d2ad 100644 --- a/Makefile.in +++ b/Makefile.in @@ -118,7 +118,8 @@ libledger_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(libledger_la_CXXFLAGS) \ $(CXXFLAGS) $(libledger_la_LDFLAGS) $(LDFLAGS) -o $@ libpyledger_la_LIBADD = -am_libpyledger_la_OBJECTS = libpyledger_la-py_amount.lo +am_libpyledger_la_OBJECTS = libpyledger_la-py_utils.lo \ + libpyledger_la-py_times.lo libpyledger_la-py_amount.lo libpyledger_la_OBJECTS = $(am_libpyledger_la_OBJECTS) libpyledger_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ @@ -147,8 +148,10 @@ ledger_DEPENDENCIES = $(LIBOBJS) libledger.la gdtoa/libgdtoa.la \ ledger_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CXXLD) $(ledger_CXXFLAGS) $(CXXFLAGS) \ $(ledger_LDFLAGS) $(LDFLAGS) -o $@ -am__ledger_so_SOURCES_DIST = src/pyledger.cc src/py_amount.cc +am__ledger_so_SOURCES_DIST = src/pyledger.cc src/py_utils.cc \ + src/py_times.cc src/py_amount.cc @HAVE_BOOST_PYTHON_TRUE@am_ledger_so_OBJECTS = pyledger.$(OBJEXT) \ +@HAVE_BOOST_PYTHON_TRUE@ py_utils.$(OBJEXT) py_times.$(OBJEXT) \ @HAVE_BOOST_PYTHON_TRUE@ py_amount.$(OBJEXT) ledger_so_OBJECTS = $(am_ledger_so_OBJECTS) ledger_so_LDADD = $(LDADD) @@ -374,6 +377,8 @@ libledger_la_SOURCES = src/session.cc src/journal.cc src/amount.cc \ libpyledger_la_CPPFLAGS = $(libledger_la_CPPFLAGS) libpyledger_la_LDFLAGS = -release 3.0 libpyledger_la_SOURCES = \ + src/py_utils.cc \ + src/py_times.cc \ src/py_amount.cc pkginclude_HEADERS = \ @@ -425,16 +430,18 @@ info_TEXINFOS = docs/ledger.texi ###################################################################### dist_lisp_LISP = lisp/ledger.el lisp/timeclock.el -@HAVE_BOOST_PYTHON_TRUE@ledger_so_SOURCES = src/pyledger.cc src/py_amount.cc +@HAVE_BOOST_PYTHON_TRUE@ledger_so_SOURCES = \ +@HAVE_BOOST_PYTHON_TRUE@ src/pyledger.cc \ +@HAVE_BOOST_PYTHON_TRUE@ src/py_utils.cc \ +@HAVE_BOOST_PYTHON_TRUE@ src/py_times.cc \ +@HAVE_BOOST_PYTHON_TRUE@ src/py_amount.cc + @HAVE_BOOST_PYTHON_TRUE@ledger_so_DEPENDENCIES = libledger.la gdtoa/libgdtoa.la libpyledger.la @HAVE_BOOST_PYTHON_TRUE@PYLIBS = pyledger ledger gdtoa gmp \ @HAVE_BOOST_PYTHON_TRUE@ boost_date_time boost_signals \ @HAVE_BOOST_PYTHON_TRUE@ boost_filesystem boost_regex \ @HAVE_BOOST_PYTHON_TRUE@ boost_python $(am__append_15) \ @HAVE_BOOST_PYTHON_TRUE@ $(am__append_16) $(am__append_17) -@HAVE_BOOST_PYTHON_TRUE@PYLEDGER_SRC = src/pyledger.cc \ -@HAVE_BOOST_PYTHON_TRUE@ src/py_amount.cc - nodist_UnitTests_SOURCES = tests/UnitTests.cc \ \ tests/numerics/BasicAmount.cc \ @@ -628,7 +635,11 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libledger_la-xmlparse.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libledger_la-xpath.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpyledger_la-py_amount.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpyledger_la-py_times.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpyledger_la-py_utils.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/py_amount.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/py_times.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/py_utils.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pyledger.Po@am__quote@ .cc.o: @@ -841,6 +852,20 @@ libledger_la-pyinterp.lo: src/pyinterp.cc @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libledger_la_CPPFLAGS) $(CPPFLAGS) $(libledger_la_CXXFLAGS) $(CXXFLAGS) -c -o libledger_la-pyinterp.lo `test -f 'src/pyinterp.cc' || echo '$(srcdir)/'`src/pyinterp.cc +libpyledger_la-py_utils.lo: src/py_utils.cc +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpyledger_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libpyledger_la-py_utils.lo -MD -MP -MF $(DEPDIR)/libpyledger_la-py_utils.Tpo -c -o libpyledger_la-py_utils.lo `test -f 'src/py_utils.cc' || echo '$(srcdir)/'`src/py_utils.cc +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libpyledger_la-py_utils.Tpo $(DEPDIR)/libpyledger_la-py_utils.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/py_utils.cc' object='libpyledger_la-py_utils.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpyledger_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libpyledger_la-py_utils.lo `test -f 'src/py_utils.cc' || echo '$(srcdir)/'`src/py_utils.cc + +libpyledger_la-py_times.lo: src/py_times.cc +@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpyledger_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libpyledger_la-py_times.lo -MD -MP -MF $(DEPDIR)/libpyledger_la-py_times.Tpo -c -o libpyledger_la-py_times.lo `test -f 'src/py_times.cc' || echo '$(srcdir)/'`src/py_times.cc +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libpyledger_la-py_times.Tpo $(DEPDIR)/libpyledger_la-py_times.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/py_times.cc' object='libpyledger_la-py_times.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpyledger_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libpyledger_la-py_times.lo `test -f 'src/py_times.cc' || echo '$(srcdir)/'`src/py_times.cc + libpyledger_la-py_amount.lo: src/py_amount.cc @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpyledger_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libpyledger_la-py_amount.lo -MD -MP -MF $(DEPDIR)/libpyledger_la-py_amount.Tpo -c -o libpyledger_la-py_amount.lo `test -f 'src/py_amount.cc' || echo '$(srcdir)/'`src/py_amount.cc @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libpyledger_la-py_amount.Tpo $(DEPDIR)/libpyledger_la-py_amount.Plo @@ -960,6 +985,34 @@ pyledger.obj: src/pyledger.cc @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o pyledger.obj `if test -f 'src/pyledger.cc'; then $(CYGPATH_W) 'src/pyledger.cc'; else $(CYGPATH_W) '$(srcdir)/src/pyledger.cc'; fi` +py_utils.o: src/py_utils.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT py_utils.o -MD -MP -MF $(DEPDIR)/py_utils.Tpo -c -o py_utils.o `test -f 'src/py_utils.cc' || echo '$(srcdir)/'`src/py_utils.cc +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/py_utils.Tpo $(DEPDIR)/py_utils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/py_utils.cc' object='py_utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o py_utils.o `test -f 'src/py_utils.cc' || echo '$(srcdir)/'`src/py_utils.cc + +py_utils.obj: src/py_utils.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT py_utils.obj -MD -MP -MF $(DEPDIR)/py_utils.Tpo -c -o py_utils.obj `if test -f 'src/py_utils.cc'; then $(CYGPATH_W) 'src/py_utils.cc'; else $(CYGPATH_W) '$(srcdir)/src/py_utils.cc'; fi` +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/py_utils.Tpo $(DEPDIR)/py_utils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/py_utils.cc' object='py_utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o py_utils.obj `if test -f 'src/py_utils.cc'; then $(CYGPATH_W) 'src/py_utils.cc'; else $(CYGPATH_W) '$(srcdir)/src/py_utils.cc'; fi` + +py_times.o: src/py_times.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT py_times.o -MD -MP -MF $(DEPDIR)/py_times.Tpo -c -o py_times.o `test -f 'src/py_times.cc' || echo '$(srcdir)/'`src/py_times.cc +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/py_times.Tpo $(DEPDIR)/py_times.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/py_times.cc' object='py_times.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o py_times.o `test -f 'src/py_times.cc' || echo '$(srcdir)/'`src/py_times.cc + +py_times.obj: src/py_times.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT py_times.obj -MD -MP -MF $(DEPDIR)/py_times.Tpo -c -o py_times.obj `if test -f 'src/py_times.cc'; then $(CYGPATH_W) 'src/py_times.cc'; else $(CYGPATH_W) '$(srcdir)/src/py_times.cc'; fi` +@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/py_times.Tpo $(DEPDIR)/py_times.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/py_times.cc' object='py_times.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o py_times.obj `if test -f 'src/py_times.cc'; then $(CYGPATH_W) 'src/py_times.cc'; else $(CYGPATH_W) '$(srcdir)/src/py_times.cc'; fi` + py_amount.o: src/py_amount.cc @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT py_amount.o -MD -MP -MF $(DEPDIR)/py_amount.Tpo -c -o py_amount.o `test -f 'src/py_amount.cc' || echo '$(srcdir)/'`src/py_amount.cc @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/py_amount.Tpo $(DEPDIR)/py_amount.Po @@ -57,10 +57,12 @@ while [ -n "$1" ]; do case "$1" in --debug) SWITCHES="$SWITCHES --enable-debug" - if [ -f /usr/local/lib/libstlportstlg.a ]; then - CPPFLAGS="-I/usr/local/include/stlport -D_STLP_DEBUG $CPPFLAGS" - #LDFLAGS="-lstlportstlg $LDFLAGS" - fi + #if [ -f /usr/local/lib/libstlportstlg.a ]; then + # SWITCHES="$SWITCHES --enable-stlportg" + # CPPFLAGS="-D_STLP_DEBUG $CPPFLAGS" + # CPPFLAGS="-I/usr/local/include/stlport $CPPFLAGS" + # LIBS="$LIBS -lstlportstlg" + #fi CXXFLAGS="$CXXFLAGS -g" ;; --prof | --perf) @@ -69,8 +71,8 @@ while [ -n "$1" ]; do --python) if [ -d "$PYTHON_HOME" ]; then SWITCHES="$SWITCHES --enable-python" - INCDIRS="$INCDIRS -I$PYTHON_HOME/include/python2.5" - LIBDIRS="$LIBDIRS -L$PYTHON_HOME/lib/python2.5/config" + CPPFLAGS="$CPPFLAGS -I$PYTHON_HOME/include/python2.5" + LDFLAGS="$LDFLAGS -L$PYTHON_HOME/lib/python2.5/config" fi ;; --opt) @@ -103,4 +105,4 @@ fi "$HERE/configure" --srcdir="$HERE" \ CPPFLAGS="$CPPFLAGS" CXXFLAGS="$CXXFLAGS $local_cxxflags" \ - LDFLAGS="$LDFLAGS" $SWITCHES "$@" + LDFLAGS="$LDFLAGS" LIBS="$LIBS" $SWITCHES "$@" diff --git a/src/TODO b/src/TODO new file mode 100644 index 00000000..5ad232dd --- /dev/null +++ b/src/TODO @@ -0,0 +1,3 @@ +- Add tracing code for functions that records call count and total + time spent, as well as average time per call. This would implement + selective profiling. diff --git a/src/py_times.cc b/src/py_times.cc new file mode 100644 index 00000000..578d887b --- /dev/null +++ b/src/py_times.cc @@ -0,0 +1,99 @@ +#include "pyinterp.h" +#include "pyutils.h" + +#include <boost/cast.hpp> +#include <boost/python/module.hpp> +#include <boost/python/def.hpp> +#include <boost/python/to_python_converter.hpp> + +#include <Python.h> +#include <datetime.h> + +namespace ledger { + +using namespace boost::python; + +typedef boost::gregorian::date date; + +struct date_to_python +{ + static PyObject* convert(const date& dte) + { + PyDateTime_IMPORT; + return PyDate_FromDate(dte.year(), dte.month(), dte.day()); + } +}; + +struct date_from_python +{ + static void* convertible(PyObject* obj_ptr) + { + PyDateTime_IMPORT; + if(PyDate_Check(obj_ptr) || PyDateTime_Check(obj_ptr)) return obj_ptr; + return 0; + } + + static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data) + { + PyDateTime_IMPORT; + int y = PyDateTime_GET_YEAR(obj_ptr); + int m = PyDateTime_GET_MONTH(obj_ptr); + int d = PyDateTime_GET_DAY(obj_ptr); + date* dte = new date(y,m,d); + data->convertible = (void*)dte; + } +}; + +typedef register_python_conversion<date, date_to_python, date_from_python> + date_python_conversion; + + +typedef boost::posix_time::ptime datetime; + +struct datetime_to_python +{ + static PyObject* convert(const datetime& moment) + { + PyDateTime_IMPORT; + date dte = moment.date(); + datetime::time_duration_type tod = moment.time_of_day(); + return PyDateTime_FromDateAndTime(dte.year(), dte.month(), dte.day(), + tod.hours(), tod.minutes(), tod.seconds(), + tod.total_microseconds() % 1000000); + } +}; + +struct datetime_from_python +{ + static void* convertible(PyObject* obj_ptr) + { + PyDateTime_IMPORT; + if(PyDateTime_Check(obj_ptr)) return obj_ptr; + return 0; + } + + static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data) + { + PyDateTime_IMPORT; + int y = PyDateTime_GET_YEAR(obj_ptr); + int m = PyDateTime_GET_MONTH(obj_ptr); + int d = PyDateTime_GET_DAY(obj_ptr); + int h = PyDateTime_DATE_GET_HOUR(obj_ptr); + int min = PyDateTime_DATE_GET_MINUTE(obj_ptr); + int s = PyDateTime_DATE_GET_SECOND(obj_ptr); + datetime* moment = new datetime(date(y,m,d), + datetime::time_duration_type(h, min, s)); + data->convertible = (void*)moment; + } +}; + +typedef register_python_conversion<datetime, datetime_to_python, datetime_from_python> + datetime_python_conversion; + +void export_times() +{ + date_python_conversion(); + datetime_python_conversion(); +} + +} // namespace ledger diff --git a/src/py_utils.cc b/src/py_utils.cc new file mode 100644 index 00000000..0f82d683 --- /dev/null +++ b/src/py_utils.cc @@ -0,0 +1,46 @@ +#include "pyinterp.h" +#include "pyutils.h" + +#include <boost/python/module.hpp> +#include <boost/python/def.hpp> +#include <boost/python/to_python_converter.hpp> + +namespace ledger { + +using namespace boost::python; + +struct string_to_python +{ + static PyObject* convert(const string& str) + { + return incref(object(*boost::polymorphic_downcast<const std::string *>(&str)).ptr()); + } +}; + +struct string_from_python +{ + static void* convertible(PyObject* obj_ptr) + { + if (!PyString_Check(obj_ptr)) return 0; + return obj_ptr; + } + + static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data) + { + const char* value = PyString_AsString(obj_ptr); + if (value == 0) throw_error_already_set(); + void* storage = ((converter::rvalue_from_python_storage<string>*) data)->storage.bytes; + new (storage) string(value); + data->convertible = storage; + } +}; + +typedef register_python_conversion<string, string_to_python, string_from_python> + string_python_conversion; + +void export_utils() +{ + string_python_conversion(); +} + +} // namespace ledger diff --git a/src/pyledger.cc b/src/pyledger.cc index 1fe5708a..013b445d 100644 --- a/src/pyledger.cc +++ b/src/pyledger.cc @@ -4,6 +4,8 @@ using namespace boost::python; namespace ledger { +void export_utils(); +void export_times(); void export_amount(); #if 0 void export_balance(); @@ -19,6 +21,8 @@ void export_valexpr(); void initialize_for_python() { + export_utils(); + export_times(); export_amount(); #if 0 export_balance(); diff --git a/src/pyutils.h b/src/pyutils.h new file mode 100644 index 00000000..4ff33f8f --- /dev/null +++ b/src/pyutils.h @@ -0,0 +1,22 @@ +#ifndef _PY_UTILS_H +#define _PY_UTILS_H + +template<class T, class TfromPy> +struct ObjFromPy { + ObjFromPy() { + boost::python::converter::registry::push_back + (&TfromPy::convertible, + &TfromPy::construct, + boost::python::type_id<T>()); + } +}; + +template<class T, class TtoPy, class TfromPy> +struct register_python_conversion { + register_python_conversion() { + boost::python::to_python_converter<T, TtoPy>(); + ObjFromPy<T, TfromPy>(); + } +}; + +#endif // _PY_UTILS_H diff --git a/src/tuples.hpp b/src/tuples.hpp new file mode 100644 index 00000000..8ec7fdc9 --- /dev/null +++ b/src/tuples.hpp @@ -0,0 +1,250 @@ +// Copyright 2004-2007 Roman Yakovenko. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef TUPLES_HPP_16_JAN_2007 +#define TUPLES_HPP_16_JAN_2007 + +#include "boost/python.hpp" +#include "boost/tuple/tuple.hpp" +#include "boost/python/object.hpp" //len function +#include <boost/mpl/int.hpp> +#include <boost/mpl/next.hpp> + +/** + * Converts boost::tuples::tuple<...> to\from Python tuple + * + * The conversion is done "on-the-fly", you should only register the conversion + * with your tuple classes. + * For example: + * + * typedef boost::tuples::tuple< int, double, std::string > triplet; + * boost::python::register_tuple< triplet >(); + * + * That's all. After this point conversion to\from next types will be handled + * by Boost.Python library: + * + * triplet + * triplet& ( return type only ) + * const triplet + * const triplet& + * + * Implementation description. + * The conversion uses Boost.Python custom r-value converters. r-value converters + * is very powerful and undocumented feature of the library. The only documentation + * we have is http://boost.org/libs/python/doc/v2/faq.html#custom_string . + * + * The conversion consists from two parts: "to" and "from". + * + * "To" conversion + * The "to" part is pretty easy and well documented ( http://docs.python.org/api/api.html ). + * You should use Python C API to create an instance of a class and than you + * initialize the relevant members of the instance. + * + * "From" conversion + * Lets start from analyzing one of the use case Boost.Python library have to + * deal with: + * + * void do_smth( const triplet& arg ){...} + * + * In order to allow calling this function from Python, the library should keep + * parameter "arg" alive until the function returns. In other words, the library + * should provide instances life-time management. The provided interface is not + * ideal and could be improved. You have to implement two functions: + * + * void* convertible( PyObject* obj ) + * Checks whether the "obj" could be converted to an instance of the desired + * class. If true, the function should return "obj", otherwise NULL + * + * void construct( PyObject* obj, converter::rvalue_from_python_stage1_data* data) + * Constructs the instance of the desired class. This function will be called + * if and only if "convertible" function returned true. The first argument + * is Python object, which was passed as parameter to "convertible" function. + * The second object is some kind of memory allocator for one object. Basically + * it keeps a memory chunk. You will use the memory for object allocation. + * + * For some unclear for me reason, the library implements "C style Inheritance" + * ( http://www.embedded.com/97/fe29712.htm ). So, in order to create new + * object in the storage you have to cast to the "right" class: + * + * typedef converter::rvalue_from_python_storage<your_type_t> storage_t; + * storage_t* the_storage = reinterpret_cast<storage_t*>( data ); + * void* memory_chunk = the_storage->storage.bytes; + * + * "memory_chunk" points to the memory, where the instance will be allocated. + * + * In order to create object at specific location, you should use placement new + * operator: + * + * your_type_t* instance = new (memory_chunk) your_type_t(); + * + * Now, you can continue to initialize the instance. + * + * instance->set_xyz = read xyz from obj + * + * If "your_type_t" constructor requires some arguments, "read" the Python + * object before you call the constructor: + * + * xyz_type xyz = read xyz from obj + * your_type_t* instance = new (memory_chunk) your_type_t(xyz); + * + * Hint: + * In most case you don't really need\have to work with C Python API. Let + * Boost.Python library to do some work for you! + * + **/ + +namespace boost{ namespace python{ + +namespace details{ + +//Small helper function, introduced to allow short syntax for index incrementing +template< int index> +typename mpl::next< mpl::int_< index > >::type increment_index(){ + typedef typename mpl::next< mpl::int_< index > >::type next_index_type; + return next_index_type(); +} + +} + +template< class TTuple > +struct to_py_tuple{ + + typedef mpl::int_< tuples::length< TTuple >::value > length_type; + + static PyObject* convert(const TTuple& c_tuple){ + list values; + //add all c_tuple items to "values" list + convert_impl( c_tuple, values, mpl::int_< 0 >(), length_type() ); + //create Python tuple from the list + return incref( python::tuple( values ).ptr() ); + } + +private: + + template< int index, int length > + static void + convert_impl( const TTuple &c_tuple, list& values, mpl::int_< index >, mpl::int_< length > ) { + values.append( c_tuple.template get< index >() ); + convert_impl( c_tuple, values, details::increment_index<index>(), length_type() ); + } + + template< int length > + static void + convert_impl( const TTuple&, list& values, mpl::int_< length >, mpl::int_< length >) + {} + +}; + + +template< class TTuple> +struct from_py_sequence{ + + typedef TTuple tuple_type; + + typedef mpl::int_< tuples::length< TTuple >::value > length_type; + + static void* + convertible(PyObject* py_obj){ + + if( !PySequence_Check( py_obj ) ){ + return 0; + } + + if( !PyObject_HasAttrString( py_obj, "__len__" ) ){ + return 0; + } + + python::object py_sequence( handle<>( borrowed( py_obj ) ) ); + + if( tuples::length< TTuple >::value != len( py_sequence ) ){ + return 0; + } + + if( convertible_impl( py_sequence, mpl::int_< 0 >(), length_type() ) ){ + return py_obj; + } + else{ + return 0; + } + } + + static void + construct( PyObject* py_obj, converter::rvalue_from_python_stage1_data* data){ + typedef converter::rvalue_from_python_storage<TTuple> storage_t; + storage_t* the_storage = reinterpret_cast<storage_t*>( data ); + void* memory_chunk = the_storage->storage.bytes; + TTuple* c_tuple = new (memory_chunk) TTuple(); + data->convertible = memory_chunk; + + python::object py_sequence( handle<>( borrowed( py_obj ) ) ); + construct_impl( py_sequence, *c_tuple, mpl::int_< 0 >(), length_type() ); + } + + static TTuple to_c_tuple( PyObject* py_obj ){ + if( !convertible( py_obj ) ){ + throw std::runtime_error( "Unable to construct boost::tuples::tuple from Python object!" ); + } + TTuple c_tuple; + python::object py_sequence( handle<>( borrowed( py_obj ) ) ); + construct_impl( py_sequence, c_tuple, mpl::int_< 0 >(), length_type() ); + return c_tuple; + } + +private: + + template< int index, int length > + static bool + convertible_impl( const python::object& py_sequence, mpl::int_< index >, mpl::int_< length > ){ + + typedef typename tuples::element< index, TTuple>::type element_type; + + object element = py_sequence[index]; + extract<element_type> type_checker( element ); + if( !type_checker.check() ){ + return false; + } + else{ + return convertible_impl( py_sequence, details::increment_index<index>(), length_type() ); + } + } + + template< int length > + static bool + convertible_impl( const python::object& py_sequence, mpl::int_< length >, mpl::int_< length > ){ + return true; + } + + template< int index, int length > + static void + construct_impl( const python::object& py_sequence, TTuple& c_tuple, mpl::int_< index >, mpl::int_< length > ){ + + typedef typename tuples::element< index, TTuple>::type element_type; + + object element = py_sequence[index]; + c_tuple.template get< index >() = extract<element_type>( element ); + + construct_impl( py_sequence, c_tuple, details::increment_index<index>(), length_type() ); + } + + template< int length > + static void + construct_impl( const python::object& py_sequence, TTuple& c_tuple, mpl::int_< length >, mpl::int_< length > ) + {} + +}; + +template< class TTuple> +void register_tuple(){ + + to_python_converter< TTuple, to_py_tuple<TTuple> >(); + + converter::registry::push_back( &from_py_sequence<TTuple>::convertible + , &from_py_sequence<TTuple>::construct + , type_id<TTuple>() ); +}; + +} } //boost::python + +#endif//TUPLES_HPP_16_JAN_2007 diff --git a/src/utils.cc b/src/utils.cc index 59028141..f6d95e36 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -353,7 +353,6 @@ void report_memory(std::ostream& out, bool report_all) } } -#if ! defined(USE_BOOST_PYTHON) string::string() : std::string() { TRACE_CTOR(string, ""); @@ -389,8 +388,6 @@ string::~string() { TRACE_DTOR(string); } -#endif - } // namespace ledger #endif // VERIFY_ON diff --git a/src/utils.h b/src/utils.h index bde23b2b..ddc6de85 100644 --- a/src/utils.h +++ b/src/utils.h @@ -36,7 +36,7 @@ namespace ledger { using namespace boost; -#if defined(VERIFY_ON) && ! defined(USE_BOOST_PYTHON) +#if defined(VERIFY_ON) class string; #else typedef std::string string; @@ -115,8 +115,10 @@ void trace_dtor_func(void * ptr, const char * cls_name, std::size_t cls_size); void report_memory(std::ostream& out, bool report_all = false); -#if ! defined(USE_BOOST_PYTHON) - +/** + * This string type is a wrapper around std::string that allows us to + * trace constructor and destructor calls. + */ class string : public std::string { public: @@ -177,8 +179,6 @@ inline bool operator!=(const char* __lhs, const string& __rhs) inline bool operator!=(const string& __lhs, const char* __rhs) { return __lhs.compare(__rhs) != 0; } -#endif - } // namespace ledger #else // ! VERIFY_ON |