diff --git a/build/bpl_static.dsp b/build/bpl_static.dsp new file mode 100644 index 00000000..ca70236d --- /dev/null +++ b/build/bpl_static.dsp @@ -0,0 +1,241 @@ +# Microsoft Developer Studio Project File - Name="bpl_static" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=bpl_static - Win32 DebugPython +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "bpl_static.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "bpl_static.mak" CFG="bpl_static - Win32 DebugPython" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "bpl_static - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "bpl_static - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE "bpl_static - Win32 DebugPython" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "bpl_static - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MD /W4 /WX /GR /GX /O2 /I "..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "bpl_static - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W4 /WX /Gm- /GR /GX /Zi /Od /I "..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "bpl_static - Win32 DebugPython" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "bpl_static___Win32_DebugPython" +# PROP BASE Intermediate_Dir "bpl_static___Win32_DebugPython" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "DebugPython" +# PROP Intermediate_Dir "DebugPython" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W4 /WX /Gm /GR /GX /Zi /Od /I "..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W4 /WX /Gm- /GR /GX /Zi /Od /I "..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "BOOST_DEBUG_PYTHON" /FR /YX /FD /GZ /EHs /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "bpl_static - Win32 Release" +# Name "bpl_static - Win32 Debug" +# Name "bpl_static - Win32 DebugPython" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\classes.cpp +# ADD CPP /W3 +# End Source File +# Begin Source File + +SOURCE=..\src\conversions.cpp +# ADD CPP /W3 +# End Source File +# Begin Source File + +SOURCE=..\src\extension_class.cpp +# ADD CPP /W3 +# End Source File +# Begin Source File + +SOURCE=..\src\functions.cpp +# ADD CPP /W3 +# End Source File +# Begin Source File + +SOURCE=..\src\init_function.cpp +# ADD CPP /W3 +# End Source File +# Begin Source File + +SOURCE=..\src\module_builder.cpp +# ADD CPP /W3 +# End Source File +# Begin Source File + +SOURCE=..\src\objects.cpp +# ADD CPP /W3 +# End Source File +# Begin Source File + +SOURCE=..\src\types.cpp +# ADD CPP /W3 +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\..\boost\python\detail\base_object.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\callback.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\caller.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\detail\cast.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\class_builder.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\classes.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\detail\config.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\conversions.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\errors.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\detail\extension_class.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\detail\functions.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\detail\init_function.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\module_builder.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\detail\none.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\objects.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\operators.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\reference.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\detail\signatures.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\detail\singleton.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\detail\types.hpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\boost\python\detail\wrap_python.hpp +# End Source File +# End Group +# End Target +# End Project diff --git a/build/build.dsw b/build/build.dsw new file mode 100644 index 00000000..8de38493 --- /dev/null +++ b/build/build.dsw @@ -0,0 +1,108 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "bpl_static"=.\bpl_static.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "example1"=.\example1\example1.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name bpl_static + End Project Dependency +}}} + +############################################################################### + +Project: "getting_started1"=.\getting_started1\getting_started1.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name bpl_static + End Project Dependency +}}} + +############################################################################### + +Project: "getting_started2"=.\getting_started2\getting_started2.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + getting_started2 + .\getting_started2 + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name bpl_static + End Project Dependency +}}} + +############################################################################### + +Project: "rwgk1"=.\rwgk1\rwgk1.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name bpl_static + End Project Dependency +}}} + +############################################################################### + +Project: "test"=.\test\test.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name bpl_static + End Project Dependency +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/build/build.opt b/build/build.opt new file mode 100644 index 00000000..89eb84a7 Binary files /dev/null and b/build/build.opt differ diff --git a/build/como.mak b/build/como.mak new file mode 100644 index 00000000..8f05e309 --- /dev/null +++ b/build/como.mak @@ -0,0 +1,58 @@ +# Revision History: +# 17 Apr 01 include cross-module support, compile getting_started1 (R.W. Grosse-Kunstleve) UNTESTED! +# 06 Mar 01 Fixed typo in use of "PYTHON_LIB" (Dave Abrahams) +# 04 Mar 01 Changed library name to libboost_python.a (David Abrahams) + +LIBSRC = \ + classes.cpp \ + conversions.cpp \ + cross_module.cpp \ + extension_class.cpp \ + functions.cpp \ + init_function.cpp \ + module_builder.cpp \ + objects.cpp \ + types.cpp + +LIBOBJ = $(LIBSRC:.cpp=.o) +OBJ = $(LIBOBJ) + + +ifeq "$(OS)" "Windows_NT" +PYTHON_LIB=c:/tools/python/libs/python15.lib +INC = -Ic:/cygnus/usr/include/g++-3 -Ic:/cygnus/usr/include -Ic:/boost -Ic:/tools/python/include +MODULE_EXTENSION=dll +else +INC = -I/usr/local/include/python1.5 +MODULE_EXTENSION=so +endif + +%.o: ../src/%.cpp + como --pic $(INC) -o $*.o -c $< + +%.d: ../src/%.cpp + @echo creating $@ + @set -e; como -M $(INC) -c $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ + [ -s $@ ] || rm -f $@ + +getting_started1: getting_started1.o libboost_python.a + como-dyn-link -o ../example/getting_started1.$(MODULE_EXTENSION) $(PYTHON_LIB) getting_started1.o -L. -lboost_python + ln -s ../test/doctest.py ../example + python ../example/test_getting_started1.py + +getting_started1.o: ../example/getting_started1.cpp + como --pic $(INC) -o $*.o -c $< + +clean: + rm -rf *.o *.$(MODULE_EXTENSION) *.a *.d *.pyc *.bak a.out + +libboost_python.a: $(LIBOBJ) + rm -f libboost_python.a + ar cq libboost_python.a $(LIBOBJ) + +DEP = $(OBJ:.o=.d) + +ifneq "$(MAKECMDGOALS)" "clean" +include $(DEP) +endif diff --git a/build/example1/example1.dsp b/build/example1/example1.dsp new file mode 100644 index 00000000..4d95aa97 --- /dev/null +++ b/build/example1/example1.dsp @@ -0,0 +1,136 @@ +# Microsoft Developer Studio Project File - Name="example1" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=example1 - Win32 DebugPython +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "example1.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "example1.mak" CFG="example1 - Win32 DebugPython" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "example1 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "example1 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "example1 - Win32 DebugPython" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "example1 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXAMPLE1_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXAMPLE1_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"Release/hello.dll" /libpath:"c:\tools\python\libs" + +!ELSEIF "$(CFG)" == "example1 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXAMPLE1_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm- /GR /GX /Zi /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXAMPLE1_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /debug /machine:I386 /out:"Debug/hello.dll" /pdbtype:sept /libpath:"c:\tools\python\libs" + +!ELSEIF "$(CFG)" == "example1 - Win32 DebugPython" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "example1___Win32_DebugPython" +# PROP BASE Intermediate_Dir "example1___Win32_DebugPython" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "DebugPython" +# PROP Intermediate_Dir "DebugPython" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /GR /GX /Zi /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXAMPLE1_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm- /GR /GX /Zi /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXAMPLE1_EXPORTS" /D "BOOST_DEBUG_PYTHON" /YX /FD /GZ /EHs /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /debug /machine:I386 /out:"Debug/hello.dll" /pdbtype:sept /libpath:"c:\tools\python\libs" +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /debug /machine:I386 /out:"DebugPython/hello_d.dll" /pdbtype:sept /libpath:"c:\tools\python\src\PCbuild" + +!ENDIF + +# Begin Target + +# Name "example1 - Win32 Release" +# Name "example1 - Win32 Debug" +# Name "example1 - Win32 DebugPython" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\example\example1.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/filemgr.py b/build/filemgr.py new file mode 100644 index 00000000..d8310f6d --- /dev/null +++ b/build/filemgr.py @@ -0,0 +1,132 @@ +# Revision history: +# 12 Apr 01 use os.path, shutil +# Initial version: R.W. Grosse-Kunstleve + +bpl_src = "/libs/python/src" +bpl_tst = "/libs/python/test" +bpl_exa = "/libs/python/example" +files = ( +bpl_src + "/classes.cpp", +bpl_src + "/conversions.cpp", +bpl_src + "/extension_class.cpp", +bpl_src + "/functions.cpp", +bpl_src + "/init_function.cpp", +bpl_src + "/module_builder.cpp", +bpl_src + "/objects.cpp", +bpl_src + "/types.cpp", +bpl_src + "/cross_module.cpp", +bpl_tst + "/comprehensive.cpp", +bpl_tst + "/comprehensive.hpp", +bpl_tst + "/comprehensive.py", +bpl_tst + "/doctest.py", +bpl_exa + "/abstract.cpp", +bpl_exa + "/getting_started1.cpp", +bpl_exa + "/getting_started2.cpp", +bpl_exa + "/simple_vector.cpp", +bpl_exa + "/do_it_yourself_converters.cpp", +bpl_exa + "/pickle1.cpp", +bpl_exa + "/pickle2.cpp", +bpl_exa + "/pickle3.cpp", +bpl_exa + "/test_abstract.py", +bpl_exa + "/test_getting_started1.py", +bpl_exa + "/test_getting_started2.py", +bpl_exa + "/test_simple_vector.py", +bpl_exa + "/test_do_it_yourself_converters.py", +bpl_exa + "/test_pickle1.py", +bpl_exa + "/test_pickle2.py", +bpl_exa + "/test_pickle3.py", +bpl_exa + "/noncopyable.h", +bpl_exa + "/noncopyable_export.cpp", +bpl_exa + "/noncopyable_import.cpp", +bpl_exa + "/dvect.h", +bpl_exa + "/dvect.cpp", +bpl_exa + "/dvect_conversions.cpp", +bpl_exa + "/dvect_defs.cpp", +bpl_exa + "/ivect.h", +bpl_exa + "/ivect.cpp", +bpl_exa + "/ivect_conversions.cpp", +bpl_exa + "/ivect_defs.cpp", +bpl_exa + "/tst_noncopyable.py", +bpl_exa + "/tst_dvect1.py", +bpl_exa + "/tst_dvect2.py", +bpl_exa + "/tst_ivect1.py", +bpl_exa + "/tst_ivect2.py", +bpl_exa + "/test_cross_module.py", +) + +defs = ( +"boost_python_test", +"abstract", +"getting_started1", +"getting_started2", +"simple_vector", +"do_it_yourself_converters", +"pickle1", +"pickle2", +"pickle3", +"noncopyable_export", +"noncopyable_import", +"ivect", +"dvect", +) + +if (__name__ == "__main__"): + + import sys, os, shutil + + path = sys.argv[1] + mode = sys.argv[2] + if (not mode in ("softlinks", "unlink", "cp", "rm", "copy", "del")): + raise RuntimeError, \ + "usage: python filemgr.py path " + + if (mode in ("cp", "copy")): + for fn in files: + f = os.path.basename(fn) + print "Copying: " + f + shutil.copy(path + fn, ".") + + elif (mode == "softlinks"): + for fn in files: + f = os.path.basename(fn) + if (os.path.exists(f)): + print "File exists: " + f + else: + print "Linking: " + f + os.symlink(path + fn, f) + + elif (mode in ("rm", "del")): + for fn in files: + f = os.path.basename(fn) + if (os.path.exists(f)): + print "Removing: " + f + try: os.unlink(f) + except: pass + + elif (mode == "unlink"): + for fn in files: + f = os.path.basename(fn) + if (os.path.exists(f)): + if (os.path.islink(f)): + print "Unlinking: " + f + try: os.unlink(f) + except: pass + else: + print "Not a softlink: " + f + + if (mode in ("softlinks", "cp", "copy")): + for d in defs: + fn = d + ".def" + print "Creating: " + fn + f = open(fn, "w") + f.write("EXPORTS\n") + f.write("\tinit" + d + "\n") + f.close() + + if (mode in ("unlink", "rm", "del")): + for d in defs: + fn = d + ".def" + if (os.path.exists(fn)): + print "Removing: " + fn + try: os.unlink(fn) + except: pass diff --git a/build/gcc.mak b/build/gcc.mak new file mode 100644 index 00000000..ce718f2b --- /dev/null +++ b/build/gcc.mak @@ -0,0 +1,87 @@ +# Revision History + +# 17 Apr 01 include cross-module support, compile getting_started1 (R.W. Grosse-Kunstleve) +# 17 Apr 01 build shared library (patch provided by Dan Nuffer) +# 04 Mar 01 Changed library name to libboost_python.a, various cleanups, +# attempted Cygwin compatibility. Still needs testing on Linux +# (David Abrahams) + + +LIBSRC = \ + classes.cpp \ + conversions.cpp \ + cross_module.cpp \ + extension_class.cpp \ + functions.cpp \ + init_function.cpp \ + module_builder.cpp \ + objects.cpp \ + types.cpp + +LIBOBJ = $(LIBSRC:.cpp=.o) +OBJ = $(LIBOBJ) + +LIBNAME = libboost_python +# libpython2.0.dll + +ifeq "$(OS)" "Windows_NT" +ROOT=c:/cygnus +INC = -Ic:/cygnus/usr/include/g++-3 -Ic:/cygnus/usr/include -Ic:/boost -I$(PYTHON_INC) +MODULE_EXTENSION=dll +PYTHON_LIB=c:/cygnus/usr/local/lib/python2.0/config/libpython2.0.dll.a +SHARED_LIB = $(LIBNAME).dll +else +PYTHON_INC=$(ROOT)/usr/local/Python-2.0/include/python2.0 +BOOST_INC=../../.. +INC = -I$(BOOST_INC) -I$(PYTHON_INC) +MODULE_EXTENSION=so +VERSION=1 +SHARED_LIB = $(LIBNAME).so.$(VERSION) +endif + +%.o: ../src/%.cpp + g++ -fPIC -Wall -W $(INC) $(CXXFLAGS) -o $*.o -c $< + +%.d: ../src/%.cpp + @echo creating $@ + @set -e; g++ -M $(INC) -c $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ + [ -s $@ ] || rm -f $@ + + +PYTHON = python + +all: test $(SHARED_LIB) getting_started1 + +test: comprehensive.o $(LIBNAME).a $(SHARED_LIB) + g++ $(CXXFLAGS) -shared -o ../test/boost_python_test.$(MODULE_EXTENSION) comprehensive.o -L. -lboost_python $(PYTHON_LIB) + $(PYTHON) ../test/comprehensive.py + +comprehensive.o: ../test/comprehensive.cpp + g++ $(CXXFLAGS) --template-depth-32 -fPIC -Wall -W $(INC) -o $*.o -c $< + + +getting_started1: getting_started1.o $(LIBNAME).a + g++ $(CXXFLAGS) -shared -o ../example/getting_started1.$(MODULE_EXTENSION) getting_started1.o -L. -lboost_python $(PYTHON_LIB) + ln -s ../test/doctest.py ../example + $(PYTHON) ../example/test_getting_started1.py + +getting_started1.o: ../example/getting_started1.cpp + g++ $(CXXFLAGS) --template-depth-32 -fPIC -Wall -W $(INC) -o $*.o -c $< + + +clean: + rm -rf *.o *.$(MODULE_EXTENSION) *.a *.d *.pyc *.bak a.out + +$(LIBNAME).a: $(LIBOBJ) + rm -f $@ + ar cqs $@ $(LIBOBJ) + +$(SHARED_LIB): $(LIBOBJ) + g++ $(CXXFLAGS) -shared -o $@ -Wl,--soname=$(LIBNAME).$(MODULE_EXTENSION) + +DEP = $(OBJ:.o=.d) + +ifneq "$(MAKECMDGOALS)" "clean" +include $(DEP) +endif diff --git a/build/getting_started1/getting_started1.dsp b/build/getting_started1/getting_started1.dsp new file mode 100644 index 00000000..a41eb057 --- /dev/null +++ b/build/getting_started1/getting_started1.dsp @@ -0,0 +1,136 @@ +# Microsoft Developer Studio Project File - Name="getting_started1" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=getting_started1 - Win32 DebugPython +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "getting_started1.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "getting_started1.mak" CFG="getting_started1 - Win32 DebugPython" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "getting_started1 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "getting_started1 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "getting_started1 - Win32 DebugPython" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=xicl6.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "getting_started1 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GR /GX /O2 /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 /libpath:"c:\tools\python\libs" + +!ELSEIF "$(CFG)" == "getting_started1 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /GR /GX /ZI /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept /libpath:"c:\tools\python\libs" + +!ELSEIF "$(CFG)" == "getting_started1 - Win32 DebugPython" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "DebugPython" +# PROP BASE Intermediate_Dir "DebugPython" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "DebugPython" +# PROP Intermediate_Dir "DebugPython" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /GR /GX /Zi /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TEST_EXPORTS" /D "BOOST_DEBUG_PYTHON" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /pdb:"DebugPython/boost_python_test_d.pdb" /debug /machine:I386 /out:"DebugPython/getting_started1_d.dll" /pdbtype:sept /libpath:"c:\tools\python\src\PCbuild" +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "getting_started1 - Win32 Release" +# Name "getting_started1 - Win32 Debug" +# Name "getting_started1 - Win32 DebugPython" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\example\getting_started1.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/getting_started2/getting_started2.dsp b/build/getting_started2/getting_started2.dsp new file mode 100644 index 00000000..284bab21 --- /dev/null +++ b/build/getting_started2/getting_started2.dsp @@ -0,0 +1,135 @@ +# Microsoft Developer Studio Project File - Name="getting_started2" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=getting_started2 - Win32 DebugPython +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "getting_started2.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "getting_started2.mak" CFG="getting_started2 - Win32 DebugPython" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "getting_started2 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "getting_started2 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "getting_started2 - Win32 DebugPython" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "getting_started2" +# PROP Scc_LocalPath "." +CPP=xicl6.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "getting_started2 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GR /GX /O2 /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 /libpath:"c:\tools\python\libs" + +!ELSEIF "$(CFG)" == "getting_started2 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm- /GR /GX /Zi /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept /libpath:"c:\tools\python\libs" + +!ELSEIF "$(CFG)" == "getting_started2 - Win32 DebugPython" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "getting_started2___Win32_DebugPython" +# PROP BASE Intermediate_Dir "getting_started2___Win32_DebugPython" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "getting_started2___Win32_DebugPython" +# PROP Intermediate_Dir "getting_started2___Win32_DebugPython" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm- /GR /GX /Zi /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TEST_EXPORTS" /D "BOOST_DEBUG_PYTHON" /FR /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"DebugPython/getting_started2_d.dll" /pdbtype:sept /libpath:"c:\tools\python\src\pcbuild" + +!ENDIF + +# Begin Target + +# Name "getting_started2 - Win32 Release" +# Name "getting_started2 - Win32 Debug" +# Name "getting_started2 - Win32 DebugPython" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\example\getting_started2.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/irix_CC.mak b/build/irix_CC.mak new file mode 100644 index 00000000..5894ff51 --- /dev/null +++ b/build/irix_CC.mak @@ -0,0 +1,160 @@ +# Usage: +# +# Create a new empty directory anywhere (preferably not in the boost tree). +# Copy this Makefile to that new directory and rename it to "Makefile" +# Adjust the pathnames below. +# +# make softlinks Create softlinks to source code and tests +# make Compile all sources +# make test Run doctest tests +# make clean Remove all object files +# make unlink Remove softlinks +# +# Revision history: +# 12 Apr 01 new macro ROOT to simplify configuration (R.W. Grosse-Kunstleve) +# Initial version: R.W. Grosse-Kunstleve + +ROOT=$(HOME) +BOOST=$(ROOT)/boost + +PYEXE=/usr/local/Python-1.5.2/bin/python +PYINC=-I/usr/local/Python-1.5.2/include/python1.5 +#PYEXE=/usr/local/Python-2.0/bin/python +#PYINC=-I/usr/local/Python-2.0/include/python2.0 +STLPORTINC=-I$(BOOST)/boost/compatibility/cpp_c_headers + +STDOPTS= +WARNOPTS=-woff 1001,1234,1682 +OPTOPTS=-g + +CPP=CC -LANG:std -n32 -mips4 +CPPOPTS=$(STLPORTINC) $(STLPORTOPTS) -I$(BOOST) $(PYINC) \ + $(STDOPTS) $(WARNOPTS) $(OPTOPTS) +MAKEDEP=-M + +LD=CC -LANG:std -n32 -mips4 +LDOPTS=-shared + +OBJ=classes.o conversions.o extension_class.o functions.o \ + init_function.o module_builder.o \ + objects.o types.o cross_module.o +DEPOBJ=$(OBJ) \ + comprehensive.o \ + abstract.o \ + getting_started1.o getting_started2.o \ + simple_vector.o \ + do_it_yourself_converters.o \ + pickle1.o pickle2.o pickle3.o \ + noncopyable_export.o noncopyable_import.o \ + ivect.o dvect.o + +.SUFFIXES: .o .cpp + +all: libboost_python.a \ + boost_python_test.so \ + abstract.so \ + getting_started1.so getting_started2.so \ + simple_vector.so \ + do_it_yourself_converters.so \ + pickle1.so pickle2.so pickle3.so \ + noncopyable_export.so noncopyable_import.so \ + ivect.so dvect.so + +libboost_python.a: $(OBJ) + rm -f libboost_python.a + $(CPP) -ar -o libboost_python.a $(OBJ) + +boost_python_test.so: $(OBJ) comprehensive.o + $(LD) $(LDOPTS) $(OBJ) comprehensive.o -o boost_python_test.so -lm + +abstract.so: $(OBJ) abstract.o + $(LD) $(LDOPTS) $(OBJ) abstract.o -o abstract.so + +getting_started1.so: $(OBJ) getting_started1.o + $(LD) $(LDOPTS) $(OBJ) getting_started1.o -o getting_started1.so + +getting_started2.so: $(OBJ) getting_started2.o + $(LD) $(LDOPTS) $(OBJ) getting_started2.o -o getting_started2.so + +simple_vector.so: $(OBJ) simple_vector.o + $(LD) $(LDOPTS) $(OBJ) simple_vector.o -o simple_vector.so + +do_it_yourself_converters.so: $(OBJ) do_it_yourself_converters.o + $(LD) $(LDOPTS) $(OBJ) do_it_yourself_converters.o -o do_it_yourself_converters.so + +pickle1.so: $(OBJ) pickle1.o + $(LD) $(LDOPTS) $(OBJ) pickle1.o -o pickle1.so + +pickle2.so: $(OBJ) pickle2.o + $(LD) $(LDOPTS) $(OBJ) pickle2.o -o pickle2.so + +pickle3.so: $(OBJ) pickle3.o + $(LD) $(LDOPTS) $(OBJ) pickle3.o -o pickle3.so + +noncopyable_export.so: $(OBJ) noncopyable_export.o + $(LD) $(LDOPTS) $(OBJ) $(HIDDEN) \ + noncopyable_export.o -o noncopyable_export.so + +noncopyable_import.so: $(OBJ) noncopyable_import.o + $(LD) $(LDOPTS) $(OBJ) $(HIDDEN) \ + noncopyable_import.o -o noncopyable_import.so + +ivect.so: $(OBJ) ivect.o + $(LD) $(LDOPTS) $(OBJ) $(HIDDEN) ivect.o -o ivect.so + +dvect.so: $(OBJ) dvect.o + $(LD) $(LDOPTS) $(OBJ) $(HIDDEN) dvect.o -o dvect.so + +.cpp.o: + $(CPP) $(CPPOPTS) -c $*.cpp + +test: + $(PYEXE) comprehensive.py + $(PYEXE) test_abstract.py + $(PYEXE) test_getting_started1.py + $(PYEXE) test_getting_started2.py + $(PYEXE) test_simple_vector.py + $(PYEXE) test_do_it_yourself_converters.py + $(PYEXE) test_pickle1.py + $(PYEXE) test_pickle2.py + $(PYEXE) test_pickle3.py + $(PYEXE) test_cross_module.py + +clean: + rm -f $(OBJ) libboost_python.a libboost_python.a.input + rm -f comprehensive.o boost_python_test.so + rm -f abstract.o abstract.so + rm -f getting_started1.o getting_started1.so + rm -f getting_started2.o getting_started2.so + rm -f simple_vector.o simple_vector.so + rm -f do_it_yourself_converters.o do_it_yourself_converters.so + rm -f pickle1.o pickle1.so + rm -f pickle2.o pickle2.so + rm -f pickle3.o pickle3.so + rm -f noncopyable_export.o noncopyable_export.so + rm -f noncopyable_import.o noncopyable_import.so + rm -f ivect.o ivect.so + rm -f dvect.o dvect.so + rm -f so_locations *.pyc + rm -rf ii_files + +softlinks: + $(PYEXE) $(BOOST)/libs/python/build/filemgr.py $(BOOST) softlinks + +unlink: + $(PYEXE) $(BOOST)/libs/python/build/filemgr.py $(BOOST) unlink + +cp: + $(PYEXE) $(BOOST)/libs/python/build/filemgr.py $(BOOST) cp + +rm: + $(PYEXE) $(BOOST)/libs/python/build/filemgr.py $(BOOST) rm + +depend: + @ cat Makefile.nodepend; \ + for obj in $(DEPOBJ); \ + do \ + bn=`echo "$$obj" | cut -d. -f1`; \ + $(CPP) $(CPPOPTS) $(MAKEDEP) "$$bn".cpp; \ + done + diff --git a/build/linux_gcc.mak b/build/linux_gcc.mak new file mode 100644 index 00000000..5971ca61 --- /dev/null +++ b/build/linux_gcc.mak @@ -0,0 +1,160 @@ +# Usage: +# +# Create a new empty directory anywhere (preferably not in the boost tree). +# Copy this Makefile to that new directory and rename it to "Makefile" +# Adjust the pathnames below. +# +# make softlinks Create softlinks to source code and tests +# make Compile all sources +# make test Run doctest tests +# make clean Remove all object files +# make unlink Remove softlinks +# +# Revision history: +# 12 Apr 01 new macro ROOT to simplify configuration (R.W. Grosse-Kunstleve) +# Initial version: R.W. Grosse-Kunstleve + +ROOT=$(HOME) +BOOST=$(ROOT)/boost + +PYEXE=PYTHONPATH=. /usr/bin/python +PYINC=-I/usr/include/python1.5 +#PYEXE=/usr/local/Python-1.5.2/bin/python +#PYINC=-I/usr/local/Python-1.5.2/include/python1.5 +#PYEXE=/usr/local/Python-2.0/bin/python +#PYINC=-I/usr/local/Python-2.0/include/python2.0 + +STDOPTS=-fPIC -ftemplate-depth-21 +WARNOPTS= +OPTOPTS=-g + +CPP=g++ +CPPOPTS=$(STLPORTINC) $(STLPORTOPTS) -I$(BOOST) $(PYINC) \ + $(STDOPTS) $(WARNOPTS) $(OPTOPTS) +MAKEDEP=-M + +LD=$(CPP) +LDOPTS=-shared + +OBJ=classes.o conversions.o extension_class.o functions.o \ + init_function.o module_builder.o \ + objects.o types.o cross_module.o +DEPOBJ=$(OBJ) \ + comprehensive.o \ + abstract.o \ + getting_started1.o getting_started2.o \ + simple_vector.o \ + do_it_yourself_converters.o \ + pickle1.o pickle2.o pickle3.o \ + noncopyable_export.o noncopyable_import.o \ + ivect.o dvect.o + +.SUFFIXES: .o .cpp + +all: libboost_python.a \ + boost_python_test.so \ + abstract.so \ + getting_started1.so getting_started2.so \ + simple_vector.so \ + do_it_yourself_converters.so \ + pickle1.so pickle2.so pickle3.so \ + noncopyable_export.so noncopyable_import.so \ + ivect.so dvect.so + +libboost_python.a: $(OBJ) + rm -f libboost_python.a + ar r libboost_python.a $(OBJ) + +boost_python_test.so: $(OBJ) comprehensive.o + $(LD) $(LDOPTS) $(OBJ) comprehensive.o -o boost_python_test.so -lm + +abstract.so: $(OBJ) abstract.o + $(LD) $(LDOPTS) $(OBJ) abstract.o -o abstract.so + +getting_started1.so: $(OBJ) getting_started1.o + $(LD) $(LDOPTS) $(OBJ) getting_started1.o -o getting_started1.so + +getting_started2.so: $(OBJ) getting_started2.o + $(LD) $(LDOPTS) $(OBJ) getting_started2.o -o getting_started2.so + +simple_vector.so: $(OBJ) simple_vector.o + $(LD) $(LDOPTS) $(OBJ) simple_vector.o -o simple_vector.so + +do_it_yourself_converters.so: $(OBJ) do_it_yourself_converters.o + $(LD) $(LDOPTS) $(OBJ) do_it_yourself_converters.o -o do_it_yourself_converters.so + +pickle1.so: $(OBJ) pickle1.o + $(LD) $(LDOPTS) $(OBJ) pickle1.o -o pickle1.so + +pickle2.so: $(OBJ) pickle2.o + $(LD) $(LDOPTS) $(OBJ) pickle2.o -o pickle2.so + +pickle3.so: $(OBJ) pickle3.o + $(LD) $(LDOPTS) $(OBJ) pickle3.o -o pickle3.so + +noncopyable_export.so: $(OBJ) noncopyable_export.o + $(LD) $(LDOPTS) $(OBJ) $(HIDDEN) \ + noncopyable_export.o -o noncopyable_export.so + +noncopyable_import.so: $(OBJ) noncopyable_import.o + $(LD) $(LDOPTS) $(OBJ) $(HIDDEN) \ + noncopyable_import.o -o noncopyable_import.so + +ivect.so: $(OBJ) ivect.o + $(LD) $(LDOPTS) $(OBJ) $(HIDDEN) ivect.o -o ivect.so + +dvect.so: $(OBJ) dvect.o + $(LD) $(LDOPTS) $(OBJ) $(HIDDEN) dvect.o -o dvect.so + +.cpp.o: + $(CPP) $(CPPOPTS) -c $*.cpp + +test: + $(PYEXE) comprehensive.py + $(PYEXE) test_abstract.py + $(PYEXE) test_getting_started1.py + $(PYEXE) test_getting_started2.py + $(PYEXE) test_simple_vector.py + $(PYEXE) test_do_it_yourself_converters.py + $(PYEXE) test_pickle1.py + $(PYEXE) test_pickle2.py + $(PYEXE) test_pickle3.py + $(PYEXE) test_cross_module.py + +clean: + rm -f $(OBJ) libboost_python.a libboost_python.a.input + rm -f comprehensive.o boost_python_test.so + rm -f abstract.o abstract.so + rm -f getting_started1.o getting_started1.so + rm -f getting_started2.o getting_started2.so + rm -f simple_vector.o simple_vector.so + rm -f do_it_yourself_converters.o do_it_yourself_converters.so + rm -f pickle1.o pickle1.so + rm -f pickle2.o pickle2.so + rm -f pickle3.o pickle3.so + rm -f noncopyable_export.o noncopyable_export.so + rm -f noncopyable_import.o noncopyable_import.so + rm -f ivect.o ivect.so + rm -f dvect.o dvect.so + rm -f so_locations *.pyc + +softlinks: + $(PYEXE) $(BOOST)/libs/python/build/filemgr.py $(BOOST) softlinks + +unlink: + $(PYEXE) $(BOOST)/libs/python/build/filemgr.py $(BOOST) unlink + +cp: + $(PYEXE) $(BOOST)/libs/python/build/filemgr.py $(BOOST) cp + +rm: + $(PYEXE) $(BOOST)/libs/python/build/filemgr.py $(BOOST) rm + +depend: + @ cat Makefile.nodepend; \ + for obj in $(DEPOBJ); \ + do \ + bn=`echo "$$obj" | cut -d. -f1`; \ + $(CPP) $(CPPOPTS) $(MAKEDEP) "$$bn".cpp; \ + done + diff --git a/build/mingw32.mak b/build/mingw32.mak new file mode 100644 index 00000000..0a16f332 --- /dev/null +++ b/build/mingw32.mak @@ -0,0 +1,189 @@ +# Usage: +# +# make copy Copy the sources and tests +# make Compile all sources +# make test Run doctest tests +# make clean Remove all object files +# make del Remove the sources and tests +# +# Revision history: +# 12 Apr 01 new macro ROOT to simplify configuration (R.W. Grosse-Kunstleve) +# Initial version: R.W. Grosse-Kunstleve + +# To install mingw32, follow instructions at: +# http://starship.python.net/crew/kernr/mingw32/Notes.html +# In particular, install: +# ftp://ftp.xraylith.wisc.edu/pub/khan/gnu-win32/mingw32/gcc-2.95.2/gcc-2.95.2-msvcrt.exe +# ftp://ftp.xraylith.wisc.edu/pub/khan/gnu-win32/mingw32/gcc-2.95.2/fixes/quote-fix-msvcrt.exe +# http://starship.python.net/crew/kernr/mingw32/Python-1.5.2-mingw32.zip +# Unpack the first two archives in the default locations and update your PATH. +# Unpack the third archive in \usr. + +# Note: comprehensive.cpp generates compiler errors and later crashes. +# L:\boost\boost\python\detail\extension_class.hpp:643: warning: +# alignment of `vtable for class +# boost::python::detail::held_instance' +# is greater than maximum object file alignment. Using 16. +# Could this be fixed with compiler options? +# -fhuge-objects looks interesting, but requires recompiling the C++ library. +# (what exactly does that mean?) +# -fvtable-thunks eliminates the compiler warning, but +# "import boost_python_test" still causes a crash. + +ROOT=L: +BOOST_WIN="$(ROOT)\boost" +BOOST_UNIX=$(HOME)/boost + +PYEXE="C:\Program files\Python\python.exe" +PYINC=-I"C:\usr\include\python1.5" +PYLIB="C:\usr\lib\libpython15.a" + +STDOPTS=-ftemplate-depth-21 +WARNOPTS= +OPTOPTS=-g + +CPP=g++ +CPPOPTS=$(STLPORTINC) $(STLPORTOPTS) -I$(BOOST_WIN) $(PYINC) \ + $(STDOPTS) $(WARNOPTS) $(OPTOPTS) + +LD=g++ +LDOPTS=-shared + +OBJ=classes.o conversions.o extension_class.o functions.o \ + init_function.o module_builder.o \ + objects.o types.o cross_module.o + +.SUFFIXES: .o .cpp + +all: libboost_python.a \ + abstract.pyd \ + getting_started1.pyd getting_started2.pyd \ + simple_vector.pyd \ + do_it_yourself_converters.pyd \ + pickle1.pyd pickle2.pyd pickle3.pyd \ + noncopyable_export.pyd noncopyable_import.pyd \ + ivect.pyd dvect.pyd + +libboost_python.a: $(OBJ) + del libboost_python.a + ar r libboost_python.a $(OBJ) + +DLLWRAPOPTS=-s --driver-name g++ -s \ + --entry _DllMainCRTStartup@12 --target=i386-mingw32 + +boost_python_test.pyd: $(OBJ) comprehensive.o + dllwrap $(DLLWRAPOPTS) \ + --dllname boost_python_test.pyd \ + --def boost_python_test.def \ + $(OBJ) comprehensive.o $(PYLIB) + +abstract.pyd: $(OBJ) abstract.o + dllwrap $(DLLWRAPOPTS) \ + --dllname abstract.pyd \ + --def abstract.def \ + $(OBJ) abstract.o $(PYLIB) + +getting_started1.pyd: $(OBJ) getting_started1.o + dllwrap $(DLLWRAPOPTS) \ + --dllname getting_started1.pyd \ + --def getting_started1.def \ + $(OBJ) getting_started1.o $(PYLIB) + +getting_started2.pyd: $(OBJ) getting_started2.o + dllwrap $(DLLWRAPOPTS) \ + --dllname getting_started2.pyd \ + --def getting_started2.def \ + $(OBJ) getting_started2.o $(PYLIB) + +simple_vector.pyd: $(OBJ) simple_vector.o + dllwrap $(DLLWRAPOPTS) \ + --dllname simple_vector.pyd \ + --def simple_vector.def \ + $(OBJ) simple_vector.o $(PYLIB) + +do_it_yourself_converters.pyd: $(OBJ) do_it_yourself_converters.o + dllwrap $(DLLWRAPOPTS) \ + --dllname do_it_yourself_converters.pyd \ + --def do_it_yourself_converters.def \ + $(OBJ) do_it_yourself_converters.o $(PYLIB) + +pickle1.pyd: $(OBJ) pickle1.o + dllwrap $(DLLWRAPOPTS) \ + --dllname pickle1.pyd \ + --def pickle1.def \ + $(OBJ) pickle1.o $(PYLIB) + +pickle2.pyd: $(OBJ) pickle2.o + dllwrap $(DLLWRAPOPTS) \ + --dllname pickle2.pyd \ + --def pickle2.def \ + $(OBJ) pickle2.o $(PYLIB) + +pickle3.pyd: $(OBJ) pickle3.o + dllwrap $(DLLWRAPOPTS) \ + --dllname pickle3.pyd \ + --def pickle3.def \ + $(OBJ) pickle3.o $(PYLIB) + +noncopyable_export.pyd: $(OBJ) noncopyable_export.o + dllwrap $(DLLWRAPOPTS) \ + --dllname noncopyable_export.pyd \ + --def noncopyable_export.def \ + $(OBJ) noncopyable_export.o $(PYLIB) + +noncopyable_import.pyd: $(OBJ) noncopyable_import.o + dllwrap $(DLLWRAPOPTS) \ + --dllname noncopyable_import.pyd \ + --def noncopyable_import.def \ + $(OBJ) noncopyable_import.o $(PYLIB) + +ivect.pyd: $(OBJ) ivect.o + dllwrap $(DLLWRAPOPTS) \ + --dllname ivect.pyd \ + --def ivect.def \ + $(OBJ) ivect.o $(PYLIB) + +dvect.pyd: $(OBJ) dvect.o + dllwrap $(DLLWRAPOPTS) \ + --dllname dvect.pyd \ + --def dvect.def \ + $(OBJ) dvect.o $(PYLIB) + +.cpp.o: + $(CPP) $(CPPOPTS) -c $*.cpp + +test: +# $(PYEXE) comprehensive.py + $(PYEXE) test_abstract.py + $(PYEXE) test_getting_started1.py + $(PYEXE) test_getting_started2.py + $(PYEXE) test_simple_vector.py + $(PYEXE) test_do_it_yourself_converters.py + $(PYEXE) test_pickle1.py + $(PYEXE) test_pickle2.py + $(PYEXE) test_pickle3.py + $(PYEXE) test_cross_module.py + +clean: + del *.o + del *.a + del *.pyd + del *.pyc + +softlinks: + python $(BOOST_UNIX)/libs/python/build/filemgr.py $(BOOST_UNIX) softlinks + +unlink: + python $(BOOST_UNIX)/libs/python/build/filemgr.py $(BOOST_UNIX) unlink + +cp: + python $(BOOST_UNIX)/libs/python/build/filemgr.py $(BOOST_UNIX) cp + +rm: + python $(BOOST_UNIX)/libs/python/build/filemgr.py $(BOOST_UNIX) rm + +copy: + $(PYEXE) $(BOOST_WIN)\libs\python\build\filemgr.py $(BOOST_WIN) copy + +del: + $(PYEXE) $(BOOST_WIN)\libs\python\build\filemgr.py $(BOOST_WIN) del diff --git a/build/rwgk1/rwgk1.dsp b/build/rwgk1/rwgk1.dsp new file mode 100644 index 00000000..67476984 --- /dev/null +++ b/build/rwgk1/rwgk1.dsp @@ -0,0 +1,135 @@ +# Microsoft Developer Studio Project File - Name="rwgk1" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=rwgk1 - Win32 DebugPython +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "rwgk1.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "rwgk1.mak" CFG="rwgk1 - Win32 DebugPython" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "rwgk1 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "rwgk1 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "rwgk1 - Win32 DebugPython" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "rwgk1 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RWGK1_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RWGK1_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /libpath:"c:\tools\python\libs" + +!ELSEIF "$(CFG)" == "rwgk1 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RWGK1_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm- /GX /Zi /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RWGK1_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /debug /machine:I386 /pdbtype:sept /libpath:"c:\tools\python\libs" + +!ELSEIF "$(CFG)" == "rwgk1 - Win32 DebugPython" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "rwgk1___Win32_DebugPython" +# PROP BASE Intermediate_Dir "rwgk1___Win32_DebugPython" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "DebugPython" +# PROP Intermediate_Dir "DebugPython" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RWGK1_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm- /GX /Zi /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RWGK1_EXPORTS" /D "BOOST_DEBUG_PYTHON" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /debug /machine:I386 /pdbtype:sept /libpath:"c:\tools\python\libs" +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /debug /machine:I386 /out:"DebugPython/rwgk1_d.dll" /pdbtype:sept /libpath:"C:\tools\python\src\PCbuild" + +!ENDIF + +# Begin Target + +# Name "rwgk1 - Win32 Release" +# Name "rwgk1 - Win32 Debug" +# Name "rwgk1 - Win32 DebugPython" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\example\rwgk1.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/test/test.dsp b/build/test/test.dsp new file mode 100644 index 00000000..4bd2822a --- /dev/null +++ b/build/test/test.dsp @@ -0,0 +1,145 @@ +# Microsoft Developer Studio Project File - Name="test" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=test - Win32 DebugPython +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "test.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "test.mak" CFG="test - Win32 DebugPython" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "test - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "test - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "test - Win32 DebugPython" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "test - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TEST_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GR /GX /O2 /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TEST_EXPORTS" /YX /FD /Zm200 /c +# SUBTRACT CPP /Fr +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"Release/boost_python_test.dll" /libpath:"c:\tools\python\libs" + +!ELSEIF "$(CFG)" == "test - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TEST_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm- /GR /GX /Zi /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TEST_EXPORTS" /YX /FD /GZ /Zm200 /c +# SUBTRACT CPP /Fr +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /debug /machine:I386 /out:"Debug/boost_python_test.dll" /pdbtype:sept /libpath:"c:\tools\python\libs" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "test - Win32 DebugPython" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "test___Win32_DebugPython" +# PROP BASE Intermediate_Dir "test___Win32_DebugPython" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "DebugPython" +# PROP Intermediate_Dir "DebugPython" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /GR /GX /Zi /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TEST_EXPORTS" /YX /FD /GZ /Zm200 /c +# ADD CPP /nologo /MDd /W3 /Gm- /GR /GX /Zi /Od /I "..\..\..\.." /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TEST_EXPORTS" /D "BOOST_DEBUG_PYTHON" /YX /FD /GZ /Zm200 /EHs /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /debug /machine:I386 /pdbtype:sept /libpath:"c:\tools\python\libs" +# SUBTRACT BASE LINK32 /pdb:none +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /debug /machine:I386 /out:"DebugPython/boost_python_test_d.dll" /pdbtype:sept /libpath:"c:\tools\python\src\PCbuild" +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "test - Win32 Release" +# Name "test - Win32 Debug" +# Name "test - Win32 DebugPython" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\test\comprehensive.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\test\comprehensive.hpp +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/tru64_cxx.mak b/build/tru64_cxx.mak new file mode 100644 index 00000000..21a8126e --- /dev/null +++ b/build/tru64_cxx.mak @@ -0,0 +1,175 @@ +# Usage: +# +# Create a new empty directory anywhere (preferably not in the boost tree). +# Copy this Makefile to that new directory and rename it to "Makefile" +# Adjust the pathnames below. +# +# make softlinks Create softlinks to source code and tests +# make Compile all sources +# make test Run doctest tests +# make clean Remove all object files +# make unlink Remove softlinks +# +# Revision history: +# 12 Apr 01 new macro ROOT to simplify configuration (R.W. Grosse-Kunstleve) +# Initial version: R.W. Grosse-Kunstleve + +ROOT=$(HOME) +BOOST=$(ROOT)/boost + +PYEXE=/usr/local/Python-1.5.2/bin/python +PYINC=-I/usr/local/Python-1.5.2/include/python1.5 +#PYEXE=/usr/local/Python-2.0/bin/python +#PYINC=-I/usr/local/Python-2.0/include/python2.0 +#STLPORTINC=-I/usr/local/STLport-4.1b3/stlport +#STLPORTINC=-I/usr/local/STLport-4.1b4/stlport +#STLPORTOPTS= \ +# -D__USE_STD_IOSTREAM \ +# -D__STL_NO_SGI_IOSTREAMS \ +# -D__STL_USE_NATIVE_STRING \ +# -D__STL_NO_NEW_C_HEADERS \ +# -D_RWSTD_COMPILE_INSTANTIATE=1 +STLPORTINC=-I$(BOOST)/boost/compatibility/cpp_c_headers + +STDOPTS=-std strict_ansi +# use -msg_display_number to obtain integer tags for -msg_disable +WARNOPTS=-msg_disable 186,450,1115 +OPTOPTS=-g + +CPP=cxx +CPPOPTS=$(STLPORTINC) $(STLPORTOPTS) -I$(BOOST) $(PYINC) \ + $(STDOPTS) $(WARNOPTS) $(OPTOPTS) +MAKEDEP=-Em + +LD=cxx +LDOPTS=-shared -expect_unresolved 'Py*' -expect_unresolved '_Py*' + +#HIDDEN=-hidden + +OBJ=classes.o conversions.o extension_class.o functions.o \ + init_function.o module_builder.o \ + objects.o types.o cross_module.o +DEPOBJ=$(OBJ) \ + comprehensive.o \ + abstract.o \ + getting_started1.o getting_started2.o \ + simple_vector.o \ + do_it_yourself_converters.o \ + pickle1.o pickle2.o pickle3.o \ + noncopyable_export.o noncopyable_import.o \ + ivect.o dvect.o + +.SUFFIXES: .o .cpp + +all: libboost_python.a \ + boost_python_test.so \ + abstract.so \ + getting_started1.so getting_started2.so \ + simple_vector.so \ + do_it_yourself_converters.so \ + pickle1.so pickle2.so pickle3.so \ + noncopyable_export.so noncopyable_import.so \ + ivect.so dvect.so + +libboost_python.a: $(OBJ) + rm -f libboost_python.a + cd cxx_repository; \ + ls -1 > ../libboost_python.a.input; \ + ar r ../libboost_python.a -input ../libboost_python.a.input + rm -f libboost_python.a.input + ar r libboost_python.a $(OBJ) + +boost_python_test.so: $(OBJ) comprehensive.o + $(LD) $(LDOPTS) $(OBJ) comprehensive.o -o boost_python_test.so -lm + +abstract.so: $(OBJ) abstract.o + $(LD) $(LDOPTS) $(OBJ) abstract.o -o abstract.so + +getting_started1.so: $(OBJ) getting_started1.o + $(LD) $(LDOPTS) $(OBJ) getting_started1.o -o getting_started1.so + +getting_started2.so: $(OBJ) getting_started2.o + $(LD) $(LDOPTS) $(OBJ) getting_started2.o -o getting_started2.so + +simple_vector.so: $(OBJ) simple_vector.o + $(LD) $(LDOPTS) $(OBJ) simple_vector.o -o simple_vector.so + +do_it_yourself_converters.so: $(OBJ) do_it_yourself_converters.o + $(LD) $(LDOPTS) $(OBJ) do_it_yourself_converters.o -o do_it_yourself_converters.so + +pickle1.so: $(OBJ) pickle1.o + $(LD) $(LDOPTS) $(OBJ) pickle1.o -o pickle1.so + +pickle2.so: $(OBJ) pickle2.o + $(LD) $(LDOPTS) $(OBJ) pickle2.o -o pickle2.so + +pickle3.so: $(OBJ) pickle3.o + $(LD) $(LDOPTS) $(OBJ) pickle3.o -o pickle3.so + +noncopyable_export.so: $(OBJ) noncopyable_export.o + $(LD) $(LDOPTS) $(OBJ) $(HIDDEN) \ + noncopyable_export.o -o noncopyable_export.so + +noncopyable_import.so: $(OBJ) noncopyable_import.o + $(LD) $(LDOPTS) $(OBJ) $(HIDDEN) \ + noncopyable_import.o -o noncopyable_import.so + +ivect.so: $(OBJ) ivect.o + $(LD) $(LDOPTS) $(OBJ) $(HIDDEN) ivect.o -o ivect.so + +dvect.so: $(OBJ) dvect.o + $(LD) $(LDOPTS) $(OBJ) $(HIDDEN) dvect.o -o dvect.so + +.cpp.o: + $(CPP) $(CPPOPTS) -c $*.cpp + +test: + $(PYEXE) comprehensive.py + $(PYEXE) test_abstract.py + $(PYEXE) test_getting_started1.py + $(PYEXE) test_getting_started2.py + $(PYEXE) test_simple_vector.py + $(PYEXE) test_do_it_yourself_converters.py + $(PYEXE) test_pickle1.py + $(PYEXE) test_pickle2.py + $(PYEXE) test_pickle3.py + $(PYEXE) test_cross_module.py + +clean: + rm -f $(OBJ) libboost_python.a libboost_python.a.input + rm -f comprehensive.o boost_python_test.so + rm -f abstract.o abstract.so + rm -f getting_started1.o getting_started1.so + rm -f getting_started2.o getting_started2.so + rm -f simple_vector.o simple_vector.so + rm -f do_it_yourself_converters.o do_it_yourself_converters.so + rm -f pickle1.o pickle1.so + rm -f pickle2.o pickle2.so + rm -f pickle3.o pickle3.so + rm -f noncopyable_export.o noncopyable_export.so + rm -f noncopyable_import.o noncopyable_import.so + rm -f ivect.o ivect.so + rm -f dvect.o dvect.so + rm -f so_locations *.pyc + rm -rf cxx_repository + +softlinks: + $(PYEXE) $(BOOST)/libs/python/build/filemgr.py $(BOOST) softlinks + +unlink: + $(PYEXE) $(BOOST)/libs/python/build/filemgr.py $(BOOST) unlink + +cp: + $(PYEXE) $(BOOST)/libs/python/build/filemgr.py $(BOOST) cp + +rm: + $(PYEXE) $(BOOST)/libs/python/build/filemgr.py $(BOOST) rm + +depend: + @ cat Makefile.nodepend; \ + for obj in $(DEPOBJ); \ + do \ + bn=`echo "$$obj" | cut -d. -f1`; \ + $(CPP) $(CPPOPTS) $(MAKEDEP) "$$bn".cpp; \ + done + diff --git a/build/vc60.mak b/build/vc60.mak new file mode 100644 index 00000000..f0016075 --- /dev/null +++ b/build/vc60.mak @@ -0,0 +1,129 @@ +# Usage: +# +# make copy Copy the sources and tests +# make Compile all sources +# make test Run doctest tests +# make clean Remove all object files +# make del Remove the sources and tests +# +# Revision history: +# 12 Apr 01 new macro ROOT to simplify configuration (R.W. Grosse-Kunstleve) +# Initial version: R.W. Grosse-Kunstleve + +ROOT=L: +BOOST_WIN="$(ROOT)\boost" +BOOST_UNIX=$(HOME)/boost + +PYEXE="C:\Program files\Python\python.exe" +PYINC=/I"C:\Program files\Python\include" +PYLIB="C:\Program files\Python\libs\python15.lib" + +STDOPTS=/nologo /MD /GR /GX /Zm200 +WARNOPTS= +OPTOPTS= + +CPP=cl.exe +CPPOPTS=$(STLPORTINC) $(STLPORTOPTS) /I$(BOOST_WIN) $(PYINC) \ + $(STDOPTS) $(WARNOPTS) $(OPTOPTS) + +LD=link.exe +LDOPTS=/nologo /dll /incremental:no + +OBJ=classes.obj conversions.obj extension_class.obj functions.obj \ + init_function.obj module_builder.obj \ + objects.obj types.obj cross_module.obj + +.SUFFIXES: .obj .cpp + +all: boost_python.lib \ + boost_python_test.pyd \ + abstract.pyd \ + getting_started1.pyd getting_started2.pyd \ + simple_vector.pyd \ + do_it_yourself_converters.pyd \ + pickle1.pyd pickle2.pyd pickle3.pyd \ + noncopyable_export.pyd noncopyable_import.pyd \ + ivect.pyd dvect.pyd + +boost_python.lib: $(OBJ) + $(LD) -lib /nologo /out:boost_python.lib $(OBJ) + +boost_python_test.pyd: $(OBJ) comprehensive.obj + $(LD) $(LDOPTS) $(OBJ) comprehensive.obj $(PYLIB) /export:initboost_python_test /out:"boost_python_test.pyd" + +abstract.pyd: $(OBJ) abstract.obj + $(LD) $(LDOPTS) $(OBJ) abstract.obj $(PYLIB) /export:initabstract /out:"abstract.pyd" + +getting_started1.pyd: $(OBJ) getting_started1.obj + $(LD) $(LDOPTS) $(OBJ) getting_started1.obj $(PYLIB) /export:initgetting_started1 /out:"getting_started1.pyd" + +getting_started2.pyd: $(OBJ) getting_started2.obj + $(LD) $(LDOPTS) $(OBJ) getting_started2.obj $(PYLIB) /export:initgetting_started2 /out:"getting_started2.pyd" + +simple_vector.pyd: $(OBJ) simple_vector.obj + $(LD) $(LDOPTS) $(OBJ) simple_vector.obj $(PYLIB) /export:initsimple_vector /out:"simple_vector.pyd" + +do_it_yourself_converters.pyd: $(OBJ) do_it_yourself_converters.obj + $(LD) $(LDOPTS) $(OBJ) do_it_yourself_converters.obj $(PYLIB) /export:initdo_it_yourself_converters /out:"do_it_yourself_converters.pyd" + +pickle1.pyd: $(OBJ) pickle1.obj + $(LD) $(LDOPTS) $(OBJ) pickle1.obj $(PYLIB) /export:initpickle1 /out:"pickle1.pyd" + +pickle2.pyd: $(OBJ) pickle2.obj + $(LD) $(LDOPTS) $(OBJ) pickle2.obj $(PYLIB) /export:initpickle2 /out:"pickle2.pyd" + +pickle3.pyd: $(OBJ) pickle3.obj + $(LD) $(LDOPTS) $(OBJ) pickle3.obj $(PYLIB) /export:initpickle3 /out:"pickle3.pyd" + +noncopyable_export.pyd: $(OBJ) noncopyable_export.obj + $(LD) $(LDOPTS) $(OBJ) noncopyable_export.obj $(PYLIB) /export:initnoncopyable_export /out:"noncopyable_export.pyd" + +noncopyable_import.pyd: $(OBJ) noncopyable_import.obj + $(LD) $(LDOPTS) $(OBJ) noncopyable_import.obj $(PYLIB) /export:initnoncopyable_import /out:"noncopyable_import.pyd" + +ivect.pyd: $(OBJ) ivect.obj + $(LD) $(LDOPTS) $(OBJ) ivect.obj $(PYLIB) /export:initivect /out:"ivect.pyd" + +dvect.pyd: $(OBJ) dvect.obj + $(LD) $(LDOPTS) $(OBJ) dvect.obj $(PYLIB) /export:initdvect /out:"dvect.pyd" + +.cpp.obj: + $(CPP) $(CPPOPTS) /c $*.cpp + +test: + $(PYEXE) comprehensive.py --broken-auto-ptr + $(PYEXE) test_abstract.py + $(PYEXE) test_getting_started1.py + $(PYEXE) test_getting_started2.py + $(PYEXE) test_simple_vector.py + $(PYEXE) test_do_it_yourself_converters.py + $(PYEXE) test_pickle1.py + $(PYEXE) test_pickle2.py + $(PYEXE) test_pickle3.py + $(PYEXE) test_cross_module.py --broken-auto-ptr + +clean: + del *.obj + del *.lib + del *.exp + del *.idb + del *.pyd + del *.pyc + +softlinks: + python $(BOOST_UNIX)/libs/python/build/filemgr.py $(BOOST_UNIX) softlinks + +unlink: + python $(BOOST_UNIX)/libs/python/build/filemgr.py $(BOOST_UNIX) unlink + +cp: + python $(BOOST_UNIX)/libs/python/build/filemgr.py $(BOOST_UNIX) cp + +rm: + python $(BOOST_UNIX)/libs/python/build/filemgr.py $(BOOST_UNIX) rm + +copy: + $(PYEXE) $(BOOST_WIN)\libs\python\build\filemgr.py $(BOOST_WIN) copy + +del: + $(PYEXE) $(BOOST_WIN)\libs\python\build\filemgr.py $(BOOST_WIN) del diff --git a/doc/#index.html# b/doc/#index.html# deleted file mode 100644 index ff5946a8..00000000 --- a/doc/#index.html# +++ /dev/null @@ -1,212 +0,0 @@ - - - - py_cpp Python/C++ binding documentation - -

- c++boost.gif (8819 bytes) py_cpp* -

- -

- The source code for py_cpp, including a MSVC demo project is available here. - -

Synopsis

-

- py_cpp is a system for quickly and easily interfacing C++ code with Python such that the Python interface is - very similar to the C++ interface. It is designed to be minimally - intrusive on your C++ design. In most cases, you should not have to alter - your C++ classes in any way in order to use them with py_cpp. The system - should simply “reflect” your C++ classes and functions into - Python. The major features of py_cpp include support for: -

-among others. - - -

Supported Platforms

-

py_cpp has been tested in the following configurations: - -

- -

Py_cpp requires the boost libraries, and is - has been accepted for inclusion into the boost libraries pending “boostification“ - (completion of the documentation, change in some naming conventions and - resolution of some namespace issues). - -

Credits

- - -

Table of Contents

- -
    -
  1. A Brief Introduction to writing Python - extension modules - -
  2. Comparisons between py_cpp and other - systems for extending Python - -
  3. A Simple Example Using py_cpp - -
  4. Overridable Virtual Functions - -
  5. Function Overloading - -
  6. Inheritance - -
  7. Special Method and Operator Support - -
  8. A Peek Under the Hood - -
  9. Building a Module with Py_cpp - -
  10. Advanced Topics - -
      -
    1. class_builder<> - -
    2. enums - -
    3. References - -
    4. Pointers and Smart Pointers - -
    5. Built-in Python Types - -
    6. Other Extension Types - -
    7. Templates -
    - -
- -

- More sophisticated examples are given in - extclass_demo.cpp, extclass_demo.h, and - test_extclass.py in the source code - archive. There's much more here, and much more documentation to - come... -

- Questions should be directed to the boost mailing list. - -

Naming Contest

- -

- Yes, I know py_cpp is a lousy name. Problem is, the best names my puny - imagination can muster (IDLE and GRAIL) are taken, so I'm holding a - naming contest. First prize? You get to pick the name<0.2wink> and - you will be credited in the documentation. Names that have been suggested - so far include: -

- Please post or send me your suggestions!
-
- -

- © Copyright David Abrahams 2000. Permission to copy, use, modify, - sell and distribute this document is granted provided this copyright - notice appears in all copies. This document is provided “as is” without - express or implied warranty, and with no claim as to its suitability for - any purpose. -

- Updated: Nov 26, 2000 - - diff --git a/doc/building.html b/doc/building.html index 9c0452e7..f9458c42 100644 --- a/doc/building.html +++ b/doc/building.html @@ -1,43 +1,180 @@ - - - Building a Module with Py_cpp - -

-

- c++boost.gif (8819 bytes)Building a Module with Py_cpp -

-

- Right now, the only supported configuration is one in which the py_cpp - source files are statically linked with the source for your extension - module. You may first build them into a library and link it with your - extension module source, but the effect is the same as compiling all - the source files together. Some users have successfully built the - py_cpp sources into a shared library, and support for a shared library - build is planned, but not yet implemented. The py_cpp source files are: -

-
-extclass.cpp
-functions.cpp
-init_function.cpp
-module.cpp
-newtypes.cpp
-objects.cpp
-py.cpp
-subclass.cpp
-         
-
-

- Previous: A Peek Under the Hood - Up: Top -

- © Copyright David Abrahams 2000. Permission to copy, use, modify, - sell and distribute this document is granted provided this copyright - notice appears in all copies. This document is provided “as - is” without express or implied warranty, and with no claim as to - its suitability for any purpose. -

- Updated: Nov 26, 2000 -

+ + + + Building an Extension Module + +
+

c++boost.gif (8819 bytes)Building an + Extension Module

+ +

The build process for Boost is currently undergoing some evolution, + and, it is to be hoped, improvement. The following facts may help: + +


+ Makefiles for various platforms and a Visual Studio project + reside in the Boost subdirectory libs/python/build. + Build targets include: + +
    +
  • The boost_python library for static linking with your + extension module. On the various Unices, this library will be + called libboost_python.a. When using Visual C++, the + library will be called boost_python.lib. + +

    +

  • A comprehensive test of Boost.Python features. This test builds + a Boost.Python extension module, then runs Python to import the + module, and runs a series of tests on it using doctest. Source code for the module + and tests is available in the Boost subdirectory + libs/python/test. + +

    +

  • Various examples from the Boost subdirectory + libs/python/example. + All these examples include a doctest modeled + on the comprehensive test above. + +
+ +
+ There is a group of makefiles with support for simultaneous + compilation on multiple platforms and a consistent set of + features that build the boost_python library for static + linking, the comprehensive test, and all examples in + libs/python/example: + + + Usage of these makefiles is described here. + +
+ There is another group of makefiles for GNU make. + These makefiles are less redundant than the makefiles + in the group above, + but the list of compilation targets is not as complete + and there is no support for simultaneous compilation + on multiple platforms. + + + +
+ A project workspace for Microsoft Visual Studio is provided at libs/python/build/build.dsw. The + include paths for this project may need to be changed for your + installation. They currently assume that python has been installed at + c:\tools\python. Three configurations of all targets are + supported: + +
    +
  • Release (optimization, -DNDEBUG) + +
  • Debug (no optimization -D_DEBUG) + +
  • DebugPython (no optimization, -D_DEBUG + -DBOOST_DEBUG_PYTHON) +
+ +

When extension modules are built with Visual C++ using + -D_DEBUG, Python defaults to force linking with a + special debugging version of the Python DLL. Since this debug DLL + isn't supplied with the default Python installation for Windows, + Boost.Python uses boost/python/detail/wrap_python.hpp + to temporarily undefine _DEBUG when Python.h is + #included. + +

If you want the extra runtime checks available with the debugging + version of the library, #define BOOST_DEBUG_PYTHON to + re-enable library forcing, and link with the DebugPython version of + boost_python.lib. You'll need to get the debugging version + of the Python executable (python_d.exe) and DLL + (python20_d.dll or python15_d.dll). The Python + sources include project files for building these. If you download them, change the name of the + top-level directory to src, and install it under + c:\tools\python, the workspace supplied by Boost.Python will + be able to use it without modification. Just open + c:\tools\python\src\pcbuild\pcbuild.dsw and invoke "build + all" to generate all the debugging targets. + +

If you do not #define BOOST_DEBUG_PYTHON, be sure that + any source files #include <boost/python/detail/wrap_python.hpp> + instead of the usual Python.h, or you will have link + incompatibilities.
+ +


+ If your platform isn't directly supported, you can build a static + library from the following source files (in the Boost subdirectory + libs/python/src), or compile them directly and link the + resulting objects into your extension module: + + + +
+ Next: Wrapping Enums Previous: A Peek Under the Hood Up: Top + +
+

© Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided ``as is'' without + express or implied warranty, and with no claim as to its suitability for + any purpose. + +

Updated: Apr 17, 2001 (R.W. Grosse-Kunstleve) +

diff --git a/doc/comparisons.html b/doc/comparisons.html index d0c31d9f..57cec744 100644 --- a/doc/comparisons.html +++ b/doc/comparisons.html @@ -6,20 +6,23 @@

c++boost.gif (8819 bytes)Comparisons with + src="../../../c++boost.gif" alt= "c++boost.gif (8819 bytes)">
+ Comparisons with Other Systems

CXX

- Like py_cpp, CXX attempts to - provide a C++-oriented interface to Python. In most cases, like py_cpp, - it relieves the user from worrying about reference-counts. As far as I - can tell, there is no support for subclassing C++ extension types in - Python. An even more-significant difference is that a user's C++ code is - still basically “dealing with Python objects”, though they are wrapped - in C++ classes. This means such jobs as argument parsing and conversion - are still left to be done explicitly by the user. + Like Boost.Python, CXX attempts to + provide a C++-oriented interface to Python. In most cases, as with the + boost library, it relieves the user from worrying about + reference-counts. Both libraries automatically convert thrown C++ + exceptions into Python exceptions. As far as I can tell, CXX has no + support for subclassing C++ extension types in Python. An even + more significant difference is that a user's C++ code is still basically + ``dealing with Python objects'', though they are wrapped in + C++ classes. This means such jobs as argument parsing and conversion are + still left to be done explicitly by the user.

CXX claims to interoperate well with the C++ Standard Library @@ -38,11 +41,15 @@

As far as I can tell, CXX enables one to write what is essentially idiomatic Python code in C++, manipulating Python objects through the - same fully-generic interfaces we use in Python. I think it would be fair - to say that while you're not programming directly to the “bare - metal” with CXX, in comparison to py_cpp, it presents a low-level - interface to Python. That use is also supported by the py_cpp object - wrappers. + same fully-generic interfaces we use in Python. While you're hardly + programming directly to the ``bare metal'' with CXX, it basically + presents a ``C++-ized'' version of the Python 'C' API. Some fraction of + that capability is available in Boost.Python through boost/python/objects.hpp, + which provides C++ objects corresponding to Python lists, tuples, + strings, and dictionaries, and through boost/python/callback.hpp, + which allows you to call back into python with C++ arguments.

Paul F. Dubois, the original @@ -51,11 +58,11 @@ fill in the other half. Here is his response to the commentary above:

-“My intention with CXX was not to do what you are doing. It was to enable a +``My intention with CXX was not to do what you are doing. It was to enable a person to write an extension directly in C++ rather than C. I figured others had the wrapping business covered. I thought maybe CXX would provide an easier target language for those making wrappers, but I never explored -that.”
-Paul Dubois +that.''
-Paul Dubois

SWIG

@@ -65,28 +72,28 @@ that.”
-Paul Dubois languages. Swig relies on a parser to read your source code and produce additional source code files which can be compiled into a Python (or Perl or Tcl) extension module. It has been successfully used to create - many Python extension modules. Like py_cpp, SWIG is trying to allow an + many Python extension modules. Like Boost.Python, SWIG is trying to allow an existing interface to be wrapped with little or no change to the - existing code. The documentation says “SWIG parses a form of ANSI C + existing code. The documentation says ``SWIG parses a form of ANSI C syntax that has been extended with a number of special directives. As a result, interfaces are usually built by grabbing a header file and - tweaking it a little bit.” For C++ interfaces, the tweaking has often + tweaking it a little bit.'' For C++ interfaces, the tweaking has often proven to amount to more than just a little bit. One user writes: -
“The problem with swig (when I used it) is that it +
``The problem with swig (when I used it) is that it couldnt handle templates, didnt do func overloading properly etc. For ANSI C libraries this was fine. But for usual C++ code this was a problem. Simple things work. But for anything very complicated (or - realistic), one had to write code by hand. I believe py_cpp doesn't have + realistic), one had to write code by hand. I believe Boost.Python doesn't have this problem[sic]... IMHO overloaded functions are very important to - wrap correctly.”
-Prabhu Ramachandran + wrap correctly.''
-Prabhu Ramachandran

- By contrast, py_cpp doesn't attempt to parse C++ - the problem is simply + By contrast, Boost.Python doesn't attempt to parse C++ - the problem is simply too complex to do correctly. Technically, one does - write code by hand to use py_cpp. The goal, however, has been to make + write code by hand to use Boost.Python. The goal, however, has been to make that code nearly as simple as listing the names of the classes and member functions you want to expose in Python. @@ -95,7 +102,7 @@ that.”
-Paul Dubois SIP is a system similar to SWIG, though seemingly more - C++-oriented. The author says that like py_cpp, SIP supports overriding + C++-oriented. The author says that like Boost.Python, SIP supports overriding extension class member functions in Python subclasses. It appears to have been designed specifically to directly support some features of PyQt/PyKDE, which is its primary client. Documentation is almost @@ -105,15 +112,15 @@ that.”
-Paul Dubois

ILU

ILU + href="ftp://ftp.parc.xerox.com/pub/ilu/ilu.html">ILU is a very ambitious project which tries to describe a module's interface (types and functions) in terms of an Interface + href="ftp://ftp.parc.xerox.com/pub/ilu/2.0b1/manual-html/manual_2.html">Interface Specification Language (ISL) so that it can be uniformly interfaced to a wide range of computer languages, including Common Lisp, C++, C, Modula-3, and Python. ILU can parse the ISL to generate a C++ language header file describing the interface, of which the user is expected to - provide an implementation. Unlike py_cpp, this means that the system + provide an implementation. Unlike Boost.Python, this means that the system imposes implementation details on your C++ code at the deepest level. It is worth noting that some of the C++ names generated by ILU are supposed to be reserved to the C++ implementation. It is unclear from the @@ -124,7 +131,7 @@ that.”
-Paul Dubois GRAD is another very ambitious project aimed at generating Python wrappers for - interfaces written in “legacy languages”, among which C++ is the first one + interfaces written in ``legacy languages'', among which C++ is the first one implemented. Like SWIG, it aims to parse source code and automatically generate wrappers, though it appears to take a more sophisticated approach to parsing in general and C++ in particular, so it should do a much better @@ -148,73 +155,77 @@ an inheritance relationship?

Zope ExtensionClasses

- ExtensionClasses in Zope use the same underlying mechanism as py_cpp + ExtensionClasses in Zope use the same underlying mechanism as Boost.Python to support subclassing of extension types in Python, including multiple-inheritance. Both systems support pickling/unpickling of extension class instances in very similar ways. Both systems rely on the - same “Don - Beaudry Hack” that also inspired Don's MESS System. + Beaudry Hack'' that also inspired Don's MESS System.

The major differences are:

    +
  • Zope is entirely 'C' language-based. It doesn't require a C++ + compiler, so it's much more portable than Boost.Python, which stresses + the limits of even some modern C++ implementations. +
  • - py_cpp lifts the burden on the user to parse and convert function + Boost.Python lifts the burden on the user to parse and convert function argument types. Zope provides no such facility.
  • - py_cpp lifts the burden on the user to maintain Python + Boost.Python lifts the burden on the user to maintain Python reference-counts.
  • - py_cpp supports function overloading; Zope does not. + Boost.Python supports function overloading; Zope does not.
  • - py_cpp supplies a simple mechanism for exposing read-only and + Boost.Python supplies a simple mechanism for exposing read-only and read/write access to data members of the wrapped C++ type as Python attributes.
  • Writing a Zope ExtensionClass is significantly more complex than - exposing a C++ class to python using py_cpp (mostly a summary of the + exposing a C++ class to python using Boost.Python (mostly a summary of the previous 4 items). A Zope Example illustrates the differences.
  • - Zope's ExtensionClasses are specifically motivated by “the need for a - C-based persistence mechanism”. Py_cpp's are motivated by the desire + Zope's ExtensionClasses are specifically motivated by ``the need for a + C-based persistence mechanism''. Boost.Python's are motivated by the desire to simply reflect a C++ API into Python with as little modification as possible.
  • - The following Zope restriction does not apply to py_cpp: “At most one + The following Zope restriction does not apply to Boost.Python: ``At most one base extension direct or indirect super class may define C data members. If an extension subclass inherits from multiple base extension classes, then all but one must be mix-in classes that - provide extension methods but no data.” + provide extension methods but no data.''
  • Zope requires use of the somewhat funky inheritedAttribute (search for - “inheritedAttribute” on this page) - method to access base class methods. In py_cpp, base class methods can + method to access base class methods. In Boost.Python, base class methods can be accessed in the usual way by writing - “BaseClass.method”. + ``BaseClass.method''.
  • Zope supplies some creative but esoteric idioms such as - Acquisition. No specific support for this is built into py_cpp. + Acquisition. No specific support for this is built into Boost.Python.
  • Zope's ComputedAttribute support is designed to be used from Python. The analogous feature of - py_cpp can be used from C++ or Python. The feature is arguably - easier to use in py_cpp. + Boost.Python can be used from C++ or Python. The feature is arguably + easier to use in Boost.Python.

+ Next: A Simple Example Using Boost.Python Previous: A Brief Introduction to writing Python Extension Modules - Next: A Simple Example Using py_cpp Up: Top

© Copyright David Abrahams 2000. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright - notice appears in all copies. This document is provided “as is” without + notice appears in all copies. This document is provided ``as is'' without express or implied warranty, and with no claim as to its suitability for any purpose.

- Updated: Nov 26, 2000 + Updated: Mar 6, 2001

diff --git a/doc/cross_module.html b/doc/cross_module.html new file mode 100644 index 00000000..08c39bfe --- /dev/null +++ b/doc/cross_module.html @@ -0,0 +1,336 @@ + + +Cross-extension-module dependencies + +
+ +c++boost.gif (8819 bytes) + +
+

Cross-extension-module dependencies

+ +It is good programming practice to organize large projects as modules +that interact with each other via well defined interfaces. With +Boost.Python it is possible to reflect this organization at the C++ +level at the Python level. This is, each logical C++ module can be +organized as a separate Python extension module. + +

+At first sight this might seem natural and straightforward. However, it +is a fairly complex problem to establish cross-extension-module +dependencies while maintaining the same ease of use Boost.Python +provides for classes that are wrapped in the same extension module. To +a large extent this complexity can be hidden from the author of a +Boost.Python extension module, but not entirely. + +


+

The recipe

+ +Suppose there is an extension module that exposes certain instances of +the C++ std::vector template library such that it can be used +from Python in the following manner: + +
+import std_vector
+v = std_vector.double([1, 2, 3, 4])
+v.push_back(5)
+v.size()
+
+ +Suppose the std_vector module is done well and reflects all +C++ functions that are useful at the Python level, for all C++ built-in +data types (std_vector.int, std_vector.long, etc.). + +

+Suppose further that there is statistic module with a C++ class that +has constructors or member functions that use or return a +std::vector. For example: + +

+class xy {
+  public:
+    xy(const std::vector<double>& x, const std::vector<double>& y) : m_x(x), m_y(y) {}
+    const std::vector<double>& x() const { return m_x; }
+    const std::vector<double>& y() const { return m_y; }
+    double correlation();
+  private:
+    std::vector<double> m_x;
+    std::vector<double> m_y;
+}
+
+ +What is more natural than reusing the std_vector extension +module to expose these constructors or functions to Python? + +

+Unfortunately, what seems natural needs a little work in both the +std_vector and the statistics module. + +

+In the std_vector extension module, +std::vector<double> is exposed to Python in the usual +way with the class_builder<> template. To also enable the +automatic conversion of std::vector<double> function +arguments or return values in other Boost.Python C++ modules, the +converters that convert a std::vector<double> C++ object +to a Python object and vice versa (i.e. the to_python() and +from_python() template functions) have to be exported. For +example: + +

+  #include <boost/python/cross_module.hpp>
+  //...
+  class_builder<std::vector<double> > v_double(std_vector_module, "double");
+  export_converters(v_double);
+
+ +In the extension module that wraps class xy we can now import +these converters with the import_converters<> template. +For example: + +
+  #include <boost/python/cross_module.hpp>
+  //...
+  import_converters<std::vector<double> > v_double_converters("std_vector", "double");
+
+ +That is all. All the attributes that are defined for +std_vector.double in the std_vector Boost.Python +module will be available for the returned objects of xy.x() +and xy.y(). Similarly, the constructor for xy will +accept objects that were created by the std_vectormodule. + +
+

Placement of import_converters<> template instantiations

+ +import_converts<> can be viewed as a drop-in replacement +for class_wrapper<>, and the recommendations for the +placement of class_wrapper<> template instantiations +also apply to to import_converts<>. In particular, it is +important that an instantiation of class_wrapper<> is +visible to any code which wraps a C++ function with a T, +T*, const T&, etc. parameter or return value. +Therefore you may want to group all class_wrapper<> and +import_converts<> instantiations at the top of your +module's init function, then def() the member functions later +to avoid problems with inter-class dependencies. + +
+

Non-copyable types

+ +export_converters() instantiates C++ template functions that +invoke the copy constructor of the wrapped type. For a type that is +non-copyable this will result in compile-time error messages. In such a +case, export_converters_noncopyable() can be used to export +the converters that do not involve the copy constructor of the wrapped +type. For example: + +
+class_builder<store> py_store(your_module, "store");
+export_converters_noncopyable(py_store);
+
+ +The corresponding import_converters<> statement does not +need any special attention: + +
+import_converters<store> py_store("noncopyable_export", "store");
+
+ +
+

Python module search path

+ +The std_vector and statistics modules can now be used +in the following way: + +
+import std_vector
+import statistics
+x = std_vector.double([1, 2, 3, 4])
+y = std_vector.double([2, 4, 6, 8])
+xy = statistics.xy(x, y)
+xy.correlation()
+
+ +In this example it is clear that Python has to be able to find both the +std_vector and the statistics extension module. In +other words, both extension modules need to be in the Python module +search path (sys.path). + +

+The situation is not always this obvious. Suppose the +statistics module has a random() function that +returns a vector of random numbers with a given length: + +

+import statistics
+x = statistics.random(5)
+y = statistics.random(5)
+xy = statistics.xy(x, y)
+xy.correlation()
+
+ +A naive user will not easily anticipate that the std_vector +module is used to pass the x and y vectors around. If +the std_vector module is in the Python module search path, +this form of ignorance is of no harm. On the contrary, we are glad +that we do not have to bother the user with details like this. + +

+If the std_vector module is not in the Python module search +path, a Python exception will be raised: + +

+Traceback (innermost last):
+  File "foo.py", line 2, in ?
+    x = statistics.random(5)
+ImportError: No module named std_vector
+
+ +As is the case with any system of a non-trivial complexity, it is +important that the setup is consistent and complete. + +
+

Two-way module dependencies

+ +Boost.Python supports two-way module dependencies. This is best +illustrated by a simple example. + +

+Suppose there is a module ivect that implements vectors of +integers, and a similar module dvect that implements vectors +of doubles. We want to be able do convert an integer vector to a double +vector and vice versa. For example: + +

+import ivect
+iv = ivect.ivect((1,2,3,4,5))
+dv = iv.as_dvect()
+
+ +The last expression will implicitly import the dvect module in +order to enable the conversion of the C++ representation of +dvect to a Python object. The analogous is possible for a +dvect: + +
+import dvect
+dv = dvect.dvect((1,2,3,4,5))
+iv = dv.as_ivect()
+
+ +Now the ivect module is imported implicitly. + +

+Note that the two-way dependencies are possible because the +dependencies are resolved only when needed. This is, the initialization +of the ivect module does not rely on the dvect +module, and vice versa. Only if as_dvect() or +as_ivect() is actually invoked will the corresponding module +be implicitly imported. This also means that, for example, the +dvect module does not have to be available at all if +as_dvect() is never used. + +


+

Clarification of compile-time and link-time dependencies

+ +Boost.Python's support for resolving cross-module dependencies at +runtime does not imply that compile-time dependencies are eliminated. +For example, the statistics extension module in the example above will +need to #include <vector>. This is immediately obvious +from the definition of class xy. + +

+If a library is wrapped that consists of both header files and compiled +components (e.g. libdvect.a, dvect.lib, etc.), both +the Boost.Python extension module with the +export_converters() statement and the module with the +import_converters<> statement need to be linked against +the object library. Ideally one would build a shared library (e.g. +libdvect.so, dvect.dll, etc.). However, this +introduces the issue of having to configure the search path for the +dynamic loading correctly. For small libraries it is therefore often +more convenient to ignore the fact that the object files are loaded +into memory more than once. + +


+

Summary of motivation for cross-module support

+ +The main purpose of Boost.Python's cross-module support is to allow for +a modular system layout. With this support it is straightforward to +reflect C++ code organization at the Python level. Without the +cross-module support, a multi-purpose module like std_vector +would be impractical because the entire wrapper code would somehow have +to be duplicated in all extension modules that use it, making them +harder to maintain and harder to build. + +

+Another motivation for the cross-module support is that two extension +modules that wrap the same class cannot both be imported into Python. +For example, if there are two modules A and B that +both wrap a given class X, this will work: + +

+import A
+x = A.X()
+
+ +This will also work: + +
+import B
+x = B.X()
+
+ +However, this will fail: + +
+import A
+import B
+python: /net/cci/rwgk/boost/boost/python/detail/extension_class.hpp:866:
+static void boost::python::detail::class_registry<X>::register_class(boost::python::detail::extension_class_base *):
+Assertion `static_class_object == 0' failed.
+Abort
+
+ +A good solution is to wrap class X only once. Depending on the +situation, this could be done by module A or B, or an +additional small extension module that only wraps and exports +class X. + +

+Finally, there can be important psychological or political reasons for +using the cross-module support. If a group of classes is lumped +together with many others in a huge module, the authors will have +difficulties in being identified with their work. The situation is +much more transparent if the work is represented by a module with a +recognizable name. This is not just a question of strong egos, but also +of getting credit and funding. + +


+

Why not use export_converters() universally?

+ +There is some overhead associated with the Boost.Python cross-module +support. Depending on the platform, the size of the code generated by +export_converters() is roughly 10%-20% of that generated +by class_builder<>. For a large extension module with +many wrapped classes, this could mean a significant difference. +Therefore the general recommendation is to use +export_converters() only for classes that are likely to +be used as function arguments or return values in other modules. + +
+© Copyright Ralf W. Grosse-Kunstleve 2001. Permission to copy, +use, modify, sell and distribute this document is granted provided this +copyright notice appears in all copies. This document is provided "as +is" without express or implied warranty, and with no claim as to its +suitability for any purpose. + +

+Updated: April 2001 + +

diff --git a/doc/data_structures.txt b/doc/data_structures.txt new file mode 100644 index 00000000..90e41b91 --- /dev/null +++ b/doc/data_structures.txt @@ -0,0 +1,192 @@ +Given a real Python class 'A', a wrapped C++ class 'B', and this definition: + + class C(A, B): + def __init__(self): + B.__init__(self) + self.x = 1 + ... + + c = C() + +this diagram describes the internal structure of an instance of 'C', including +its inheritance relationships. Note that ExtensionClass is derived from +Class, and is in fact identical for all intents and purposes. + + MetaClass + +---------+ +---------+ +types.ClassType: | | | | + | | | | + | | | | + +---------+ +---------+ + ^ ^ ^ + PyClassObject | ExtensionClass | | + A: +------------+ | B: +------------+ | | + | ob_type -+-+ | ob_type -+-----+ | + | | ()<--+- __bases__ | | + | | | __dict__ -+->{...} | + | | 'B'<-+- __name__ | | + +------------+ +------------+ | + ^ ^ | + | | | + +-----+ +-------------+ | + | | | + | | Class | + | | C: +------------+ | + | | | ob_type -+------------+ + tuple:(*, *)<--+- __bases__ | + | __dict__ -+->{__module__, } + 'C' <-+- __name__ | + +------------+ + ^ (in case of inheritance from more than one + | extension class, this vector would contain + +---------------+ a pointer to an instance holder for the data + | of each corresponding C++ class) + | ExtensionInstance + | c: +---------------------+ std::vector + +----+- __class__ | +---+-- + | m_wrapped_objects -+->| * | ... + {'x': 1}<-+- __dict__ | +-|-+-- + +---------------------+ | InstanceValueHolder + | +--------------------------------+ + +-->| (contains a C++ instance of B) | + +--------------------------------+ + + + + + + +In our inheritance test cases in extclass_demo.cpp/test_extclass.py, we have the +following C++ inheritance hierarchy: + + +-----+ +----+ + | A1 | | A2 | + +-----+ +----+ + ^ ^ ^ ^ ^ + | | | | | + +-----+ | +---------+-----+ + | | | | + | +---+----------+ + .......!...... | | + : A_callback : +-+--+ +-+--+ + :............: | B1 | | B2 | + +----+ +----+ + ^ + | + +-------+---------+ + | | + +-+-+ ......!....... + | C | : B_callback : + +---+ :............: + + +A_callback and B_callback are used as part of the wrapping mechanism but not +represented in Python. C is also not represented in Python but is delivered +there polymorphically through a smart pointer. + +This is the data structure in Python. + + ExtensionClass + A1: +------------+ + ()<--+- __bases__ | + | __dict__ -+->{...} + +------------+ + ^ + | ExtensionInstance + | a1: +---------------------+ vec InstanceValueHolder + +---------+- __class__ | +---+ +---------------------+ + | | m_wrapped_objects -+->| *-+-->| contains A_callback | + | +---------------------+ +---+ +---------------------+ + | + | ExtensionInstance + | pa1_a1: +---------------------+ vec InstancePtrHolder,A1> + +---------+- __class__ | +---+ +---+ + | | m_wrapped_objects -+->| *-+-->| *-+-+ A1 + | +---------------------+ +---+ +---+ | +---+ + | +->| | + | ExtensionInstance +---+ + | pb1_a1: +---------------------+ vec InstancePtrHolder,A1> + +---------+- __class__ | +---+ +---+ + | | m_wrapped_objects -+->| *-+-->| *-+-+ B1 + | +---------------------+ +---+ +---+ | +---+ + | +->| | + | ExtensionInstance +---+ + | pb2_a1: +---------------------+ vec InstancePtrHolder,A1> + +---------+- __class__ | +---+ +---+ + | | m_wrapped_objects -+->| *-+-->| *-+-+ B2 + | +---------------------+ +---+ +---+ | +---+ + | +->| | + | +---+ + | ExtensionClass + | A2: +------------+ + | ()<--+- __bases__ | + | | __dict__ -+->{...} + | +------------+ + | ^ + | | ExtensionInstance + | a2: | +---------------------+ vec InstanceValueHolder + | +-+- __class__ | +---+ +-------------+ + | | | m_wrapped_objects -+->| *-+-->| contains A2 | + | | +---------------------+ +---+ +-------------+ + | | + | | ExtensionInstance + | pa2_a2: | +---------------------+ vec InstancePtrHolder,A2> + | +-+- __class__ | +---+ +---+ + | | | m_wrapped_objects -+->| *-+-->| *-+-+ A2 + | | +---------------------+ +---+ +---+ | +---+ + | | +->| | + | | ExtensionInstance +---+ + | pb1_a2: | +---------------------+ vec InstancePtrHolder,A2> + | +-+- __class__ | +---+ +---+ + | | | m_wrapped_objects -+->| *-+-->| *-+-+ B1 + | | +---------------------+ +---+ +---+ | +---+ + | | +->| | + | | +---+ + | | + | +---------------+------------------------------+ + | | | + +------+-------------------------+-|----------------------------+ | + | | | | | + | Class | | ExtensionClass | | ExtensionClass + | DA1: +------------+ | | B1: +------------+ | | B2: +------------+ +(*,)<---+- __bases__ | (*,*)<---+- __bases__ | (*,*)<---+- __bases__ | + | __dict__ -+->{...} | __dict__ -+->{...} | __dict__ -+->{...} + +------------+ +------------+ +------------+ + ^ ^ ^ + | ExtensionInstance | | + | da1: +---------------------+ | vec InstanceValueHolder + +-------+- __class__ | | +---+ +---------------------+ | + | m_wrapped_objects -+--|-->| *-+-->| contains A_callback | | + +---------------------+ | +---+ +---------------------+ | + +--------------------------------------+ | + | ExtensionInstance | + b1: | +---------------------+ vec InstanceValueHolder | + +-+- __class__ | +---+ +---------------------+ | + | | m_wrapped_objects -+->| *-+-->| contains B_callback | | + | +---------------------+ +---+ +---------------------+ | + | | + | ExtensionInstance | +pb1_b1: | +---------------------+ vec InstancePtrHolder,B1> | + +-+- __class__ | +---+ +---+ | + | | m_wrapped_objects -+->| *-+-->| *-+-+ B1 | + | +---------------------+ +---+ +---+ | +---+ | + | +->| | | + | ExtensionInstance +---+ | + pc_b1: | +---------------------+ vec InstancePtrHolder,B1> | + +-+- __class__ | +---+ +---+ | + | | m_wrapped_objects -+->| *-+-->| *-+-+ C | + | +---------------------+ +---+ +---+ | +---+ | + | +->| | | + | +---+ | + | | + | Class +---------------------------------------+ + | DB1: +------------+ | ExtensionInstance + (*,)<---+- __bases__ | a2: | +---------------------+ vec InstanceValueHolder + | __dict__ -+->{...} +-+- __class__ | +---+ +-------------+ + +------------+ | m_wrapped_objects -+->| *-+-->| contains A2 | + ^ +---------------------+ +---+ +-------------+ + | ExtensionInstance + db1: | +---------------------+ vec InstanceValueHolder + +-+- __class__ | +---+ +----------------------+ + | m_wrapped_objects -+-->| *-+-->| contains B1_callback | + +---------------------+ +---+ +----------------------+ diff --git a/doc/enums.html b/doc/enums.html index 57aef35f..32d61447 100644 --- a/doc/enums.html +++ b/doc/enums.html @@ -6,33 +6,54 @@

c++boost.gif (8819 bytes)Wrapping enums + src="../../../c++boost.gif" alt= "c++boost.gif (8819 bytes)">
+ Wrapping enums

+

Because there is in general no way to deduce that a value of arbitrary type T -is an enumeration constant, py_cpp cannot automatically convert enum values to -and from Python. To handle this case, you need to decide how you want the enum -to show up in Python (since Python doesn't have enums). Once you have done that, -you can write some simple from_python() and -to_python() functions. +is an enumeration constant, the Boost Python Library cannot automatically +convert enum values to and from Python. To handle this case, you need to decide +how you want the enum to show up in Python (since Python doesn't have +enums). Once you have done that, you can write some simple +from_python() and to_python() functions.

If you are satisfied with a Python int as a way to represent your enum -values, py_cpp provides a shorthand for these functions. You just need to -instantiate boost::python::enum_as_int_converters<EnumType> where +values, we provide a shorthand for these functions. You just need to cause +boost::python::enum_as_int_converters<EnumType> to be +instantiated, where EnumType is your enumerated type. There are two convenient ways to do this:

    -
  1. +
  2. Explicit instantiation: + +
    +  template class boost::python::enum_as_int_converters<my_enum>;
    +
    + +Some buggy C++ implementations require a class to be instantiated in the same +namespace in which it is defined. In that case, the simple incantation above becomes: + +
    +   ...
    +} // close my_namespace
    +
     // drop into namespace python and explicitly instantiate
    -BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // this is a gcc 2.95.2 bug workaround
    -  template class enum_as_int_converters;
    -BOOST_PYTHON_END_CONVERSION_NAMESPACE
    +namespace boost { namespace python {
    +  template class enum_as_int_converters<my_enum_type>;
    +}} // namespace boost::python
    +
    +namespace my_namespace { // re-open my_namespace
    +   ...
     
    -
  3. +
    +
    +
  4. If you have such an implementation, you may find this technique more convenient +
     // instantiate as base class in any namespace
     struct EnumTypeConverters
    -    : boost::python::py_enum_as_int_converters
    +    : boost::python::enum_as_int_converters<EnumType>
     {
     };
     
  5. @@ -66,7 +87,8 @@ BOOST_PYTHON_END_CONVERSION_NAMESPACE long type. You may also want to add a bunch of lines like this to your module -initialization: +initialization. These bind the corresponding enum values to the appropriate +names so they can be used from Python:
     mymodule.add(boost::python::to_python(enum_value_1), "enum_value_1");
    @@ -78,17 +100,21 @@ You can also add these to an extension class definition, if your enum happens to
     be local to a class and you want the analogous interface in Python:
     
     
    -my_class.add(boost::python::to_python(enum_value_1), "enum_value_1");
    -my_class.add(boost::python::to_python(enum_value_2), "enum_value_2");
    +my_class_builder.add(boost::python::to_python(enum_value_1), "enum_value_1");
    +my_class_builder.add(boost::python::to_python(enum_value_2), "enum_value_2");
     ...
     
    +

    + Next: Pointers and Smart Pointers + Previous: Building an Extension Module + Up: Top

    © Copyright David Abrahams 2000. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright - notice appears in all copies. This document is provided “as - is” without express or implied warranty, and with no claim as to + notice appears in all copies. This document is provided ``as + is'' without express or implied warranty, and with no claim as to its suitability for any purpose.

    - Updated: Nov 26, 2000 + Updated: Mar 6, 2001

diff --git a/doc/example1.html b/doc/example1.html index 9bf9e280..402cc7a6 100644 --- a/doc/example1.html +++ b/doc/example1.html @@ -1,7 +1,7 @@ - A Simple Example Using py_cpp + A Simple Example

@@ -9,7 +9,7 @@ "c++boost.gif (8819 bytes)">

- A Simple Example Using py_cpp + A Simple Example

Suppose we have the following C++ API which we want to expose in @@ -18,104 +18,57 @@

 #include <string>
 
-namespace hello {
-  class world
-  {
-   public:
-      world(int);
-      ~world();
-      std::string get() const { return "hi, world"; }
-    ...
-  };
-  std::size_t length(const world& x) { return std::strlen(x.get()); }
+namespace { // Avoid cluttering the global namespace.
+
+  // A couple of simple C++ functions that we want to expose to Python.
+  std::string greet() { return "hello, world"; }
+  int square(int number) { return number * number; }
 }
 
 

- Here is the C++ code for a python module called hello - which exposes the API using py_cpp: + Here is the C++ code for a python module called getting_started1 + which exposes the API.

-#include 
-// Python requires an exported function called init<module-name> in every
-// extension module. This is where we build the module contents.
-extern "C"
-#ifdef _WIN32
-__declspec(dllexport)
-#endif
-void inithello()
+#include <boost/python/class_builder.hpp>
+namespace python = boost::python;
+
+BOOST_PYTHON_MODULE_INIT(getting_started1)
 {
-    try
-    {
-       // create an object representing this extension module
-       boost::python::module_builder hello("hello");
-       // Create the Python type object for our extension class
-       boost::python::class_builder<hello::world> world_class(hello, "world");
-       // Add the __init__ function
-       world_class.def(boost::python::constructor<int>());
-       // Add a regular member function
-       world_class.def(&hello::world::get, "get");
-       // Add a regular function to the module
-       hello.def(hello::length, "length");
-    }
-    catch(...)
-    {
-       boost::python::handle_exception();    // Deal with the exception for Python
-    }
+  try
+  {
+    // Create an object representing this extension module.
+    python::module_builder this_module("getting_started1");
+
+    // Add regular functions to the module.
+    this_module.def(greet, "greet");
+    this_module.def(square, "square");
+  }
+  catch(...)
+  {
+    python::handle_exception(); // Deal with the exception for Python
+  }
 }
-// Win32 DLL boilerplate
-#if defined(_WIN32)
-#include <windows.h>
-extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID)
-{
-    return 1;
-}
-#endif // _WIN32
 

That's it! If we build this shared library and put it on our - PYTHONPATH we can now access our C++ class and function from + PYTHONPATH we can now access our C++ functions from Python.

->>> import hello
->>> hi_world = hello.world(3)
->>> hi_world.get()
-'hi, world'
->>> hello.length(hi_world)
-9
+>>> import getting_started1
+>>> print getting_started1.greet()
+hello, world
+>>> number = 11
+>>> print number, '*', number, '=', getting_started1.square(number)
+11 * 11 = 121
 
-

- We can even make a subclass of hello.world: -

-
->>> class my_subclass(hello.world):
-...     def get(self):
-...         return 'hello, world'
-...
->>> y = my_subclass(4)
->>> y.get()
-'hello, world'
-
-
-

- Pretty cool! You can't do that with an ordinary Python extension type! -

-
->>> hello.length(y)
-9
-
-
-

- Of course, you may now have a slightly empty feeling in the pit of - your little pythonic stomach. Perhaps you feel your subclass deserves - to have a length() of 12? If so, read on... -

- Previous: Comparisons with other systems Next: Overridable virtual functions Up: + Next: Exporting Classes + Previous: Comparisons with other systems Up: Top

© Copyright David Abrahams 2000. Permission to copy, use, modify, @@ -124,6 +77,6 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) express or implied warranty, and with no claim as to its suitability for any purpose.

- Updated: Nov 26, 2000 + Updated: Mar 6, 2000

diff --git a/doc/exporting_classes.html b/doc/exporting_classes.html new file mode 100644 index 00000000..e5932e70 --- /dev/null +++ b/doc/exporting_classes.html @@ -0,0 +1,144 @@ + + + Exporting Classes + +
+

+ + +

+

+ Exporting Classes +

+

+ Now let's expose a C++ class to Python: + +

+#include <iostream>
+#include <string>
+
+namespace { // Avoid cluttering the global namespace.
+
+  // A friendly class.
+  class hello
+  {
+    public:
+      hello(const std::string& country) { this->country = country; }
+      std::string greet() const { return "Hello from " + country; }
+    private:
+      std::string country;
+  };
+
+  // A function taking a hello object as an argument.
+  std::string invite(const hello& w) {
+    return w.greet() + "! Please come soon!";
+  }
+}
+
+

+ To expose the class, we use a class_builder in addition to the + module_builder from the previous example. Class member functions + are exposed by using the def() member function on the + class_builder: +

+#include <boost/python/class_builder.hpp>
+namespace python = boost::python;
+
+BOOST_PYTHON_MODULE_INIT(getting_started2)
+{
+  try
+  {
+    // Create an object representing this extension module.
+    python::module_builder this_module("getting_started2");
+
+    // Create the Python type object for our extension class.
+    python::class_builder<hello> hello_class(this_module, "hello");
+
+    // Add the __init__ function.
+    hello_class.def(python::constructor<std::string>());
+    // Add a regular member function.
+    hello_class.def(&hello::greet, "greet");
+
+    // Add invite() as a regular function to the module.
+    this_module.def(invite, "invite");
+
+    // Even better, invite() can also be made a member of hello_class!!!
+    hello_class.def(invite, "invite");
+  }
+  catch(...)
+  {
+    python::handle_exception(); // Deal with the exception for Python
+  }
+}
+
+

+Now we can use the class normally from Python: + +

+>>> from getting_started2 import *
+>>> hi = hello('California')
+>>> hi.greet()
+'Hello from California'
+>>> invite(hi)
+'Hello from California! Please come soon!'
+>>> hi.invite()
+'Hello from California! Please come soon!'
+
+ +Notes:
    +
  • We expose the class' constructor by calling def() on the + class_builder with an argument whose type is + constructor<params>, where params + matches the list of constructor argument types: + + +
  • Regular member functions are defined by calling def() with a + member function pointer and its Python name: + +
  • Any function added to a class whose initial argument matches the class (or +any base) will act like a member function in Python. +
+

+ We can even make a subclass of hello.world: + +

+>>> class wordy(hello):
+...     def greet(self):
+...         return hello.greet(self) + ', where the weather is fine'
+...
+>>> hi2 = wordy('Florida')
+>>> hi2.greet()
+'Hello from Florida, where the weather is fine'
+>>> invite(hi2)
+'Hello from Florida! Please come soon!'
+
+

+ Pretty cool! You can't do that with an ordinary Python extension type! + + Of course, you may now have a slightly empty feeling in the pit of + your little pythonic stomach. Perhaps you wanted to see the following + wordy invitation: + +

+'Hello from Florida, where the weather is fine! Please come soon!'
+
+ + After all, invite calls hello::greet(), and you + reimplemented that in your Python subclass, wordy. If so, read on... + +

+ Next: Overridable virtual functions + Previous: A Simple Example Up: + Top +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability + for any purpose. +

+ Updated: Mar 6, 2001 +

+ diff --git a/doc/extending.html b/doc/extending.html index 47341389..8839ab43 100644 --- a/doc/extending.html +++ b/doc/extending.html @@ -38,11 +38,11 @@ This last item typically occupies a great deal of code in an extension module. Remember that Python is a completely dynamic language. A callable - object receives its arguments in a tuple; it is up to that object to - extract those arguments from the tuple, check their types, and raise - appropriate exceptions. There are numerous other tedious details that need - to be managed; too many to mention here. Py_cpp is designed to lift most of - that burden.
+ object receives its arguments in a tuple; it is up to that object to extract + those arguments from the tuple, check their types, and raise appropriate + exceptions. There are numerous other tedious details that need to be + managed; too many to mention here. The Boost Python Library is designed to + lift most of that burden.

@@ -56,10 +56,10 @@ sublcassing the extension type. Aside from being tedious, it's not really the same as having a true class, because there's no way for the user to override a method of the extension type which is called from the - extension module. Py_cpp solves this problem by taking advantage of Python's metaclass feature to provide objects which look, walk, and hiss almost exactly - like regular Python classes. Py_cpp classes are actually cleaner than + like regular Python classes. Boost.Python classes are actually cleaner than Python classes in some subtle ways; a more detailed discussion will follow (someday).

Next: Comparisons with Other Systems Up: - - py_cpp Python/C++ binding documentation + The Boost Python Library (Boost.Python)

c++boost.gif (8819 bytes) py_cpp* + align="center" height="86">
The Boost Python Library (Boost.Python)

-

- The source code for py_cpp, including a MSVC demo project is available here. -

Synopsis

- py_cpp is a system for quickly and easily interfacing C++ code with Python such that the Python interface is + Use the Boost Python Library to quickly and easily export a C++ library to Python such that the Python interface is very similar to the C++ interface. It is designed to be minimally intrusive on your C++ design. In most cases, you should not have to alter - your C++ classes in any way in order to use them with py_cpp. The system - should simply “reflect” your C++ classes and functions into - Python. The major features of py_cpp include support for: + your C++ classes in any way in order to use them with Boost.Python. The system + should simply ``reflect'' your C++ classes and functions into + Python. The major features of Boost.Python include support for:

    -
  • Subclassing extension types in Python +
  • Subclassing extension types in Python
  • Overriding virtual functions in Python
  • [Member] function Overloading
  • Automatic wrapping of numeric operators @@ -32,9 +27,27 @@ among others.

    Supported Platforms

    -

    py_cpp has been tested in the following configurations: +

    Boost.Python is known to have been tested in the following configurations:

      +
    • Against Python 2.0 using the following compiler/library combinations: + +
    • Against Python 1.5.2 using the following compiler/library:
        @@ -56,29 +69,17 @@ among others. href="mailto:rwgk@cci.lbl.gov">Ralf W. Grosse-Kunstleve]
      • An upcoming release of Metrowerks CodeWarrior - Pro6 for Windows (the first release has a bug that's fatal to py_cpp) + Pro6 for Windows (the first release has a bug that's fatal to Boost.Python)
      -
      -
    • Against Python 2.0 using the following compiler/library combinations: - -
    - -

    Py_cpp requires the boost libraries, and is - has been accepted for inclusion into the boost libraries pending “boostification“ - (completion of the documentation, change in some naming conventions and - resolution of some namespace issues).

    Credits

      -
    • David Abrahams originated - and wrote py_cpp. +
    • David Abrahams originated + and wrote most of the library, and continues to coordinate development.
    • Ullrich Koethe - had independently developed a similar system. When he discovered py_cpp, + had independently developed a similar system. When he discovered Boost.Python, he generously contributed countless hours of coding and much insight into improving it. He is responsible for an early version of the support for function overloading and wrote the support for @@ -87,16 +88,22 @@ among others. Python and C++, and has designed an extremely easy-to-use way of exposing numeric operators, including a way to avoid explicit coercion by means of overloading. + +
    • Ralf W. + Grosse-Kunstleve contributed pickle support + and numerous other small improvements. He's working on a way to allow + types exported by multiple modules to interact. -
    • The members of the boost mailing list and the Python community supplied - invaluable early feedback. In particular, Ron Clarke, Mark Evans, Anton - Gluck, Ralf W. Grosse-Kunstleve, Prabhu Ramachandran, and Barry Scott took - the brave step of trying to use py_cpp while it was still in early stages - of development. +
    • The members of the boost mailing list and the Python community + supplied invaluable early feedback. In particular, Ron Clarke, Mark Evans, + Anton Gluck, Chuck Ingold, Prabhu Ramachandran, and Barry Scott took the + brave step of trying to use Boost.Python while it was still in early + stages of development. -
    • The development of py_cpp wouldn't have been - possible without the generous support of Dragon Systems/Lernout and - Hauspie, Inc who supported its development as an open-source project. +
    • The development of Boost.Python wouldn't have been possible without + the generous support of Dragon + Systems/Lernout and Hauspie, Inc who supported its development as an + open-source project.

    Table of Contents

    @@ -105,10 +112,12 @@ among others.
  • A Brief Introduction to writing Python extension modules -
  • Comparisons between py_cpp and other +
  • Comparisons between Boost.Python and other systems for extending Python -
  • A Simple Example Using py_cpp +
  • A Simple Example + +
  • Exporting Classes
  • Overridable Virtual Functions @@ -120,92 +129,38 @@ among others.
  • A Peek Under the Hood -
  • Building a Module with Py_cpp +
  • Building an Extension Module -
  • Advanced Topics +
  • Pickle Support -
      -
    1. class_builder<> +
    2. Cross-Extension-Module Dependencies -
    3. enums +
    4. Wrapping Enums -
    5. References +
    6. Pointers and Smart Pointers -
    7. Pointers and Smart Pointers - -
    8. Built-in Python Types - -
    9. Other Extension Types - -
    10. Templates -
    +
  • Internal Data Structures

    - More sophisticated examples are given in - extclass_demo.cpp, extclass_demo.h, and - test_extclass.py in the source code - archive. There's much more here, and much more documentation to - come... + Documentation is a major ongoing project; assistance is greatly + appreciated! In the meantime, useful examples of every Boost.Python feature should + be evident in the regression test files test/comprehensive.[py/hpp/cpp] +

    Questions should be directed to the boost mailing list. - -

    Naming Contest

    + "http://www.yahoogroups.com/list/boost">the boost mailing list.

    - Yes, I know py_cpp is a lousy name. Problem is, the best names my puny - imagination can muster (IDLE and GRAIL) are taken, so I'm holding a - naming contest. First prize? You get to pick the name<0.2wink> and - you will be credited in the documentation. Names that have been suggested - so far include: -

      -
    • - Py++ -
    • - Python++ -
    • - Coil -
    • - SnakeSkin -
    • - CCCP - Convert C++ - Classes to Python -
    • - C3PO - Convert C++ - Classes to Python - Objects -
    • - PALIN - Python - Augmented-Language - INtegration -
    • - CLEESE - C++ Language Extension Environment - Supremely Easy -
    • - JONES - Just Obscenely Neat Extension - System -
    • - C-thru -
    • - SeamlessC -
    • - BorderCrossing -
    • - Perseus (because he solved a hairy problem involving snakes by using - reflection and was invisible most of the time). -
    - Please post or send me your suggestions!
    -
    - -

    - © Copyright David Abrahams 2000. Permission to copy, use, modify, + © Copyright David Abrahams 2001. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright - notice appears in all copies. This document is provided “as is” without + notice appears in all copies. This document is provided ``as is'' without express or implied warranty, and with no claim as to its suitability for any purpose.

    - Updated: Nov 26, 2000 + Updated: Mar 6, 2001 diff --git a/doc/inheritance.html b/doc/inheritance.html index 2dd8071b..cd95c744 100644 --- a/doc/inheritance.html +++ b/doc/inheritance.html @@ -12,11 +12,11 @@

    Inheritance in Python

    - Py_cpp extension classes support single and multiple-inheritance in - Python, just like regular Python classes. You can mix built-in Python - classes with py_cpp extension classes in a derived class' tuple of - bases. Whenever a py_cpp extension class is among the bases for a new - class in Python, the result is an extension class: + Boost.Python extension classes support single and multiple-inheritance in + Python, just like regular Python classes. You can arbitrarily mix + built-in Python classes with extension classes in a derived class' + tuple of bases. Whenever a Boost.Python extension class is among the bases for a + new class in Python, the result is an extension class:

     >>> class MyPythonClass:
    @@ -37,7 +37,7 @@
     
     

    Reflecting C++ Inheritance Relationships

    - Py_cpp also allows us to represent C++ inheritance relationships so that + Boost.Python also allows us to represent C++ inheritance relationships so that wrapped derived classes may be passed where values, pointers, or references to a base class are expected as arguments. The declare_base member function of @@ -71,22 +71,22 @@ int get_derived_x(const Derived& d) { return d.x; }


    -#include -extern "C" -#ifdef _WIN32 -__declspec(dllexport) -#endif -void initmy_module() +#include <boost/python/class_builder.hpp> + +// namespace alias for code brevity +namespace python = boost::python; + +BOOST_PYTHON_MODULE_INIT(my_module) {     try     { -       boost::python::module_builder my_module("my_module"); +       python::module_builder my_module("my_module"); -       boost::python::class_builder<Base> base_class(my_module, "Base"); -       base_class.def(boost::python::constructor<void>()); +       python::class_builder<Base> base_class(my_module, "Base"); +       base_class.def(python::constructor<void>()); -       boost::python::class_builder<Derived> derived_class(my_module, "Derived"); -       derived_class.def(boost::python::constructor<void>()); +       python::class_builder<Derived> derived_class(my_module, "Derived"); +       derived_class.def(python::constructor<void>()); // Establish the inheritance relationship between Base and Derived derived_class.declare_base(base_class); @@ -96,7 +96,7 @@ void initmy_module()     }     catch(...)     { -       boost::python::handle_exception();    // Deal with the exception for Python +       python::handle_exception();    // Deal with the exception for Python     } }
    @@ -111,11 +111,19 @@ void initmy_module() >>> derived = Derived() >>> get_name(base) 'Base' -objects of wrapped class Derived may be passed where Base is expected
    +
    +
    +objects of wrapped class Derived may be passed where Base is expected +
    +
     >>> get_name(derived) 
     'Derived'
    -
    objects of wrapped class Derived can be passed where Derived is -expected but where type information has been lost.
    +
    +
    +objects of wrapped class Derived can be passed where Derived is +expected but where type information has been lost. +
    +
     >>> get_derived_x(derived_as_base()) 
     -1
     
    @@ -135,12 +143,12 @@ struct Base2 {}; struct Derived2 { int f(); };
    ... -   boost::python::class_builder<Base> base2_class(my_module, "Base2"); -   base2_class.def(boost::python::constructor<void>()); +   python::class_builder<Base> base2_class(my_module, "Base2"); +   base2_class.def(python::constructor<void>()); -   boost::python::class_builder<Derived2> derived2_class(my_module, "Derived2"); -   derived2_class.def(boost::python::constructor<void>()); - derived_class.declare_base(base_class, boost::python::without_downcast); +   python::class_builder<Derived2> derived2_class(my_module, "Derived2"); +   derived2_class.def(python::constructor<void>()); + derived_class.declare_base(base_class, python::without_downcast);
    @@ -150,8 +158,8 @@ struct Derived2 { int f(); }; references, or values.

    + Next: Special Method and Operator Support Previous: Function Overloading - Next: Special Method Names Up: Top

    © Copyright David Abrahams 2000. Permission to copy, use, modify, diff --git a/doc/overloading.html b/doc/overloading.html index 6197b230..242e023f 100644 --- a/doc/overloading.html +++ b/doc/overloading.html @@ -29,7 +29,7 @@ private: }; ... -void initoverload_demo() +BOOST_PYTHON_MODULE_INIT(overload_demo) {     try     { @@ -113,18 +113,17 @@ namespace scope as Python member functions.

    Overload Resolution

    - The function overload resolution mechanism in py_cpp works as - follows: + The function overload resolution mechanism works as follows:

    • Attribute lookup for extension classes proceeds in the + href="http://www.python.org/doc/current/tut/node11.html#SECTION0011510000000000000000">the usual Python way using a depth-first, left-to-right search. When a class is found which has a matching attribute, only functions overloaded in the context of that class are candidates for overload resolution. In this sense, overload resolution mirrors the C++ mechanism, where a name - in a derived class “hides” all functions with the same name from a base + in a derived class ``hides'' all functions with the same name from a base class.

      @@ -133,24 +132,24 @@ namespace scope as Python member functions. def()ed. The first function whose signature can be made to match each argument passed is the one which is ultimately called. This means in particular that you cannot overload the same function on - both “int” and “float” because Python + both ``int'' and ``float'' because Python automatically converts either of the two types into the other one. - If the “float” overload is found first, it is used - also used for arguments of type “int” as well, and the - “int” version of the function is never invoked. + If the ``float'' overload is found first, it is used + also used for arguments of type ``int'' as well, and the + ``int'' version of the function is never invoked.

    - Prev: Overridable Virtual Functions - Next: Special Method Names + Next: Inheritance + Previous: Overridable Virtual Functions Up: Top

    - © Copyright David Abrahams 2000. Permission to copy, use, modify, + © Copyright David Abrahams 2001. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright - notice appears in all copies. This document is provided “as - is” without express or implied warranty, and with no claim as to + notice appears in all copies. This document is provided ``as + is'' without express or implied warranty, and with no claim as to its suitability for any purpose.

    - Updated: Nov 26, 2000 + Updated: Mar 6, 2001 diff --git a/doc/overriding.html b/doc/overriding.html index f8810ca1..02665be5 100644 --- a/doc/overriding.html +++ b/doc/overriding.html @@ -9,7 +9,7 @@

    Overridable Virtual Functions

    - In the previous example we exposed a simple + In the previous example we exposed a simple C++ class in Python and showed that we could write a subclass. We even redefined one of the functions in our derived class. Now we will learn how to make the function behave virtually when called from C++. @@ -17,16 +17,17 @@

    Example

    -

    In this example, it is assumed that world::get() is a virtual +

    In this example, it is assumed that hello::greet() is a virtual member function:

    -class world
    +class hello
     {
      public:
    -    world(int);
    -    virtual ~world();
    -    virtual std::string get() const { return "hi, world"; }
    +    hello(const std::string& country) { this->country = country; }
    +    virtual std::string greet() const { return "Hello from " + country; }
    +    virtual ~hello(); // Good practice 
    +    ...
     };
     
    @@ -37,21 +38,28 @@ class world
      -
    1. A PyObject* data member that holds a - reference to the corresponding Python object. +
    2. A PyObject* data member (usually + called self) that holds a pointer to the Python object corresponding + to our C++ hello instance. -
    3. A constructor for each exposed constructor of the - base class which stores an additional initial PyObject* argument - in the data member described above. +
    4. For each exposed constructor of the + base class T, a constructor which takes the same parameters preceded by an initial + PyObject* argument. The initial argument should be stored in the self data + member described above. -
    5. An implementation of each virtual function you may +
    6. If the class being wrapped is ever returned by + value from a wrapped function, be sure you do the same for the + T's copy constructor: you'll need a constructor taking arguments + (PyObject*, const T&). + +
    7. An implementation of each virtual function you may wish to override in Python which uses - boost::python::callback<return-type>::call_method() to call + callback<return-type>::call_method(self, "name", args...) to call the Python override. -
    8. For each non-pure virtual function meant to be +
    9. For each non-pure virtual function meant to be overridable from Python, a static member function (or a free function) taking - a reference or pointer to the base type as the first parameter and which + a reference or pointer to the T as the first parameter and which forwards any additional parameters neccessary to the default implementation of the virtual function. See also this note if the base class virtual function is private. @@ -59,52 +67,60 @@ class world
    -struct world_callback : world
    +struct hello_callback : hello
     {
    -    world_callback(PyObject* self, int x) // 2
    -        : world(x),
    -          m_self(self) {}
    +    // hello constructor storing initial self_ parameter
    +    hello_callback(PyObject* self_, const std::string& x) // 2
    +        : hello(x), self(self_) {}
     
    -    std::string get() const // 3
    -        { return boost::python::callback<std::string>::call_method(m_self, "get"); }
    +    // In case hello is returned by-value from a wrapped function
    +    hello_callback(PyObject* self_, const hello& x) // 3
    +        : hello(x), self(self_) {}
     
    -    static std::string default_get(const hello::world& self) const // 4
    -        { return self.world::get(); }
    +    // Override greet to call back into Python
    +    std::string greet() const // 4
    +        { return boost::python::callback<std::string>::call_method(self, "greet"); }
    +
    +    // Supplies the default implementation of greet
    +    static std::string default_greet(const hello& self_) const // 5
    +        { return self_.hello::greet(); }
      private:
    -    PyObject* m_self; // 1
    +    PyObject* self; // 1
     };
     

    - Finally, we add world_callback to the - class_builder<> declaration in our module initialization - function, and when we define the function, we must tell py_cpp about the default + Finally, we add hello_callback to the + class_builder<> declaration in our module initialization + function, and when we define the function, we must tell Boost.Python about the default implementation:

     // Create the Python type object for our extension class
    -boost::python::class_builder<hello::world,world_callback> world_class(hello, "world");
    +"hello_class">Python type object for our extension class
    +boost::python::class_builder<hello,hello_callback> hello_class(hello, "hello");
     // Add a virtual member function
    -world_class.def(&world::get, "get", &world_callback::default_get);
    +hello_class.def(&hello::greet, "greet", &hello_callback::default_greet);
     

    - Now our subclass of hello.world behaves as expected: + Now our Python subclass of hello behaves as expected:

    ->>> class my_subclass(hello.world):
    -...     def get(self):
    -...         return 'hello, world'
    +>>> class wordy(hello):
    +...     def greet(self):
    +...         return hello.greet(self) + ', where the weather is fine'
     ...
    ->>> hello.length(my_subclass())
    -12
    +>>> hi2 = wordy('Florida')
    +>>> hi2.greet()
    +'Hello from Florida, where the weather is fine'
    +>>> invite(hi2)
    +'Hello from Florida, where the weather is fine! Please come soon!'
     
    -

    *You may ask, "Why do we need this derived class? This could have been designed so that everything gets done right - inside of hello::world." One of the goals of py_cpp is to be + inside of hello." One of the goals of Boost.Python is to be minimally intrusive on an existing C++ design. In principle, it should be possible to expose the interface for a 3rd party library without changing it. To unintrusively hook into the virtual functions so that a Python @@ -117,32 +133,29 @@ world_class.def(&world::get, "get", &world_callback::default_get) deal with than a virtual function with a default implementation. First of all, you obviously don't need to supply a default implementation. Secondly, you don't need to call - def() on the extension_class<> instance + def() on the extension_class<> instance for the virtual function. In fact, you wouldn't want to: if the corresponding attribute on the Python class stays undefined, you'll get an - AttributeError in Python when you try to call the function, + AttributeError in Python when you try to call the function, indicating that it should have been implemented. For example:

     struct baz {
         virtual int pure(int) = 0;
    +    int calls_pure(int x) { return pure(x) + 1000; }
     };
     
     struct baz_callback {
         int pure(int x) { boost::python::callback<int>::call_method(m_self, "pure", x); }
     };
     
    -extern "C"
    -#ifdef _WIN32
    -__declspec(dllexport)
    -#endif
    -initfoobar()
    +BOOST_PYTHON_MODULE_INIT(foobar)
     {
         try
         {
            boost::python::module_builder foobar("foobar");
            boost::python::class_builder<baz,baz_callback> baz_class("baz");
    -       baz_class.def(&baz::pure, "pure");
    +       baz_class.def(&baz::calls_pure, "calls_pure");
         }
         catch(...)
         {
    @@ -161,12 +174,18 @@ initfoobar()
     Traceback (innermost last):
       File "<stdin>", line 1, in ?
     AttributeError: pure
    +>>> x.calls_pure(1)
    +Traceback (innermost last):
    +  File "<stdin>", line 1, in ?
    +AttributeError: pure
     >>> class mumble(baz):
     ...    def pure(self, x): return x + 1
     ...
     >>> y = mumble()
     >>> y.pure(99)
     100
    +>>> y.calls_pure(99)
    +1100
     

    Private Non-Pure Virtual Functions

    @@ -180,16 +199,17 @@ this limited way without breaking binary compatibility (though it will certainly break the ODR). +

    - Prev: A Simple Example Using py_cpp Next: Function Overloading Up: Top + Next: Function Overloading + Previous: Exporting Classes + Up: Top

    - © Copyright David Abrahams 2000. Permission to copy, use, modify, + © Copyright David Abrahams 2001. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.

    - Updated: Nov 26, 2000 + Updated: Mar 21, 2001 diff --git a/doc/pickle.html b/doc/pickle.html new file mode 100644 index 00000000..994a78ab --- /dev/null +++ b/doc/pickle.html @@ -0,0 +1,272 @@ + + +Boost.Python Pickle Support + +

    + +c++boost.gif (8819 bytes) + +
    +

    Boost.Python Pickle Support

    + +Pickle is a Python module for object serialization, also known +as persistence, marshalling, or flattening. + +

    +It is often necessary to save and restore the contents of an object to +a file. One approach to this problem is to write a pair of functions +that read and write data from a file in a special format. A powerful +alternative approach is to use Python's pickle module. Exploiting +Python's ability for introspection, the pickle module recursively +converts nearly arbitrary Python objects into a stream of bytes that +can be written to a file. + +

    +The Boost Python Library supports the pickle module by emulating the +interface implemented by Jim Fulton's ExtensionClass module that is +included in the +ZOPE +distribution. +This interface is similar to that for regular Python classes as +described in detail in the +Python Library Reference for pickle. + +


    +

    The Boost.Python Pickle Interface

    + +At the user level, the Boost.Python pickle interface involves three special +methods: + +
    +
    +__getinitargs__ +
    + When an instance of a Boost.Python extension class is pickled, the + pickler tests if the instance has a __getinitargs__ method. + This method must return a Python tuple (it is most convenient to use + a boost::python::tuple). When the instance is restored by the + unpickler, the contents of this tuple are used as the arguments for + the class constructor. + +

    + If __getinitargs__ is not defined, the class constructor + will be called without arguments. + +

    +

    +__getstate__ + +
    + When an instance of a Boost.Python extension class is pickled, the + pickler tests if the instance has a __getstate__ method. + This method should return a Python object representing the state of + the instance. + +

    + If __getstate__ is not defined, the instance's + __dict__ is pickled (if it is not empty). + +

    +

    +__setstate__ + +
    + When an instance of a Boost.Python extension class is restored by the + unpickler, it is first constructed using the result of + __getinitargs__ as arguments (see above). Subsequently the + unpickler tests if the new instance has a __setstate__ + method. If so, this method is called with the result of + __getstate__ (a Python object) as the argument. + +

    + If __setstate__ is not defined, the result of + __getstate__ must be a Python dictionary. The items of this + dictionary are added to the instance's __dict__. + +

    + +If both __getstate__ and __setstate__ are defined, +the Python object returned by __getstate__ need not be a +dictionary. The __getstate__ and __setstate__ methods +can do what they want. + +
    +

    Pitfalls and Safety Guards

    + +In Boost.Python extension modules with many extension classes, +providing complete pickle support for all classes would be a +significant overhead. In general complete pickle support should only be +implemented for extension classes that will eventually be pickled. +However, the author of a Boost.Python extension module might not +anticipate correctly which classes need support for pickle. +Unfortunately, the pickle protocol described above has two important +pitfalls that the end user of a Boost.Python extension module might not +be aware of: + +
    +
    +Pitfall 1: +Both __getinitargs__ and __getstate__ are not defined. + +
    + In this situation the unpickler calls the class constructor without + arguments and then adds the __dict__ that was pickled by + default to that of the new instance. + +

    + However, most C++ classes wrapped with Boost.Python will have member + data that are not restored correctly by this procedure. To alert the + user to this problem, a safety guard is provided. If both + __getinitargs__ and __getstate__ are not defined, + Boost.Python tests if the class has an attribute + __dict_defines_state__. An exception is raised if this + attribute is not defined: + +

    +    RuntimeError: Incomplete pickle support (__dict_defines_state__ not set)
    +
    + + In the rare cases where this is not the desired behavior, the safety + guard can deliberately be disabled. The corresponding C++ code for + this is, e.g.: + +
    +    class_builder<your_class> py_your_class(your_module, "your_class");
    +    py_your_class.dict_defines_state();
    +
    + + It is also possible to override the safety guard at the Python level. + E.g.: + +
    +    import your_bpl_module
    +    class your_class(your_bpl_module.your_class):
    +      __dict_defines_state__ = 1
    +
    + +

    +

    +Pitfall 2: +__getstate__ is defined and the instance's __dict__ is not empty. + +
    + The author of a Boost.Python extension class might provide a + __getstate__ method without considering the possibilities + that: + +

    +

      +
    • + his class is used in Python as a base class. Most likely the + __dict__ of instances of the derived class needs to be + pickled in order to restore the instances correctly. + +

      +

    • + the user adds items to the instance's __dict__ directly. + Again, the __dict__ of the instance then needs to be + pickled. + +
    +

    + + To alert the user to this highly unobvious problem, a safety guard is + provided. If __getstate__ is defined and the instance's + __dict__ is not empty, Boost.Python tests if the class has + an attribute __getstate_manages_dict__. An exception is + raised if this attribute is not defined: + +

    +    RuntimeError: Incomplete pickle support (__getstate_manages_dict__ not set)
    +
    + + To resolve this problem, it should first be established that the + __getstate__ and __setstate__ methods manage the + instances's __dict__ correctly. Note that this can be done + both at the C++ and the Python level. Finally, the safety guard + should intentionally be overridden. E.g. in C++: + +
    +    class_builder<your_class> py_your_class(your_module, "your_class");
    +    py_your_class.getstate_manages_dict();
    +
    + + In Python: + +
    +    import your_bpl_module
    +    class your_class(your_bpl_module.your_class):
    +      __getstate_manages_dict__ = 1
    +      def __getstate__(self):
    +        # your code here
    +      def __setstate__(self, state):
    +        # your code here
    +
    +
    + +
    +

    Practical Advice

    + +
      +
    • + Avoid using __getstate__ if the instance can also be + reconstructed by way of __getinitargs__. This automatically + avoids Pitfall 2. + +

      +

    • + If __getstate__ is required, include the instance's + __dict__ in the Python object that is returned. + +
    + +
    +

    Examples

    + +There are three files in boost/libs/python/example that +show how so provide pickle support. + +

    pickle1.cpp

    + + The C++ class in this example can be fully restored by passing the + appropriate argument to the constructor. Therefore it is sufficient + to define the pickle interface method __getinitargs__. + +

    pickle2.cpp

    + + The C++ class in this example contains member data that cannot be + restored by any of the constructors. Therefore it is necessary to + provide the __getstate__/__setstate__ pair of + pickle interface methods. + +

    + For simplicity, the __dict__ is not included in the result + of __getstate__. This is not generally recommended, but a + valid approach if it is anticipated that the object's + __dict__ will always be empty. Note that the safety guards + will catch the cases where this assumption is violated. + +

    pickle3.cpp

    + + This example is similar to pickle2.cpp. However, the + object's __dict__ is included in the result of + __getstate__. This requires more code but is unavoidable + if the object's __dict__ is not always empty. + +
    +© Copyright Ralf W. Grosse-Kunstleve 2001. Permission to copy, +use, modify, sell and distribute this document is granted provided this +copyright notice appears in all copies. This document is provided "as +is" without express or implied warranty, and with no claim as to its +suitability for any purpose. + +

    +Updated: March 21, 2001 +

    diff --git a/doc/pointers.html b/doc/pointers.html index fe30af0b..00f7bb41 100644 --- a/doc/pointers.html +++ b/doc/pointers.html @@ -13,7 +13,7 @@

    In general, raw pointers passed to or returned from functions are problematic -for py_cpp because pointers have too many potential meanings. Is it an iterator? +for Boost.Python because pointers have too many potential meanings. Is it an iterator? A pointer to a single element? An array? When used as a return value, is the caller expected to manage (delete) the pointed-to object or is the pointer really just a reference? If the latter, what happens to Python references to the @@ -34,12 +34,10 @@ converted from/to Python strings.

    Can you avoid the problem?

    My first piece of advice to anyone with a case not covered above is -“find a way to avoid the problem.” For example, if you have just one -or two functions returning a pointer to a single (not an array of) const -T* for some wrapped T, you may be able to write a “thin -converting wrapper” over those two functions as follows (Since py_cpp -converts const T& values to_python by copying the T -into a new extension instance, Foo must have a public copy constructor): +``find a way to avoid the problem.'' For example, if you have just one +or two functions that return a pointer to an individual const +T, and T is a wrapped class, you may be able to write a ``thin +converting wrapper'' over those two functions as follows:

     const Foo* f(); // original function
    @@ -47,6 +45,10 @@ const Foo& f_wrapper() { return *f(); }
       ...
     my_module.def(f_wrapper, "f");
     
    +

    +Foo must have a public copy constructor for this technique to work, since Boost.Python +converts const T& values to_python by copying the T +value into a new extension instance.

    Dealing with the problem

    @@ -83,12 +85,12 @@ code before the last Python reference to it disappears: BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // this is a gcc 2.95.2 bug workaround PyObject* to_python(Foo* p) { - return boost::python::PyExtensionClassConverters::ptr_to_python(p); + return boost::python::python_extension_class_converters<Foo>::ptr_to_python(p); } PyObject* to_python(const Foo* p) { - return to_python(const_cast(p)); + return to_python(const_cast<Foo*>(p)); } BOOST_PYTHON_END_CONVERSION_NAMESPACE @@ -118,7 +120,7 @@ return value. You can handle this case by returning a Python tuple: typedef unsigned ErrorCode; const char* f(int* in_out_x); // original function ... -#include +#include <boost/python/objects.hpp> const boost::python::tuple f_wrapper(int in_x) { const char* s = f(in_x); return boost::python::tuple(s, in_x); @@ -131,8 +133,9 @@ my_module.def(f_wrapper, "f"); >>> str,out_x = f(3) - - +

    + Previous: Enums + Up: Top

    © Copyright David Abrahams 2000. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright diff --git a/doc/special.html b/doc/special.html index 4c314fcf..0caa1924 100644 --- a/doc/special.html +++ b/doc/special.html @@ -1,11 +1,10 @@ - + Special Method and Operator Support

    - c++boost.gif (8819 bytes)Special Method and Operator Support

    @@ -13,8 +12,8 @@ Overview

    - Py_cpp supports all of the standard + Boost.Python supports all of the standard special method names supported by real Python class instances except __complex__ (more on the reasons below). In addition, it can quickly and easily expose @@ -34,19 +33,24 @@ Python provides a number of special operators for basic customization of a class. Only a brief description is provided below; more complete documentation can be found here. + href="http://www.python.org/doc/current/ref/customization.html">here.

    __init__(self)
    Initialize the class instance. For extension classes not subclassed in - Python, this is provided by the - boost::python::constructor<...>() construct and should not be explicitly defed. + Python, __init__ is defined by + +
        my_class.def(boost::python::constructor<...>())
    + + (see section "A Simple Example Using Boost.Python").

    __del__(self)
    - Called when the extension instance is about to be destroyed. + Called when the extension instance is about to be destroyed. For extension classes + not subclassed in Python, __del__ is always defined automatically by + means of the class' destructor.
    __repr__(self)
    @@ -78,7 +82,7 @@
    __call__ (self[, args...])
    -Called when the instance is “called” as a function; if this method +Called when the instance is ``called'' as a function; if this method is defined, x(arg1, arg2, ...) is a shorthand for x.__call__(arg1, arg2, ...).
    @@ -100,7 +104,7 @@ std::string to_string(Foo const& f) boost::python::class_builder<Foo> foo_class(my_module, "Foo"); foo_class.def(&to_string, "__str__"); - Note that py_cpp also supports automatic wrapping of + Note that Boost.Python also supports automatic wrapping of __str__ and __cmp__. This is explained in the next section and the Table of Automatically Wrapped Methods. @@ -110,10 +114,10 @@ foo_class.def(&to_string, "__str__");

    Numeric operators can be exposed manually, by defing C++ [member] functions that support the standard Python numeric - protocols. This is the basic same technique used to expose + href="http://www.python.org/doc/current/ref/numeric-types.html">numeric + protocols. This is the same basic technique used to expose to_string() as __str__() above, and is covered in detail below. Py_cpp also supports + href="#numeric_manual">covered in detail below. Boost.Python also supports automatic wrapping of numeric operators whenever they have already been defined in C++. @@ -121,7 +125,7 @@ foo_class.def(&to_string, "__str__");

    Supose we wanted to expose a C++ class - BigNum which supports addition, so that we can write (in C++): + BigNum which supports addition. That is, in C++ we can write:

     BigNum a, b, c;
     ...
    @@ -143,7 +147,7 @@ bignum_class.def(boost::python::operators<boost::python::op_add>());
     
           Since BigNum also supports subtraction, multiplication, and division, we
           want to export those also. This can be done in a single command by
    -      “or”ing the operator identifiers together (a complete list of these
    +      ``or''ing the operator identifiers together (a complete list of these
           identifiers and the corresponding operators can be found in the Table of Automatically Wrapped Methods):
     
    @@ -170,7 +174,7 @@ a = i + b;
     bignum_class.def(boost::python::operators<boost::python::op_add>(), boost::python::right_operand<int>());
     bignum_class.def(boost::python::operators<boost::python::op_add>(), boost::python::left_operand<int>());
     
    - Py_cpp uses overloading to register several variants of the same + Boost.Python uses overloading to register several variants of the same operation (more on this in the context of coercion). Again, several operators can be exported at once:
    @@ -181,7 +185,7 @@ bignum_class.def(boost::python::operators<(boost::python::op_sub | boost::pyt
     
    The type of the operand not mentioned is taken from the class being wrapped. In our example, the class object is bignum_class, and thus the - other operand's type is “BigNum const&”. You can override + other operand's type is ``BigNum const&''. You can override this default by explicitly specifying a type in the operators template:
    @@ -189,15 +193,18 @@ bignum_class.def(boost::python::operators<boost::python::op_add, BigNum>()
     

    Note that automatic wrapping uses the expression - “left + right” and can be used uniformly + ``left + right'' and can be used uniformly regardless of whether the C++ operators are supplied as free functions -

    +
    + 
     BigNum operator+(BigNum, BigNum)
     
    - or as member - functions
    +
    +        or as member functions
    +
    +
     BigNum::operator+(BigNum).
    -
    +

    For the Python built-in functions pow() and @@ -218,8 +225,7 @@ namespace boost { namespace python {

    In some cases, automatic wrapping of operators may be impossible or undesirable. Suppose, for example, that the modulo operation for BigNums - is defined by a set of functions mod() (for automatic - wrapping, we would need operator%()): + is defined by a set of functions called mod():

     BigNum mod(BigNum const& left, BigNum const& right);
    @@ -228,8 +234,9 @@ BigNum mod(int left, BigNum const& right);
     

    - In order to create the Python operator "__mod__" from these functions, we - have to wrap them manually: + For automatic wrapping of the modulo function, operator%() would be needed. + Therefore, the mod()-functions must be wrapped manually. That is, we have + to export them explicitly with the Python special name "__mod__":

     bignum_class.def((BigNum (*)(BigNum const&, BigNum const&))&mod, "__mod__");
    @@ -237,8 +244,8 @@ bignum_class.def((BigNum (*)(BigNum const&, int))&mod, "__mod__");
     

    - The third form (with int as left operand) cannot be wrapped - this way. We must first create a function rmod() with the + The third form of mod() (with int as left operand) cannot + be wrapped directly. We must first create a function rmod() with the operands reversed:

    @@ -248,7 +255,7 @@ BigNum rmod(BigNum const& right, int left)
     }
     
    - This function must be wrapped under the name "__rmod__": + This function must be wrapped under the name "__rmod__" (standing for "reverse mod"):
     bignum_class.def(&rmod,  "__rmod__");
    @@ -261,9 +268,9 @@ bignum_class.def(&rmod,  "__rmod__");
           

    Automatic and manual wrapping can be mixed arbitrarily. Note that you cannot overload the same operator for a given extension class on both - “int” and “float”, because Python implicitly + ``int'' and ``float'', because Python implicitly converts these types into each other. Thus, the overloaded variant - found first (be it “int“ or “float”) will be + found first (be it ``int`` or ``float'') will be used for either of the two types.

    Coercion

    @@ -271,18 +278,18 @@ bignum_class.def(&rmod, "__rmod__"); Plain Python can only execute operators with identical types on the left and right hand side. If it encounters an expression where the types of - the left and right operand differ, it tries to coerce these type to a + the left and right operand differ, it tries to coerce these types to a common type before invoking the actual operator. Implementing good coercion functions can be difficult if many type combinations must be supported.

    - Py_cpp solves this problem the same way that C++ does: with overloading. This technique drastically simplifies the code neccessary to support operators: you just register - operators for all desired type combinations, and py_cpp automatically + operators for all desired type combinations, and Boost.Python automatically ensures that the correct function is called in each case; there is no need for user-defined coercion functions. To enable operator - overloading, py_cpp provides a standard coercion which is implicitly + overloading, Boost.Python provides a standard coercion which is implicitly registered whenever automatic operator wrapping is used.

    If you wrap all operator functions manually, but still want to use @@ -295,7 +302,7 @@ bignum_class.def_standard_coerce();

    If you encounter a situation where you absolutely need a customized - coercion, you can overload the "__coerce__" operator itself. The signature + coercion, you can still define the "__coerce__" operator manually. The signature of a coercion function should look like one of the following (the first is the safest): @@ -310,13 +317,22 @@ PyObject* custom_coerce(PyObject* left, PyObject* right); converted to the same type. Such a function is wrapped as usual:
    +// this must be called before any use of automatic operator  
    +// wrapping or a call to some_class.def_standard_coerce()
     some_class.def(&custom_coerce, "__coerce__");
     
    - Note that the later use of automatic operator wrapping on a - class_builder or a call to - “some_class.def_standard_coerce()” will cause any - custom coercion function to be replaced by the standard one. + Note that the standard coercion (defined by use of automatic + operator wrapping on a class_builder or a call to + class_builder::def_standard_coerce()) will never be applied if + a custom coercion function has been registered. Therefore, in + your coercion function you should call + +
    +boost::python::standard_coerce(left, right);
    +
    + + for all cases that you don't want to handle yourself.

    The Ternary pow() Operator

    @@ -330,7 +346,7 @@ some_class.def(&custom_coerce, "__coerce__"); this is done as usual:
    -BigNum power(BigNum const& first, BigNum const& second, BigNum const& module);
    +BigNum power(BigNum const& first, BigNum const& second, BigNum const& modulus);
     typedef BigNum (ternary_function1)(const BigNum&, const BigNum&, const BigNum&);
     ...
     bignum_class.def((ternary_function1)&power,  "__pow__");
    @@ -353,19 +369,19 @@ bignum_class.def((ternary_function2)&power,  "__pow__");
     
    In the second variant, however, BigNum appears only as second - argument, and in the last one it is the third argument. These functions - must be presented to py_cpp such that that the BigNum + argument, and in the last one it's the third argument. These functions + must be presented to Boost.Python such that that the BigNum argument appears in first position:
     BigNum rpower(BigNum const& second, int first, int modulus)
     {
    -    return power(first, second, third);
    +    return power(first, second, modulus);
     }
     
    -BigNum rrpower(BigNum const& third, int first, int second)
    +BigNum rrpower(BigNum const& modulus, int first, int second)
     {
    -    return power(first, second, third);
    +    return power(first, second, modulus);
     }
     
    @@ -381,8 +397,8 @@ Note that "__rrpow__" is an extension not present in plain Python.

    Table of Automatically Wrapped Methods

    - Py_cpp can automatically wrap the following + Boost.Python can automatically wrap the following special methods:

    @@ -630,15 +646,16 @@ Note that "__rrpow__" is an extension not present in plain Python. to Python's iteration and access protocols. These protocols differ considerably from the ones found in C++. For example, Python's typical iteration idiom looks like +

     for i in S:
    -
    +
    -while in C++ one writes + while in C++ one writes
    -for (iterator i = S.begin(), end = S.end(); i != end)
    -
    +for (iterator i = S.begin(), end = S.end(); i != end; ++i) +

    One could try to wrap C++ iterators in order to carry the C++ idiom into Python. However, this does not work very well because @@ -655,12 +672,12 @@ for (iterator i = S.begin(), end = S.end(); i != end)

    It is a better idea to support the standard Python + href="http://www.python.org/doc/current/ref/sequence-types.html">Python sequence and mapping protocols for your wrapped containers. These operators have to be wrapped manually because there are no corresponding C++ operators that could be used for automatic wrapping. The Python documentation lists the relevant + "http://www.python.org/doc/current/ref/sequence-types.html"> container operators. In particular, expose __getitem__, __setitem__ and remember to raise the appropriate Python exceptions (PyExc_IndexError for sequences, @@ -689,7 +706,7 @@ void throw_key_error_if_end( // Define some simple wrapper functions which match the Python protocol // for __getitem__, __setitem__, and __delitem__. Just as in Python, a -// free function with a “self” first parameter makes a fine class method. +// free function with a ``self'' first parameter makes a fine class method. const std::string& get_item(const StringMap& self, std::size_t key) { @@ -755,13 +772,13 @@ KeyError: 2

    Customized Attribute Access

    - Just like built-in Python classes, py_cpp extension classes support special + Just like built-in Python classes, Boost.Python extension classes support special the usual attribute access methods __getattr__, __setattr__, and __delattr__. Because writing these functions can be tedious in the common case where the attributes being accessed are - known statically, py_cpp checks the special names + known statically, Boost.Python checks the special names

    • @@ -774,10 +791,10 @@ KeyError: 2 to provide functional access to the attribute <name>. This facility can be used from C++ or entirely from Python. For example, the - following shows how we can implement a “computed attribute” in Python: + following shows how we can implement a ``computed attribute'' in Python:
      ->>> class Range(AnyPy_cppExtensionClass):
      +>>> class Range(AnyBoost.PythonExtensionClass):
       ...    def __init__(self, start, end):
       ...        self.start = start
       ...        self.end = end
      @@ -793,7 +810,7 @@ KeyError: 2
                Direct Access to Data Members
             
             

      - Py_cpp uses the special + Boost.Python uses the special __xxxattr__<name>__ functionality described above to allow direct access to data members through the following special functions on class_builder<> and @@ -873,14 +890,14 @@ if (PyInstance_Check(r)) { ...

      - Previous: Inheritance Next: A Peek Under the Hood Up: Top +Next: A Peek Under the Hood +Previous: Inheritance +Up: Top

      © Copyright David Abrahams and Ullrich Köthe 2000. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This - document is provided “as is” without express or implied + document is provided ``as is'' without express or implied warranty, and with no claim as to its suitability for any purpose.

      Updated: Nov 26, 2000 diff --git a/doc/template.html b/doc/template.html deleted file mode 100644 index 1d0cd7a4..00000000 --- a/doc/template.html +++ /dev/null @@ -1,26 +0,0 @@ - - - The Title Of This Page - -

      -

      - c++boost.gif (8819 bytes)The Title Of This - Page -

      -

      -

      - Prev: Previous - Next: Next - Up: Top -

      - © Copyright David Abrahams 2000. Permission to copy, use, modify, - sell and distribute this document is granted provided this copyright - notice appears in all copies. This document is provided "as is" without - express or implied warranty, and with no claim as to its suitability - for any purpose. -

      - Updated: Nov 26, 2000 -

      - diff --git a/doc/under-the-hood.html b/doc/under-the-hood.html index 288021cf..733f0f54 100644 --- a/doc/under-the-hood.html +++ b/doc/under-the-hood.html @@ -22,7 +22,7 @@ "example1.html#add_world_class">add it to the module it goes into the module's dictionary to be looked up under the name "world".

      - Py_cpp uses C++'s template argument deduction mechanism to determine the + Boost.Python uses C++'s template argument deduction mechanism to determine the types of arguments to functions (except constructors, for which we must provide an argument list because they can't be named in C++). Then, it calls the appropriate @@ -48,8 +48,8 @@ the top of your module's init function, then def the member functions later to avoid problems with inter-class dependencies.

      - Previous: Function Overloading - Next: Building a Module with Py_cpp + Next: Building a Module with Boost.Python + Previous: Special Method and Operator Support Up: Top

      © Copyright David Abrahams 2000. Permission to copy, use, modify, diff --git a/example/README b/example/README new file mode 100644 index 00000000..35d822d5 --- /dev/null +++ b/example/README @@ -0,0 +1,24 @@ +To get started with the Boost Python Library, use the examples +getting_started1.cpp and getting_started2.cpp. + +Examples for providing pickle support can be found in: + pickle1.cpp + pickle2.cpp + pickle3.cpp +See also: libs/python/doc/pickle.html + +Other advanced concepts are introduced by: + abstract.cpp + simple_vector.cpp + do_it_yourself_converters.cpp + +Examples for the cross-module support are provided by: + noncopyable_export.cpp + noncopyable_import.cpp + dvect.cpp + ivect.cpp +See also: libs/python/doc/cross_module.html + +The files example1.cpp and rwgk1.cpp are obsolete. They are only +included because the Visual Studio project in the build directory still +refers to them. diff --git a/example/abstract.cpp b/example/abstract.cpp new file mode 100644 index 00000000..97e38c2e --- /dev/null +++ b/example/abstract.cpp @@ -0,0 +1,32 @@ +// Example by Ullrich Koethe +#include "boost/python/class_builder.hpp" +#include + +struct Abstract +{ + virtual std::string test() = 0; +}; + +struct Abstract_callback: Abstract +{ + Abstract_callback(PyObject * self) + : m_self(self) + {} + + std::string test() + { + return boost::python::callback::call_method(m_self, "test"); + } + + PyObject * m_self; +}; + +BOOST_PYTHON_MODULE_INIT(abstract) +{ + boost::python::module_builder a("abstract"); + + boost::python::class_builder + a_class(a, "Abstract"); + a_class.def(boost::python::constructor<>()); // wrap a constructor + a_class.def(&Abstract::test, "test"); +} diff --git a/example/do_it_yourself_converters.cpp b/example/do_it_yourself_converters.cpp new file mode 100644 index 00000000..6d2d2d6a --- /dev/null +++ b/example/do_it_yourself_converters.cpp @@ -0,0 +1,128 @@ +// Example by Ralf W. Grosse-Kunstleve +/* + + This example shows how to convert a class from and to native + Python objects, such as tuples. + + We do not want to expose the helper class MillerIndex as an + Extension Class. However, in order to simplify the wrapper code, + we want to define from_python() and to_python() functions for + class MillerIndex. + + Consider the alternatives: + + - Expose MillerIndex as an Extension Class. + We need a constructor MillerIndex(python::tuple). + Python function calls become more complex: + foo(MillerIndex((1,2,3)) instead of foo((1,2,3)) + We need a method such as MillerIndex().as_tuple(). + + - Define a wrapper function for each function that we + want to expose, e.g.: + void add(const IndexingSet& ixset, const python::tuple PyMIx) + + The first alternative introduces a new type that the user has to + deal with. Other modules using Miller indices might organize them in + different ways, for example to increase runtime efficiency for + important procedures. This means, the user has to know how to + convert between the different kinds of Miller index representations. + This can quickly become a nuisance. Relying on native Python data + structures minimizes the number of special types the user has to + learn and convert. Of course, this argument is only valid for + small and relatively simply classes. + + If there are many member functions with MillerIndex arguments, the + second alternative is impractical, and concentrating the conversion + mechanism in one central place is essential for code + maintainability. An added benefit is that more convenient (smarter) + conversion functions can be provided without cluttering the rest of + the wrapper code. + + */ + +#include +#include +#include +namespace python = boost::python; + +namespace { // Avoid cluttering the global namespace. + + // The helper class. + // + class MillerIndex { + public: + int v[3]; + }; + + // The main class. Imagine that there are MANY member functions + // like add() and get(). + // + class IndexingSet { + private: + std::vector VMIx; + public: + void add(const MillerIndex& MIx) { VMIx.push_back(MIx); } + MillerIndex get(std::size_t i) const { return VMIx[i]; } + }; +} + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE + + // Convert a Python tuple to a MillerIndex object. + // + MillerIndex from_python(PyObject* p, python::type) + { + python::tuple tup + = python::tuple(python::ref(p, python::ref::increment_count)); + if (tup.size() != 3) { + PyErr_SetString(PyExc_ValueError, + "expecting exactly 3 values in tuple."); + throw python::error_already_set(); + } + MillerIndex result; + for (int i = 0; i < 3; i++) + result.v[i] = from_python(tup[i].get(), python::type()); + return result; + } + + // Similar conversion for MillerIndex objects passed by value. + // Not actually used, but included to show the principle. + // + MillerIndex from_python(PyObject* p, python::type) + { + return from_python(p, python::type()); + } + + // Convert a MillerIndex object to a Python tuple. + // + PyObject* to_python(const MillerIndex& hkl) + { + python::tuple result(3); + for (int i = 0; i < 3; i++) + result.set_item(i, python::ref(to_python(hkl.v[i]))); + return result.reference().release(); + } + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +BOOST_PYTHON_MODULE_INIT(do_it_yourself_converters) +{ + try + { + // Create an object representing this extension module. + python::module_builder this_module("do_it_yourself_converters"); + + // Create the Python type object for our extension class. + python::class_builder ixset_class(this_module, "IndexingSet"); + + // Add the __init__ function. + ixset_class.def(python::constructor<>()); + // Add the member functions. + ixset_class.def(&IndexingSet::add, "add"); + ixset_class.def(&IndexingSet::get, "get"); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} diff --git a/example/dvect.cpp b/example/dvect.cpp new file mode 100644 index 00000000..4d109c96 --- /dev/null +++ b/example/dvect.cpp @@ -0,0 +1,56 @@ +// Example by Ralf W. Grosse-Kunstleve +// See root/libs/python/doc/cross_module.html for an introduction. + +#include "dvect.h" +#include "ivect.h" +#include +namespace python = boost::python; + +namespace { + +# include "dvect_conversions.cpp" +# include "ivect_conversions.cpp" + + vects::ivect dvect_as_ivect(const vects::dvect& dv) + { + vects::ivect iv(dv.size()); + vects::ivect::iterator iviter = iv.begin(); + for (int i = 0; i < dv.size(); i++) iviter[i] = static_cast(dv[i]); + return iv; + } +} + +# ifdef BOOST_MSVC // fixes for JIT debugging +# include +extern "C" void structured_exception_translator(unsigned int, EXCEPTION_POINTERS*) +{ + throw; +} +extern "C" void (*old_translator)(unsigned int, EXCEPTION_POINTERS*) + = _set_se_translator(structured_exception_translator); +# endif + +BOOST_PYTHON_MODULE_INIT(dvect) +{ + try + { + python::module_builder this_module("dvect"); + + python::class_builder dvect_class(this_module, "dvect"); + python::export_converters(dvect_class); + + python::import_converters ivect_converters("ivect", "ivect"); + + dvect_class.def(python::constructor()); + dvect_class.def(&vects::dvect::as_tuple, "as_tuple"); + dvect_class.def(dvect_as_ivect, "as_ivect"); + +# include "dvect_defs.cpp" +# include "ivect_defs.cpp" + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} + diff --git a/example/dvect.h b/example/dvect.h new file mode 100644 index 00000000..8ffe7b50 --- /dev/null +++ b/example/dvect.h @@ -0,0 +1,32 @@ +#ifndef DVECT_H +#define DVECT_H + +#include +#include + +namespace vects { + + struct dvect : public std::vector + { + dvect() : std::vector() {} + dvect(size_t n) : std::vector(n) {} + dvect(boost::python::tuple tuple) : std::vector(tuple.size()) + { + std::vector::iterator v_it = begin(); + for (int i = 0; i < tuple.size(); i++) + v_it[i] = BOOST_PYTHON_CONVERSION::from_python(tuple[i].get(), + boost::python::type()); + } + + boost::python::tuple as_tuple() const + { + boost::python::tuple t(size()); + for (int i = 0; i < size(); i++) + t.set_item(i, + boost::python::ref(BOOST_PYTHON_CONVERSION::to_python((*this)[i]))); + return t; + } + }; +} + +#endif // DVECT_H diff --git a/example/dvect_conversions.cpp b/example/dvect_conversions.cpp new file mode 100644 index 00000000..21527243 --- /dev/null +++ b/example/dvect_conversions.cpp @@ -0,0 +1,51 @@ + // basics first: const reference converters + boost::python::tuple const_dvect_reference_as_tuple(const vects::dvect& dv) + { + return dv.as_tuple(); + } + + // to_python smart pointer conversions + std::auto_ptr dvect_as_auto_ptr(const vects::dvect& dv) + { + return std::auto_ptr(new vects::dvect(dv)); + } + boost::shared_ptr dvect_as_shared_ptr(const vects::dvect& dv) + { + return boost::shared_ptr(new vects::dvect(dv)); + } + + // smart pointers passed by value + boost::python::ref auto_ptr_value_dvect_as_tuple(std::auto_ptr dv) + { + if (dv.get() == 0) return boost::python::ref(Py_None, boost::python::ref::increment_count); + return dv->as_tuple().reference(); + } + boost::python::ref shared_ptr_value_dvect_as_tuple(boost::shared_ptr dv) + { + if (dv.get() == 0) return boost::python::ref(Py_None, boost::python::ref::increment_count); + return dv->as_tuple().reference(); + } + + // smart pointers passed by reference + boost::python::ref auto_ptr_reference_dvect_as_tuple(std::auto_ptr& dv) + { + if (dv.get() == 0) return boost::python::ref(Py_None, boost::python::ref::increment_count); + return dv->as_tuple().reference(); + } + boost::python::ref shared_ptr_reference_dvect_as_tuple(boost::shared_ptr& dv) + { + if (dv.get() == 0) return boost::python::ref(Py_None, boost::python::ref::increment_count); + return dv->as_tuple().reference(); + } + + // smart pointers passed by const reference + boost::python::ref auto_ptr_const_reference_dvect_as_tuple(const std::auto_ptr& dv) + { + if (dv.get() == 0) return boost::python::ref(Py_None, boost::python::ref::increment_count); + return dv->as_tuple().reference(); + } + boost::python::ref shared_ptr_const_reference_dvect_as_tuple(const boost::shared_ptr& dv) + { + if (dv.get() == 0) return boost::python::ref(Py_None, boost::python::ref::increment_count); + return dv->as_tuple().reference(); + } diff --git a/example/dvect_defs.cpp b/example/dvect_defs.cpp new file mode 100644 index 00000000..2739b219 --- /dev/null +++ b/example/dvect_defs.cpp @@ -0,0 +1,13 @@ + this_module.def(dvect_as_auto_ptr, "dvect_as_auto_ptr"); + this_module.def(dvect_as_shared_ptr, "dvect_as_shared_ptr"); + + this_module.def(const_dvect_reference_as_tuple, "const_dvect_reference_as_tuple"); + + this_module.def(auto_ptr_value_dvect_as_tuple, "auto_ptr_value_dvect_as_tuple"); + this_module.def(shared_ptr_value_dvect_as_tuple, "shared_ptr_value_dvect_as_tuple"); + + this_module.def(auto_ptr_reference_dvect_as_tuple, "auto_ptr_reference_dvect_as_tuple"); + this_module.def(shared_ptr_reference_dvect_as_tuple, "shared_ptr_reference_dvect_as_tuple"); + + this_module.def(auto_ptr_const_reference_dvect_as_tuple, "auto_ptr_const_reference_dvect_as_tuple"); + this_module.def(shared_ptr_const_reference_dvect_as_tuple, "shared_ptr_const_reference_dvect_as_tuple"); diff --git a/example/getting_started1.cpp b/example/getting_started1.cpp new file mode 100644 index 00000000..c6a77723 --- /dev/null +++ b/example/getting_started1.cpp @@ -0,0 +1,32 @@ +// Example by Ralf W. Grosse-Kunstleve + +#include + +namespace { // Avoid cluttering the global namespace. + + // A couple of simple C++ functions that we want to expose to Python. + std::string greet() { return "hello, world"; } + int square(int number) { return number * number; } +} + +#include +namespace python = boost::python; + +// Python requires an exported function called init in every +// extension module. This is where we build the module contents. +BOOST_PYTHON_MODULE_INIT(getting_started1) +{ + try + { + // Create an object representing this extension module. + python::module_builder this_module("getting_started1"); + + // Add regular functions to the module. + this_module.def(greet, "greet"); + this_module.def(square, "square"); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} diff --git a/example/getting_started2.cpp b/example/getting_started2.cpp new file mode 100644 index 00000000..9121b1a0 --- /dev/null +++ b/example/getting_started2.cpp @@ -0,0 +1,52 @@ +// Example by Ralf W. Grosse-Kunstleve + +#include +#include + +namespace { // Avoid cluttering the global namespace. + + // A friendly class. + class hello + { + public: + hello(const std::string& country) { this->country = country; } + std::string greet() const { return "Hello from " + country; } + private: + std::string country; + }; + + // A function taking a hello object as an argument. + std::string invite(const hello& w) { + return w.greet() + "! Please come soon!"; + } +} + +#include +namespace python = boost::python; + +BOOST_PYTHON_MODULE_INIT(getting_started2) +{ + try + { + // Create an object representing this extension module. + python::module_builder this_module("getting_started2"); + + // Create the Python type object for our extension class. + python::class_builder hello_class(this_module, "hello"); + + // Add the __init__ function. + hello_class.def(python::constructor()); + // Add a regular member function. + hello_class.def(&hello::greet, "greet"); + + // Add invite() as a regular function to the module. + this_module.def(invite, "invite"); + + // Even better, invite() can also be made a member of hello_class!!! + hello_class.def(invite, "invite"); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} diff --git a/example/ivect.cpp b/example/ivect.cpp new file mode 100644 index 00000000..848d693e --- /dev/null +++ b/example/ivect.cpp @@ -0,0 +1,56 @@ +// Example by Ralf W. Grosse-Kunstleve +// See root/libs/python/doc/cross_module.html for an introduction. + +#include "dvect.h" +#include "ivect.h" +#include +namespace python = boost::python; + +namespace { + +# include "dvect_conversions.cpp" +# include "ivect_conversions.cpp" + + vects::dvect ivect_as_dvect(const vects::ivect& iv) + { + vects::dvect dv(iv.size()); + vects::dvect::iterator dviter = dv.begin(); + for (int i = 0; i < iv.size(); i++) dviter[i] = static_cast(iv[i]); + return dv; + } +} + +# ifdef BOOST_MSVC // fixes for JIT debugging +# include +extern "C" void structured_exception_translator(unsigned int, EXCEPTION_POINTERS*) +{ + throw; +} +extern "C" void (*old_translator)(unsigned int, EXCEPTION_POINTERS*) + = _set_se_translator(structured_exception_translator); +# endif + +BOOST_PYTHON_MODULE_INIT(ivect) +{ + try + { + python::module_builder this_module("ivect"); + + python::class_builder ivect_class(this_module, "ivect"); + python::export_converters(ivect_class); + + python::import_converters dvect_converters("dvect", "dvect"); + + ivect_class.def(python::constructor()); + ivect_class.def(&vects::ivect::as_tuple, "as_tuple"); + ivect_class.def(ivect_as_dvect, "as_dvect"); + +# include "dvect_defs.cpp" +# include "ivect_defs.cpp" + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} + diff --git a/example/ivect.h b/example/ivect.h new file mode 100644 index 00000000..a0187307 --- /dev/null +++ b/example/ivect.h @@ -0,0 +1,32 @@ +#ifndef IVECT_H +#define IVECT_H + +#include +#include + +namespace vects { + + struct ivect : public std::vector + { + ivect() : std::vector() {} + ivect(size_t n) : std::vector(n) {} + ivect(boost::python::tuple tuple) : std::vector(tuple.size()) + { + std::vector::iterator v_it = begin(); + for (int i = 0; i < tuple.size(); i++) + v_it[i] = BOOST_PYTHON_CONVERSION::from_python(tuple[i].get(), + boost::python::type()); + } + + boost::python::tuple as_tuple() const + { + boost::python::tuple t(size()); + for (int i = 0; i < size(); i++) + t.set_item(i, + boost::python::ref(BOOST_PYTHON_CONVERSION::to_python((*this)[i]))); + return t; + } + }; +} + +#endif // IVECT_H diff --git a/example/ivect_conversions.cpp b/example/ivect_conversions.cpp new file mode 100644 index 00000000..4f59573d --- /dev/null +++ b/example/ivect_conversions.cpp @@ -0,0 +1,51 @@ + // basics first: const reference converters + boost::python::tuple const_ivect_reference_as_tuple(const vects::ivect& iv) + { + return iv.as_tuple(); + } + + // to_python smart pointer conversions + std::auto_ptr ivect_as_auto_ptr(const vects::ivect& iv) + { + return std::auto_ptr(new vects::ivect(iv)); + } + boost::shared_ptr ivect_as_shared_ptr(const vects::ivect& iv) + { + return boost::shared_ptr(new vects::ivect(iv)); + } + + // smart pointers passed by value + boost::python::ref auto_ptr_value_ivect_as_tuple(std::auto_ptr iv) + { + if (iv.get() == 0) return boost::python::ref(Py_None, boost::python::ref::increment_count); + return iv->as_tuple().reference(); + } + boost::python::ref shared_ptr_value_ivect_as_tuple(boost::shared_ptr iv) + { + if (iv.get() == 0) return boost::python::ref(Py_None, boost::python::ref::increment_count); + return iv->as_tuple().reference(); + } + + // smart pointers passed by reference + boost::python::ref auto_ptr_reference_ivect_as_tuple(std::auto_ptr& iv) + { + if (iv.get() == 0) return boost::python::ref(Py_None, boost::python::ref::increment_count); + return iv->as_tuple().reference(); + } + boost::python::ref shared_ptr_reference_ivect_as_tuple(boost::shared_ptr& iv) + { + if (iv.get() == 0) return boost::python::ref(Py_None, boost::python::ref::increment_count); + return iv->as_tuple().reference(); + } + + // smart pointers passed by const reference + boost::python::ref auto_ptr_const_reference_ivect_as_tuple(const std::auto_ptr& iv) + { + if (iv.get() == 0) return boost::python::ref(Py_None, boost::python::ref::increment_count); + return iv->as_tuple().reference(); + } + boost::python::ref shared_ptr_const_reference_ivect_as_tuple(const boost::shared_ptr& iv) + { + if (iv.get() == 0) return boost::python::ref(Py_None, boost::python::ref::increment_count); + return iv->as_tuple().reference(); + } diff --git a/example/ivect_defs.cpp b/example/ivect_defs.cpp new file mode 100644 index 00000000..811c243d --- /dev/null +++ b/example/ivect_defs.cpp @@ -0,0 +1,13 @@ + this_module.def(ivect_as_auto_ptr, "ivect_as_auto_ptr"); + this_module.def(ivect_as_shared_ptr, "ivect_as_shared_ptr"); + + this_module.def(const_ivect_reference_as_tuple, "const_ivect_reference_as_tuple"); + + this_module.def(auto_ptr_value_ivect_as_tuple, "auto_ptr_value_ivect_as_tuple"); + this_module.def(shared_ptr_value_ivect_as_tuple, "shared_ptr_value_ivect_as_tuple"); + + this_module.def(auto_ptr_reference_ivect_as_tuple, "auto_ptr_reference_ivect_as_tuple"); + this_module.def(shared_ptr_reference_ivect_as_tuple, "shared_ptr_reference_ivect_as_tuple"); + + this_module.def(auto_ptr_const_reference_ivect_as_tuple, "auto_ptr_const_reference_ivect_as_tuple"); + this_module.def(shared_ptr_const_reference_ivect_as_tuple, "shared_ptr_const_reference_ivect_as_tuple"); diff --git a/example/noncopyable.h b/example/noncopyable.h new file mode 100644 index 00000000..de7b3672 --- /dev/null +++ b/example/noncopyable.h @@ -0,0 +1,14 @@ +#ifndef NONCOPYABLE_H +#define NONCOPYABLE_H + +class store +{ + private: + store(const store&) { } // Disable the copy constructor. + int number; + public: + store(const int i) : number(i) { } + int recall() const { return number; } +}; + +#endif // NONCOPYABLE_H diff --git a/example/noncopyable_export.cpp b/example/noncopyable_export.cpp new file mode 100644 index 00000000..b118abb8 --- /dev/null +++ b/example/noncopyable_export.cpp @@ -0,0 +1,35 @@ +// Example by Ralf W. Grosse-Kunstleve +// See root/libs/python/doc/cross_module.html for an introduction. + +#include +namespace python = boost::python; + +#include "noncopyable.h" + +# ifdef BOOST_MSVC // fixes for JIT debugging +# include +extern "C" void structured_exception_translator(unsigned int, EXCEPTION_POINTERS*) +{ + throw; +} +extern "C" void (*old_translator)(unsigned int, EXCEPTION_POINTERS*) + = _set_se_translator(structured_exception_translator); +# endif + +BOOST_PYTHON_MODULE_INIT(noncopyable_export) +{ + try + { + python::module_builder this_module("noncopyable_export"); + + python::class_builder store_class(this_module, "store"); + python::export_converters_noncopyable(store_class); + + store_class.def(python::constructor()); + store_class.def(&store::recall, "recall"); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} diff --git a/example/noncopyable_import.cpp b/example/noncopyable_import.cpp new file mode 100644 index 00000000..66c6457d --- /dev/null +++ b/example/noncopyable_import.cpp @@ -0,0 +1,52 @@ +// Example by Ralf W. Grosse-Kunstleve +// See root/libs/python/doc/cross_module.html for an introduction. + +#include +namespace python = boost::python; + +#include "noncopyable.h" + +namespace { // Avoid cluttering the global namespace. + + // A function with store objects as both input and output parameters. + // Because the copy constructor is disabled, we cannot pass a store + // object by value. Instead, we pass a smart pointer. + std::auto_ptr add_stores(const store& s1, const store& s2) + { + int sum = s1.recall() + s2.recall(); + std::auto_ptr ss = std::auto_ptr(new store(sum)); + return ss; + } +} + +# ifdef BOOST_MSVC // fixes for JIT debugging +# include +extern "C" void structured_exception_translator(unsigned int, EXCEPTION_POINTERS*) +{ + throw; +} +extern "C" void (*old_translator)(unsigned int, EXCEPTION_POINTERS*) + = _set_se_translator(structured_exception_translator); +# endif + +BOOST_PYTHON_MODULE_INIT(noncopyable_import) +{ + try + { + python::module_builder this_module("noncopyable_import"); + + python::import_converters + dvect_converters("noncopyable_export", "store"); + + // Imagine all the additional classes with member functions + // that have store objects as input and output parameters. + // Lots and lots of them. + // However, to keep this example simple, we only define a + // module-level function. + this_module.def(add_stores, "add_stores"); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} diff --git a/example/pickle1.cpp b/example/pickle1.cpp new file mode 100644 index 00000000..cdd78989 --- /dev/null +++ b/example/pickle1.cpp @@ -0,0 +1,64 @@ +// Example by Ralf W. Grosse-Kunstleve + +/* + This example shows how to make an Extension Class "pickleable". + + The world class below can be fully restored by passing the + appropriate argument to the constructor. Therefore it is sufficient + to define the pickle interface method __getinitargs__. + + For more information refer to boost/libs/python/doc/pickle.html. + */ + +#include + +#include +namespace python = boost::python; + +namespace { // Avoid cluttering the global namespace. + + // A friendly class. + class world + { + private: + std::string country; + int secret_number; + public: + world(const std::string& country) : secret_number(0) { + this->country = country; + } + std::string greet() const { return "Hello from " + country + "!"; } + std::string get_country() const { return country; } + }; + + // Support for pickle. + python::ref world_getinitargs(const world& w) { + python::tuple result(1); + result.set_item(0, w.get_country()); + return result.reference(); + } +} + +BOOST_PYTHON_MODULE_INIT(pickle1) +{ + try + { + // Create an object representing this extension module. + python::module_builder this_module("pickle1"); + + // Create the Python type object for our extension class. + python::class_builder world_class(this_module, "world"); + + // Add the __init__ function. + world_class.def(python::constructor()); + // Add a regular member function. + world_class.def(&world::greet, "greet"); + + // Support for pickle. + world_class.def(world_getinitargs, "__getinitargs__"); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} diff --git a/example/pickle2.cpp b/example/pickle2.cpp new file mode 100644 index 00000000..d6aa3201 --- /dev/null +++ b/example/pickle2.cpp @@ -0,0 +1,100 @@ +// Example by Ralf W. Grosse-Kunstleve + +/* + This example shows how to make an Extension Class "pickleable". + + The world class below contains member data (secret_number) that + cannot be restored by any of the constructors. Therefore it is + necessary to provide the __getstate__/__setstate__ pair of pickle + interface methods. + + For simplicity, the __dict__ is not included in the result of + __getstate__. This is not generally recommended, but a valid + approach if it is anticipated that the object's __dict__ will + always be empty. Note that safety guard are provided to catch the + cases where this assumption is not true. + + pickle3.cpp shows how to include the object's __dict__ in the + result of __getstate__. + + For more information refer to boost/libs/python/doc/pickle.html. + */ + +#include + +#include +namespace python = boost::python; + +namespace { // Avoid cluttering the global namespace. + + // A friendly class. + class world + { + public: + world(const std::string& country) : secret_number(0) { + this->country = country; + } + std::string greet() const { return "Hello from " + country + "!"; } + std::string get_country() const { return country; } + void set_secret_number(int number) { secret_number = number; } + int get_secret_number() const { return secret_number; } + private: + std::string country; + int secret_number; + }; + + // Support for pickle. + + using BOOST_PYTHON_CONVERSION::from_python; + + python::ref world_getinitargs(const world& w) { + python::tuple result(1); + result.set_item(0, w.get_country()); + return result.reference(); // returning the reference avoids the copying. + } + + python::ref world_getstate(const world& w) { + python::tuple result(1); + result.set_item(0, w.get_secret_number()); + return result.reference(); // returning the reference avoids the copying. + } + + void world_setstate(world& w, python::tuple state) { + if (state.size() != 1) { + PyErr_SetString(PyExc_ValueError, + "Unexpected argument in call to __setstate__."); + throw python::error_already_set(); + } + int number = from_python(state[0].get(), python::type()); + if (number != 42) + w.set_secret_number(number); + } +} + +BOOST_PYTHON_MODULE_INIT(pickle2) +{ + try + { + // Create an object representing this extension module. + python::module_builder this_module("pickle2"); + + // Create the Python type object for our extension class. + python::class_builder world_class(this_module, "world"); + + // Add the __init__ function. + world_class.def(python::constructor()); + // Add a regular member function. + world_class.def(&world::greet, "greet"); + world_class.def(&world::get_secret_number, "get_secret_number"); + world_class.def(&world::set_secret_number, "set_secret_number"); + + // Support for pickle. + world_class.def(world_getinitargs, "__getinitargs__"); + world_class.def(world_getstate, "__getstate__"); + world_class.def(world_setstate, "__setstate__"); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} diff --git a/example/pickle3.cpp b/example/pickle3.cpp new file mode 100644 index 00000000..bfa7dc54 --- /dev/null +++ b/example/pickle3.cpp @@ -0,0 +1,150 @@ +// Example by Ralf W. Grosse-Kunstleve + +/* + This example shows how to make an Extension Class "pickleable". + + The world class below contains member data (secret_number) that + cannot be restored by any of the constructors. Therefore it is + necessary to provide the __getstate__/__setstate__ pair of pickle + interface methods. + + The object's __dict__ is included in the result of __getstate__. + This requires more code (compare with pickle2.cpp), but is + unavoidable if the object's __dict__ is not always empty. + + For more information refer to boost/libs/python/doc/pickle.html. + */ + +#include + +#include +namespace python = boost::python; + +namespace boost { namespace python { + + ref getattr(PyObject* o, const std::string& attr_name) { + return ref(PyObject_GetAttrString(o, const_cast(attr_name.c_str()))); + } + ref getattr(const ref& r, const std::string& attr_name) { + return getattr(r.get(), attr_name); + } + +}} + +namespace { // Avoid cluttering the global namespace. + + // A friendly class. + class world + { + public: + world(const std::string& country) : secret_number(0) { + this->country = country; + } + std::string greet() const { return "Hello from " + country + "!"; } + std::string get_country() const { return country; } + void set_secret_number(int number) { secret_number = number; } + int get_secret_number() const { return secret_number; } + private: + std::string country; + int secret_number; + }; + + // Support for pickle. + python::ref world_getinitargs(const world& w) { + python::tuple result(1); + result.set_item(0, w.get_country()); + return result.reference(); // returning the reference avoids the copying. + } + + python::ref world_getstate(python::tuple const & args, + python::dictionary const & keywords); + + PyObject* world_setstate(python::tuple const & args, + python::dictionary const & keywords); +} + +BOOST_PYTHON_MODULE_INIT(pickle3) +{ + try + { + // Create an object representing this extension module. + python::module_builder this_module("pickle3"); + + // Create the Python type object for our extension class. + python::class_builder world_class(this_module, "world"); + + // Add the __init__ function. + world_class.def(python::constructor()); + // Add a regular member function. + world_class.def(&world::greet, "greet"); + world_class.def(&world::get_secret_number, "get_secret_number"); + world_class.def(&world::set_secret_number, "set_secret_number"); + + // Support for pickle. + world_class.def(world_getinitargs, "__getinitargs__"); + world_class.def_raw(world_getstate, "__getstate__"); + world_class.def_raw(world_setstate, "__setstate__"); + world_class.getstate_manages_dict(); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} + +namespace { + + using BOOST_PYTHON_CONVERSION::from_python; + using boost::python::type; + using boost::python::ref; + using boost::python::tuple; + using boost::python::list; + using boost::python::dictionary; + using boost::python::getattr; + + ref world_getstate(tuple const & args, dictionary const & keywords) + { + if(args.size() != 1 || keywords.size() != 0) { + PyErr_SetString(PyExc_TypeError, "wrong number of arguments"); + throw boost::python::argument_error(); + } + const world& w = from_python(args[0].get(), type()); + ref mydict = getattr(args[0], "__dict__"); + tuple result(2); + // store the object's __dict__ + result.set_item(0, mydict); + // store the internal state of the C++ object + result.set_item(1, w.get_secret_number()); + return result.reference(); // returning the reference avoids the copying. + } + + PyObject* world_setstate(tuple const & args, dictionary const & keywords) + { + if(args.size() != 2 || keywords.size() != 0) { + PyErr_SetString(PyExc_TypeError, "wrong number of arguments"); + throw boost::python::argument_error(); + } + world& w = from_python(args[0].get(), type()); + ref mydict = getattr(args[0], "__dict__"); + tuple state = from_python(args[1].get(), type()); + if (state.size() != 2) { + PyErr_SetString(PyExc_ValueError, + "Unexpected argument in call to __setstate__."); + throw python::error_already_set(); + } + // restore the object's __dict__ + dictionary odict = from_python(mydict.get(), type()); + const dictionary& pdict = from_python(state[0].get(), type()); + list pkeys(pdict.keys()); + for (int i = 0; i < pkeys.size(); i++) { + ref k(pkeys[i]); + //odict[k] = pdict[k]; // XXX memory leak! + odict[k] = pdict.get_item(k); // this does not leak. + } + // restore the internal state of the C++ object + int number = from_python(state[1].get(), type()); + if (number != 42) + w.set_secret_number(number); + return python::detail::none(); + } +} diff --git a/example/rwgk1.cpp b/example/rwgk1.cpp new file mode 100644 index 00000000..b21ae4d1 --- /dev/null +++ b/example/rwgk1.cpp @@ -0,0 +1,41 @@ +#include + +namespace { // Avoid cluttering the global namespace. + + // A couple of simple C++ functions that we want to expose to Python. + std::string greet() { return "hello, world"; } + int square(int number) { return number * number; } +} + +#include + +namespace python = boost::python; + +// Python requires an exported function called init in every +// extension module. This is where we build the module contents. +extern "C" +#ifdef _WIN32 +__declspec(dllexport) +#endif +void initrwgk1() +{ + try + { + // Create an object representing this extension module. + python::module_builder this_module("rwgk1"); + + // Add regular functions to the module. + this_module.def(greet, "greet"); + this_module.def(square, "square"); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} + +// Win32 DLL boilerplate +#if defined(_WIN32) +#include +extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) { return 1; } +#endif // _WIN32 diff --git a/example/simple_vector.cpp b/example/simple_vector.cpp new file mode 100644 index 00000000..3499c8f7 --- /dev/null +++ b/example/simple_vector.cpp @@ -0,0 +1,111 @@ +// Example by Ralf W. Grosse-Kunstleve + +#include +namespace python = boost::python; + +namespace { // Avoid cluttering the global namespace. + + // A wrapper is used to define additional constructors. + // + struct vector_double_wrapper: std::vector + { + // Tell the compiler how to convert a base class object to + // this wrapper object. + vector_double_wrapper(PyObject*, const std::vector& vd) + : std::vector(vd) {} + + vector_double_wrapper(PyObject* self) + : std::vector() {} + + vector_double_wrapper(PyObject* self, int n) + : std::vector(n) {} + + vector_double_wrapper(PyObject* self, python::tuple tuple) + : std::vector(tuple.size()) + { + std::vector::iterator vd = begin(); + for (int i = 0; i < tuple.size(); i++) + vd[i] = BOOST_PYTHON_CONVERSION::from_python(tuple[i].get(), + python::type()); + } + }; + + void raise_vector_IndexError() { + PyErr_SetString(PyExc_IndexError, "vector index out of range"); + throw python::error_already_set(); + } + + double getitem(const std::vector& vd, std::size_t key) { + if (key >= vd.size()) raise_vector_IndexError(); + return vd[key]; + } + + void setitem(std::vector& vd, std::size_t key, double d) { + if (key >= vd.size()) raise_vector_IndexError(); + std::vector::iterator vditer = vd.begin(); + vditer[key] = d; + } + + void delitem(std::vector& vd, std::size_t key) { + if (key >= vd.size()) raise_vector_IndexError(); + std::vector::iterator vditer = vd.begin(); + vd.erase(vditer + key); + } + + // Convert vector_double to a regular Python tuple. + // + python::tuple as_tuple(const std::vector& vd) + { + python::tuple t(vd.size()); + for (int i = 0; i < vd.size(); i++) t.set_item(i, + python::ref(BOOST_PYTHON_CONVERSION::to_python(vd[i]))); + return t; + } + + // Function returning a vector_double object to Python. + // + std::vector foo(int n) + { + std::vector vd(n); + std::vector::iterator vditer = vd.begin(); + for (int i = 0; i < n; i++) vditer[i] = double(i); + return vd; + } + + // Same as foo(), but avoid copying on return. + // + std::auto_ptr > bar(int n) + { + std::auto_ptr > vdptr(new std::vector(n)); + std::vector::iterator vditer = vdptr->begin(); + for (int i = 0; i < n; i++) vditer[i] = double(10 * i); + return vdptr; + } +} + +BOOST_PYTHON_MODULE_INIT(simple_vector) +{ + try + { + python::module_builder this_module("simple_vector"); + + python::class_builder, vector_double_wrapper> + vector_double(this_module, "vector_double"); + + vector_double.def(python::constructor<>()); + vector_double.def(python::constructor()); + vector_double.def(python::constructor()); + vector_double.def(&std::vector::size, "__len__"); + vector_double.def(getitem, "__getitem__"); + vector_double.def(setitem, "__setitem__"); + vector_double.def(delitem, "__delitem__"); + vector_double.def(as_tuple, "as_tuple"); + + this_module.def(foo, "foo"); + this_module.def(bar, "bar"); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} diff --git a/example/test_abstract.py b/example/test_abstract.py new file mode 100644 index 00000000..a48aff1b --- /dev/null +++ b/example/test_abstract.py @@ -0,0 +1,24 @@ +# Example by Ullrich Koethe +r'''>>> from abstract import * + >>> class A(Abstract): + ... def __init__(self, text): + ... Abstract.__init__(self) # call the base class constructor + ... self.text = text + ... def test(self): # implement abstract function + ... return self.text + ... + >>> a = A("Hello") + >>> a.test() + 'Hello' +''' + +def run(args = None): + if args is not None: + import sys + sys.argv = args + import doctest, test_abstract + return doctest.testmod(test_abstract) + +if __name__ == '__main__': + import sys + sys.exit(run()[0]) diff --git a/example/test_cross_module.py b/example/test_cross_module.py new file mode 100644 index 00000000..c5e2bef6 --- /dev/null +++ b/example/test_cross_module.py @@ -0,0 +1,140 @@ +r'''>>> import tst_noncopyable + >>> tst_noncopyable.f() + 1 + 2 + 3 + >>> import tst_dvect1 + >>> tst_dvect1.f() + (1.0, 2.0, 3.0, 4.0, 5.0) + (1, 2, 3, 4, 5) + (1, 2, 3, 4, 5) + (1, 2, 3, 4, 5) + (1, 2, 3, 4, 5) + (1, 2, 3, 4, 5) + (1, 2, 3, 4, 5) + >>> import tst_ivect1 + >>> tst_ivect1.f() + (1, 2, 3, 4, 5) + (1.0, 2.0, 3.0, 4.0, 5.0) + (1.0, 2.0, 3.0, 4.0, 5.0) + (1.0, 2.0, 3.0, 4.0, 5.0) + (1.0, 2.0, 3.0, 4.0, 5.0) + (1.0, 2.0, 3.0, 4.0, 5.0) + (1.0, 2.0, 3.0, 4.0, 5.0) + >>> import sys + >>> if ("--broken-auto-ptr" in sys.argv): + ... broken_auto_ptr = 1 + ... else: + ... broken_auto_ptr = 0 + >>> import tst_dvect2 + >>> tst_dvect2.f(broken_auto_ptr) + 1. auto_ptr_value_ivect_as_tuple + (1, 2, 3, 4, 5) + 2. auto_ptr_value_ivect_as_tuple + None + 1. auto_ptr_value_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 2. auto_ptr_value_dvect_as_tuple + None + 1. shared_ptr_value_ivect_as_tuple + (1, 2, 3, 4, 5) + 2. shared_ptr_value_ivect_as_tuple + (1, 2, 3, 4, 5) + 1. shared_ptr_value_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 2. shared_ptr_value_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 1. auto_ptr_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 2. auto_ptr_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 1. auto_ptr_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 2. auto_ptr_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 1. shared_ptr_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 2. shared_ptr_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 1. shared_ptr_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 2. shared_ptr_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 1. auto_ptr_const_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 2. auto_ptr_const_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 1. auto_ptr_const_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 2. auto_ptr_const_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 1. shared_ptr_const_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 2. shared_ptr_const_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 1. shared_ptr_const_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 2. shared_ptr_const_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + >>> import tst_ivect2 + >>> tst_ivect2.f(broken_auto_ptr) + 1. auto_ptr_value_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 2. auto_ptr_value_dvect_as_tuple + None + 1. auto_ptr_value_ivect_as_tuple + (1, 2, 3, 4, 5) + 2. auto_ptr_value_ivect_as_tuple + None + 1. shared_ptr_value_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 2. shared_ptr_value_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 1. shared_ptr_value_ivect_as_tuple + (1, 2, 3, 4, 5) + 2. shared_ptr_value_ivect_as_tuple + (1, 2, 3, 4, 5) + 1. auto_ptr_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 2. auto_ptr_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 1. auto_ptr_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 2. auto_ptr_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 1. shared_ptr_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 2. shared_ptr_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 1. shared_ptr_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 2. shared_ptr_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 1. auto_ptr_const_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 2. auto_ptr_const_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 1. auto_ptr_const_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 2. auto_ptr_const_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 1. shared_ptr_const_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 2. shared_ptr_const_reference_dvect_as_tuple + (1.0, 2.0, 3.0, 4.0, 5.0) + 1. shared_ptr_const_reference_ivect_as_tuple + (1, 2, 3, 4, 5) + 2. shared_ptr_const_reference_ivect_as_tuple + (1, 2, 3, 4, 5) +''' + +def run(args = None): + if args is not None: + import sys + sys.argv = args + import doctest, test_cross_module + return doctest.testmod(test_cross_module) + +if __name__ == '__main__': + import sys + sys.exit(run()[0]) diff --git a/example/test_do_it_yourself_converters.py b/example/test_do_it_yourself_converters.py new file mode 100644 index 00000000..17240750 --- /dev/null +++ b/example/test_do_it_yourself_converters.py @@ -0,0 +1,23 @@ +r'''>>> import do_it_yourself_converters + >>> ixset = do_it_yourself_converters.IndexingSet() + >>> ixset.add((1,2,3)) + >>> ixset.add((4,5,6)) + >>> ixset.add((7,8,9)) + >>> print ixset.get(0) + (1, 2, 3) + >>> print ixset.get(1) + (4, 5, 6) + >>> print ixset.get(2) + (7, 8, 9) +''' + +def run(args = None): + if args is not None: + import sys + sys.argv = args + import doctest, test_do_it_yourself_converters + return doctest.testmod(test_do_it_yourself_converters) + +if __name__ == '__main__': + import sys + sys.exit(run()[0]) diff --git a/example/example1.py b/example/test_example1.py similarity index 94% rename from example/example1.py rename to example/test_example1.py index 0e3a9a18..3a30cb5b 100644 --- a/example/example1.py +++ b/example/test_example1.py @@ -44,7 +44,8 @@ def run(args = None): import sys sys.argv = args import doctest, test_example1 - doctest.testmod(test_example1) + return doctest.testmod(test_example1) if __name__ == '__main__': - run() + import sys + sys.exit(run()[0]) diff --git a/example/test_getting_started1.py b/example/test_getting_started1.py new file mode 100644 index 00000000..cd8fb59e --- /dev/null +++ b/example/test_getting_started1.py @@ -0,0 +1,18 @@ +r'''>>> import getting_started1 + >>> print getting_started1.greet() + hello, world + >>> number = 11 + >>> print number, '*', number, '=', getting_started1.square(number) + 11 * 11 = 121 +''' + +def run(args = None): + if args is not None: + import sys + sys.argv = args + import doctest, test_getting_started1 + return doctest.testmod(test_getting_started1) + +if __name__ == '__main__': + import sys + sys.exit(run()[0]) diff --git a/example/test_getting_started2.py b/example/test_getting_started2.py new file mode 100644 index 00000000..ccfaa4f1 --- /dev/null +++ b/example/test_getting_started2.py @@ -0,0 +1,31 @@ +r'''>>> from getting_started2 import * + >>> hi = hello('California') + >>> hi.greet() + 'Hello from California' + >>> invite(hi) + 'Hello from California! Please come soon!' + >>> hi.invite() + 'Hello from California! Please come soon!' + + >>> class wordy(hello): + ... def greet(self): + ... return hello.greet(self) + ', where the weather is fine' + ... + >>> hi2 = wordy('Florida') + >>> hi2.greet() + 'Hello from Florida, where the weather is fine' + >>> invite(hi2) + 'Hello from Florida! Please come soon!' +''' + +def run(args = None): + if args is not None: + import sys + sys.argv = args + import doctest, test_getting_started2 + return doctest.testmod(test_getting_started2) + +if __name__ == '__main__': + import sys + sys.exit(run()[0]) + diff --git a/example/test_pickle1.py b/example/test_pickle1.py new file mode 100644 index 00000000..48c76a5f --- /dev/null +++ b/example/test_pickle1.py @@ -0,0 +1,33 @@ +r'''>>> import pickle1 + >>> import re + >>> import pickle + >>> pickle1.world.__module__ + 'pickle1' + >>> pickle1.world.__safe_for_unpickling__ + 1 + >>> pickle1.world.__reduce__() + 'world' + >>> assert re.match( + ... "\(, \('Hello',\)\)", + ... repr(pickle1.world('Hello').__reduce__())) + >>> + >>> wd = pickle1.world('California') + >>> pstr = pickle.dumps(wd) + >>> wl = pickle.loads(pstr) + >>> print wd.greet() + Hello from California! + >>> print wl.greet() + Hello from California! +''' + +def run(args = None): + if args is not None: + import sys + sys.argv = args + import doctest, test_pickle1 + return doctest.testmod(test_pickle1) + +if __name__ == '__main__': + import sys + sys.exit(run()[0]) + diff --git a/example/test_pickle2.py b/example/test_pickle2.py new file mode 100644 index 00000000..bafa9875 --- /dev/null +++ b/example/test_pickle2.py @@ -0,0 +1,47 @@ +r'''>>> import pickle2 + >>> import re + >>> import pickle + >>> pickle2.world.__module__ + 'pickle2' + >>> pickle2.world.__safe_for_unpickling__ + 1 + >>> pickle2.world.__reduce__() + 'world' + >>> assert re.match( + ... "\(, \('Hello',\), \(0,\)\)", + ... repr(pickle2.world('Hello').__reduce__())) + >>> + >>> for number in (24, 42): + ... wd = pickle2.world('California') + ... wd.set_secret_number(number) + ... pstr = pickle.dumps(wd) + ... wl = pickle.loads(pstr) + ... print wd.greet(), wd.get_secret_number() + ... print wl.greet(), wl.get_secret_number() + Hello from California! 24 + Hello from California! 24 + Hello from California! 42 + Hello from California! 0 + +# Now show that the __dict__ is not taken care of. + >>> wd = pickle2.world('California') + >>> wd.x = 1 + >>> wd.__dict__ + {'x': 1} + >>> try: pstr = pickle.dumps(wd) + ... except RuntimeError, err: print err[0] + ... + Incomplete pickle support (__getstate_manages_dict__ not set) +''' + +def run(args = None): + if args is not None: + import sys + sys.argv = args + import doctest, test_pickle2 + return doctest.testmod(test_pickle2) + +if __name__ == '__main__': + import sys + sys.exit(run()[0]) + diff --git a/example/test_pickle3.py b/example/test_pickle3.py new file mode 100644 index 00000000..36da735d --- /dev/null +++ b/example/test_pickle3.py @@ -0,0 +1,39 @@ +r'''>>> import pickle3 + >>> import re + >>> import pickle + >>> pickle3.world.__module__ + 'pickle3' + >>> pickle3.world.__safe_for_unpickling__ + 1 + >>> pickle3.world.__reduce__() + 'world' + >>> assert re.match( + ... "\(, \('Hello',\), \(\{\}, 0\)\)", + ... repr(pickle3.world('Hello').__reduce__())) + >>> + >>> for number in (24, 42): + ... wd = pickle3.world('California') + ... wd.set_secret_number(number) + ... wd.x = 2 * number + ... wd.y = 'y' * number + ... wd.z = 3. * number + ... pstr = pickle.dumps(wd) + ... wl = pickle.loads(pstr) + ... print wd.greet(), wd.get_secret_number(), wd.__dict__ + ... print wl.greet(), wl.get_secret_number(), wl.__dict__ + Hello from California! 24 {'z': 72.0, 'x': 48, 'y': 'yyyyyyyyyyyyyyyyyyyyyyyy'} + Hello from California! 24 {'z': 72.0, 'x': 48, 'y': 'yyyyyyyyyyyyyyyyyyyyyyyy'} + Hello from California! 42 {'z': 126.0, 'x': 84, 'y': 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'} + Hello from California! 0 {'z': 126.0, 'x': 84, 'y': 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'} +''' + +def run(args = None): + if args is not None: + import sys + sys.argv = args + import doctest, test_pickle3 + return doctest.testmod(test_pickle3) + +if __name__ == '__main__': + import sys + sys.exit(run()[0]) diff --git a/example/test_rwgk1.py b/example/test_rwgk1.py new file mode 100644 index 00000000..631eea3e --- /dev/null +++ b/example/test_rwgk1.py @@ -0,0 +1,19 @@ +r'''>>> import rwgk1 + >>> print rwgk1.greet() + hello, world + >>> number = 11 + >>> print number, '*', number, '=', rwgk1.square(number) + 11 * 11 = 121 +''' + +def run(args = None): + if args is not None: + import sys + sys.argv = args + import doctest, test_rwgk1 + return doctest.testmod(test_rwgk1) + +if __name__ == '__main__': + import sys + sys.exit(run()[0]) + diff --git a/example/test_simple_vector.py b/example/test_simple_vector.py new file mode 100644 index 00000000..c6a2cd59 --- /dev/null +++ b/example/test_simple_vector.py @@ -0,0 +1,42 @@ +r'''>>> import simple_vector + >>> v=simple_vector.vector_double() + >>> print v.as_tuple() + () + >>> v=simple_vector.vector_double(5) + >>> print v.as_tuple() + (0.0, 0.0, 0.0, 0.0, 0.0) + >>> print len(v) + 5 + >>> v=simple_vector.vector_double((3,4,5)) + >>> print v.as_tuple() + (3.0, 4.0, 5.0) + >>> print v[1] + 4.0 + >>> v[1] = 40 + >>> print v.as_tuple() + (3.0, 40.0, 5.0) + >>> for e in v: + ... print e + 3.0 + 40.0 + 5.0 + >>> del v[1] + >>> print v.as_tuple() + (3.0, 5.0) + >>> print simple_vector.foo(11).as_tuple() + (0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0) + >>> print simple_vector.bar(12).as_tuple() + (0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, 110.0) +''' + +def run(args = None): + if args is not None: + import sys + sys.argv = args + import doctest, test_simple_vector + return doctest.testmod(test_simple_vector) + +if __name__ == '__main__': + import sys + sys.exit(run()[0]) + diff --git a/example/tst_dvect1.py b/example/tst_dvect1.py new file mode 100644 index 00000000..22315528 --- /dev/null +++ b/example/tst_dvect1.py @@ -0,0 +1,20 @@ +def f(): + import dvect + dv = dvect.dvect((1,2,3,4,5)) + print dv.as_tuple() + iv = dv.as_ivect() + print iv.as_tuple() + print dvect.const_ivect_reference_as_tuple(iv) + aiv = dvect.ivect_as_auto_ptr(iv) + print dvect.const_ivect_reference_as_tuple(aiv) + siv = dvect.ivect_as_shared_ptr(iv) + print dvect.const_ivect_reference_as_tuple(siv) + print aiv.as_tuple() + print siv.as_tuple() + +if (__name__ == "__main__"): + import sys, string + n = 1 + if (len(sys.argv) > 1): n = string.atoi(sys.argv[1]) + for i in xrange(n): + f() diff --git a/example/tst_dvect2.py b/example/tst_dvect2.py new file mode 100644 index 00000000..90d381a7 --- /dev/null +++ b/example/tst_dvect2.py @@ -0,0 +1,104 @@ +def f(broken_auto_ptr): + import dvect + import ivect + # + dv = dvect.dvect((1,2,3,4,5)) + iv = dv.as_ivect() + # + aiv = dvect.ivect_as_auto_ptr(iv) + print '1. auto_ptr_value_ivect_as_tuple' + print ivect.auto_ptr_value_ivect_as_tuple(aiv) + print '2. auto_ptr_value_ivect_as_tuple' + if (not broken_auto_ptr): + print ivect.auto_ptr_value_ivect_as_tuple(aiv) + else: + print None + # + adv = dvect.dvect_as_auto_ptr(dv) + print '1. auto_ptr_value_dvect_as_tuple' + print ivect.auto_ptr_value_dvect_as_tuple(adv) + print '2. auto_ptr_value_dvect_as_tuple' + if (not broken_auto_ptr): + print ivect.auto_ptr_value_dvect_as_tuple(adv) + else: + print None + # + siv = dvect.ivect_as_shared_ptr(iv) + print '1. shared_ptr_value_ivect_as_tuple' + print ivect.shared_ptr_value_ivect_as_tuple(siv) + print '2. shared_ptr_value_ivect_as_tuple' + print ivect.shared_ptr_value_ivect_as_tuple(siv) + # + sdv = dvect.dvect_as_shared_ptr(dv) + print '1. shared_ptr_value_dvect_as_tuple' + print ivect.shared_ptr_value_dvect_as_tuple(sdv) + print '2. shared_ptr_value_dvect_as_tuple' + print ivect.shared_ptr_value_dvect_as_tuple(sdv) + # + aiv = dvect.ivect_as_auto_ptr(iv) + print '1. auto_ptr_reference_ivect_as_tuple' + print ivect.auto_ptr_reference_ivect_as_tuple(aiv) + print '2. auto_ptr_reference_ivect_as_tuple' + print ivect.auto_ptr_reference_ivect_as_tuple(aiv) + # + adv = dvect.dvect_as_auto_ptr(dv) + print '1. auto_ptr_reference_dvect_as_tuple' + print ivect.auto_ptr_reference_dvect_as_tuple(adv) + print '2. auto_ptr_reference_dvect_as_tuple' + print ivect.auto_ptr_reference_dvect_as_tuple(adv) + # + siv = dvect.ivect_as_shared_ptr(iv) + print '1. shared_ptr_reference_ivect_as_tuple' + print ivect.shared_ptr_reference_ivect_as_tuple(siv) + print '2. shared_ptr_reference_ivect_as_tuple' + print ivect.shared_ptr_reference_ivect_as_tuple(siv) + # + sdv = dvect.dvect_as_shared_ptr(dv) + print '1. shared_ptr_reference_dvect_as_tuple' + print ivect.shared_ptr_reference_dvect_as_tuple(sdv) + print '2. shared_ptr_reference_dvect_as_tuple' + print ivect.shared_ptr_reference_dvect_as_tuple(sdv) + # + aiv = dvect.ivect_as_auto_ptr(iv) + print '1. auto_ptr_const_reference_ivect_as_tuple' + print ivect.auto_ptr_const_reference_ivect_as_tuple(aiv) + print '2. auto_ptr_const_reference_ivect_as_tuple' + print ivect.auto_ptr_const_reference_ivect_as_tuple(aiv) + # + adv = dvect.dvect_as_auto_ptr(dv) + print '1. auto_ptr_const_reference_dvect_as_tuple' + print ivect.auto_ptr_const_reference_dvect_as_tuple(adv) + print '2. auto_ptr_const_reference_dvect_as_tuple' + print ivect.auto_ptr_const_reference_dvect_as_tuple(adv) + # + siv = dvect.ivect_as_shared_ptr(iv) + print '1. shared_ptr_const_reference_ivect_as_tuple' + print ivect.shared_ptr_const_reference_ivect_as_tuple(siv) + print '2. shared_ptr_const_reference_ivect_as_tuple' + print ivect.shared_ptr_const_reference_ivect_as_tuple(siv) + # + sdv = dvect.dvect_as_shared_ptr(dv) + print '1. shared_ptr_const_reference_dvect_as_tuple' + print ivect.shared_ptr_const_reference_dvect_as_tuple(sdv) + print '2. shared_ptr_const_reference_dvect_as_tuple' + print ivect.shared_ptr_const_reference_dvect_as_tuple(sdv) + +if (__name__ == "__main__"): + import sys, string + broken_auto_ptr = 0 + n = 1 + + if len(sys.argv) > 1: + argv = [] + + for x in sys.argv: + if x != '--broken-auto-ptr': + argv.append(x) + broken_auto_ptr = argv != sys.argv + sys.argv = argv + + if len(sys.argv) > 1: + n = string.atoi(sys.argv[1]) + + for i in xrange(n): + f(broken_auto_ptr) diff --git a/example/tst_ivect1.py b/example/tst_ivect1.py new file mode 100644 index 00000000..7369fdbf --- /dev/null +++ b/example/tst_ivect1.py @@ -0,0 +1,20 @@ +def f(): + import ivect + iv = ivect.ivect((1,2,3,4,5)) + print iv.as_tuple() + dv = iv.as_dvect() + print dv.as_tuple() + print ivect.const_dvect_reference_as_tuple(dv) + adv = ivect.dvect_as_auto_ptr(dv) + print ivect.const_dvect_reference_as_tuple(adv) + sdv = ivect.dvect_as_shared_ptr(dv) + print ivect.const_dvect_reference_as_tuple(sdv) + print adv.as_tuple() + print sdv.as_tuple() + +if (__name__ == "__main__"): + import sys, string + n = 1 + if (len(sys.argv) > 1): n = string.atoi(sys.argv[1]) + for i in xrange(n): + f() diff --git a/example/tst_ivect2.py b/example/tst_ivect2.py new file mode 100644 index 00000000..a9e6aeef --- /dev/null +++ b/example/tst_ivect2.py @@ -0,0 +1,104 @@ +def f(broken_auto_ptr): + import ivect + import dvect + # + iv = ivect.ivect((1,2,3,4,5)) + dv = iv.as_dvect() + # + adv = ivect.dvect_as_auto_ptr(dv) + print '1. auto_ptr_value_dvect_as_tuple' + print dvect.auto_ptr_value_dvect_as_tuple(adv) + print '2. auto_ptr_value_dvect_as_tuple' + if (not broken_auto_ptr): + print dvect.auto_ptr_value_dvect_as_tuple(adv) + else: + print None + # + aiv = ivect.ivect_as_auto_ptr(iv) + print '1. auto_ptr_value_ivect_as_tuple' + print dvect.auto_ptr_value_ivect_as_tuple(aiv) + print '2. auto_ptr_value_ivect_as_tuple' + if (not broken_auto_ptr): + print dvect.auto_ptr_value_ivect_as_tuple(aiv) + else: + print None + # + sdv = ivect.dvect_as_shared_ptr(dv) + print '1. shared_ptr_value_dvect_as_tuple' + print dvect.shared_ptr_value_dvect_as_tuple(sdv) + print '2. shared_ptr_value_dvect_as_tuple' + print dvect.shared_ptr_value_dvect_as_tuple(sdv) + # + siv = ivect.ivect_as_shared_ptr(iv) + print '1. shared_ptr_value_ivect_as_tuple' + print dvect.shared_ptr_value_ivect_as_tuple(siv) + print '2. shared_ptr_value_ivect_as_tuple' + print dvect.shared_ptr_value_ivect_as_tuple(siv) + # + adv = ivect.dvect_as_auto_ptr(dv) + print '1. auto_ptr_reference_dvect_as_tuple' + print dvect.auto_ptr_reference_dvect_as_tuple(adv) + print '2. auto_ptr_reference_dvect_as_tuple' + print dvect.auto_ptr_reference_dvect_as_tuple(adv) + # + aiv = ivect.ivect_as_auto_ptr(iv) + print '1. auto_ptr_reference_ivect_as_tuple' + print dvect.auto_ptr_reference_ivect_as_tuple(aiv) + print '2. auto_ptr_reference_ivect_as_tuple' + print dvect.auto_ptr_reference_ivect_as_tuple(aiv) + # + sdv = ivect.dvect_as_shared_ptr(dv) + print '1. shared_ptr_reference_dvect_as_tuple' + print dvect.shared_ptr_reference_dvect_as_tuple(sdv) + print '2. shared_ptr_reference_dvect_as_tuple' + print dvect.shared_ptr_reference_dvect_as_tuple(sdv) + # + siv = ivect.ivect_as_shared_ptr(iv) + print '1. shared_ptr_reference_ivect_as_tuple' + print dvect.shared_ptr_reference_ivect_as_tuple(siv) + print '2. shared_ptr_reference_ivect_as_tuple' + print dvect.shared_ptr_reference_ivect_as_tuple(siv) + # + adv = ivect.dvect_as_auto_ptr(dv) + print '1. auto_ptr_const_reference_dvect_as_tuple' + print dvect.auto_ptr_const_reference_dvect_as_tuple(adv) + print '2. auto_ptr_const_reference_dvect_as_tuple' + print dvect.auto_ptr_const_reference_dvect_as_tuple(adv) + # + aiv = ivect.ivect_as_auto_ptr(iv) + print '1. auto_ptr_const_reference_ivect_as_tuple' + print dvect.auto_ptr_const_reference_ivect_as_tuple(aiv) + print '2. auto_ptr_const_reference_ivect_as_tuple' + print dvect.auto_ptr_const_reference_ivect_as_tuple(aiv) + # + sdv = ivect.dvect_as_shared_ptr(dv) + print '1. shared_ptr_const_reference_dvect_as_tuple' + print dvect.shared_ptr_const_reference_dvect_as_tuple(sdv) + print '2. shared_ptr_const_reference_dvect_as_tuple' + print dvect.shared_ptr_const_reference_dvect_as_tuple(sdv) + # + siv = ivect.ivect_as_shared_ptr(iv) + print '1. shared_ptr_const_reference_ivect_as_tuple' + print dvect.shared_ptr_const_reference_ivect_as_tuple(siv) + print '2. shared_ptr_const_reference_ivect_as_tuple' + print dvect.shared_ptr_const_reference_ivect_as_tuple(siv) + +if (__name__ == "__main__"): + import sys, string + broken_auto_ptr = 0 + n = 1 + + if len(sys.argv) > 1: + argv = [] + + for x in sys.argv: + if x != '--broken-auto-ptr': + argv.append(x) + broken_auto_ptr = argv != sys.argv + sys.argv = argv + + if len(sys.argv) > 1: + n = string.atoi(sys.argv[1]) + + for i in xrange(n): + f(broken_auto_ptr) diff --git a/example/tst_noncopyable.py b/example/tst_noncopyable.py new file mode 100644 index 00000000..155910a5 --- /dev/null +++ b/example/tst_noncopyable.py @@ -0,0 +1,16 @@ +def f(): + import noncopyable_export + import noncopyable_import + s1 = noncopyable_export.store(1) + print s1.recall() + s2 = noncopyable_export.store(2) + print s2.recall() + s3 = noncopyable_import.add_stores(s1, s2) + print s3.recall() + +if (__name__ == "__main__"): + import sys, string + n = 1 + if (len(sys.argv) > 1): n = string.atoi(sys.argv[1]) + for i in xrange(n): + f() diff --git a/include/boost/python/class_builder.hpp b/include/boost/python/class_builder.hpp index 9e88f049..4a9ec1b2 100644 --- a/include/boost/python/class_builder.hpp +++ b/include/boost/python/class_builder.hpp @@ -1,3 +1,6 @@ +// Revision History: +// Mar 03 01 added: pickle safety measures (Ralf W. Grosse-Kunstleve) + #ifndef CLASS_WRAPPER_DWA101000_H_ # define CLASS_WRAPPER_DWA101000_H_ @@ -24,11 +27,18 @@ class class_builder ~class_builder() {} + + inline void dict_defines_state() { + add(ref(BOOST_PYTHON_CONVERSION::to_python(1)), "__dict_defines_state__"); + } + inline void getstate_manages_dict() { + add(ref(BOOST_PYTHON_CONVERSION::to_python(1)), "__getstate_manages_dict__"); + } // define constructors template - void def(const signature& signature) - { m_class->def(signature); } + void def(const signature& s) + { m_class->def(s); } // export heterogeneous reverse-argument operators // (type of lhs: 'left', of rhs: 'right') diff --git a/include/boost/python/conversions.hpp b/include/boost/python/conversions.hpp index d11f7f14..47f80989 100644 --- a/include/boost/python/conversions.hpp +++ b/include/boost/python/conversions.hpp @@ -5,6 +5,11 @@ // // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. +// +// Revision History: +// 04 Mar 01 Fixed std::complex<> stuff to work with MSVC (David Abrahams) +// 03 Mar 01 added: converters for [plain] char and std::complex +// (Ralf W. Grosse-Kunstleve) #ifndef METHOD_DWA122899_H_ # define METHOD_DWA122899_H_ @@ -17,6 +22,17 @@ # include # include +# ifdef BOOST_MSVC6_OR_EARLIER +# pragma warning(push) +# pragma warning(disable:4275) // disable a bogus warning caused by +# endif + +# include + +# ifdef BOOST_MSVC6_OR_EARLIER +# pragma warning(pop) +# endif + BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // this is a gcc 2.95.2 bug workaround // This can be instantiated on an enum to provide the to_python/from_python @@ -70,6 +86,30 @@ inline void xdecref(T* p) xdecref_impl(reinterpret_cast(p_base)); } +namespace detail { + + void expect_complex(PyObject*); + + template + std::complex complex_from_python(PyObject* p, boost::python::type) + { + expect_complex(p); + + return std::complex( + static_cast(PyComplex_RealAsDouble(p)), + static_cast(PyComplex_ImagAsDouble(p))); + } + + template + PyObject* complex_to_python(const std::complex& sc) { + Py_complex pcc; + pcc.real = sc.real(); + pcc.imag = sc.imag(); + return PyComplex_FromCComplex(pcc); + } + +} + }} // namespace boost::python BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE @@ -100,6 +140,10 @@ PyObject* to_python(unsigned short); unsigned short from_python(PyObject*, boost::python::type); unsigned short from_python(PyObject*, boost::python::type); +PyObject* to_python(char); +char from_python(PyObject*, boost::python::type); +char from_python(PyObject*, boost::python::type); + PyObject* to_python(signed char); signed char from_python(PyObject*, boost::python::type); signed char from_python(PyObject*, boost::python::type); @@ -130,6 +174,36 @@ PyObject* to_python(const std::string& s); std::string from_python(PyObject*, boost::python::type); std::string from_python(PyObject*, boost::python::type); +inline PyObject* to_python(const std::complex& x) +{ + return boost::python::detail::complex_to_python(x); +} + +inline PyObject* to_python(const std::complex& x) +{ + return boost::python::detail::complex_to_python(x); +} + +inline std::complex from_python(PyObject* p, + boost::python::type >) { + return boost::python::detail::complex_from_python(p, boost::python::type()); +} + +inline std::complex from_python(PyObject* p, + boost::python::type&>) { + return boost::python::detail::complex_from_python(p, boost::python::type()); +} + +inline std::complex from_python(PyObject* p, + boost::python::type >) { + return boost::python::detail::complex_from_python(p, boost::python::type()); +} + +inline std::complex from_python(PyObject* p, + boost::python::type&>) { + return boost::python::detail::complex_from_python(p, boost::python::type()); +} + // For when your C++ function really wants to pass/return a PyObject* PyObject* to_python(PyObject*); PyObject* from_python(PyObject*, boost::python::type); @@ -304,6 +378,11 @@ inline unsigned short from_python(PyObject* p, boost::python::type()); } +inline char from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + inline signed char from_python(PyObject* p, boost::python::type) { return from_python(p, boost::python::type()); diff --git a/include/boost/python/cross_module.hpp b/include/boost/python/cross_module.hpp new file mode 100644 index 00000000..7c1fe507 --- /dev/null +++ b/include/boost/python/cross_module.hpp @@ -0,0 +1,322 @@ +/* (C) Copyright Ralf W. Grosse-Kunstleve 2001. Permission to copy, use, + modify, sell and distribute this software is granted provided this + copyright notice appears in all copies. This software is provided + "as is" without express or implied warranty, and with no claim as to + its suitability for any purpose. + + Revision History: + 17 Apr 01 merged into boost CVS trunk (Ralf W. Grosse-Kunstleve) +*/ + +/* Implementation of Boost.Python cross-module support. + See root/libs/python/doc/cross_module.html for details. +*/ + +#ifndef CROSS_MODULE_HPP +# define CROSS_MODULE_HPP + +# include + +namespace boost { namespace python { + struct import_error : error_already_set {}; + struct export_error : error_already_set {}; +}} + +namespace boost { namespace python { namespace detail { + +// Concept: throw exception if api_major is changed +// show warning on stderr if api_minor is changed +const int export_converters_api_major = 4; +const int export_converters_api_minor = 1; +extern const char* converters_attribute_name; +void* import_converter_object(const std::string& module_name, + const std::string& py_class_name, + const std::string& attribute_name); +void check_export_converters_api(const int importing_major, + const int importing_minor, + const int imported_major, + const int imported_minor); + +}}} + +// forward declaration +namespace boost { namespace python { namespace detail { +template class import_extension_class; +}}} + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE + +/* This class template is instantiated by import_converters. + This class is a look-alike of class python_extension_class_converters. + The converters in this class are wrappers that call converters + imported from another module. + To ensure that the dynamic loader resolves all symbols in the + intended way, the signature of all friend functions is changed with + respect to the original functions in class + python_extension_class_converters by adding an arbitrary additional + parameter with a default value, in this case "bool sig = false". + See also: comments for class export_converter_object_base below. + */ +template +class python_import_extension_class_converters +{ + public: + + friend python_import_extension_class_converters py_extension_class_converters(boost::python::type, bool sig = false) { + return python_import_extension_class_converters(); + } + + PyObject* to_python(const T& x) const { + return boost::python::detail::import_extension_class::get_converters()->to_python(x); + } + + friend T* from_python(PyObject* p, boost::python::type t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_Ts(p, t); + } + friend const T* from_python(PyObject* p, boost::python::type t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_cTs(p, t); + } + friend const T* from_python(PyObject* p, boost::python::type t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_cTscr(p, t); + } + friend T* from_python(PyObject* p, boost::python::type t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_Tscr(p, t); + } + friend T& from_python(PyObject* p, boost::python::type t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_Tr(p, t); + } + friend const T& from_python(PyObject* p, boost::python::type t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_cTr(p, t); + } + friend const T& from_python(PyObject* p, boost::python::type t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_T(p, t); + } + + friend std::auto_ptr& from_python(PyObject* p, boost::python::type&> t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_aTr(p, t); + } + friend std::auto_ptr from_python(PyObject* p, boost::python::type > t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_aT(p, t); + } + friend const std::auto_ptr& from_python(PyObject* p, boost::python::type&> t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_caTr(p, t); + } + friend PyObject* to_python(std::auto_ptr x, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->to_python(x); + } + + friend boost::shared_ptr& from_python(PyObject* p, boost::python::type&> t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_sTr(p, t); + } + friend const boost::shared_ptr& from_python(PyObject* p, boost::python::type > t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_sT(p, t); + } + friend const boost::shared_ptr& from_python(PyObject* p, boost::python::type&> t, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->from_python_csTr(p, t); + } + friend PyObject* to_python(boost::shared_ptr x, bool sig = false) { + return boost::python::detail::import_extension_class::get_converters()->to_python(x); + } +}; + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +namespace boost { namespace python { + +BOOST_PYTHON_IMPORT_CONVERSION(python_import_extension_class_converters); + +/* This class template is instantiated by export_converters(). + A pointer to this class is exported/imported via the Python API. + Using the Python API ensures maximum portability. + All member functions are virtual. This is, what we export/import + is essentially just a pointer to a vtbl. + To work around a deficiency of Visual C++ 6.0, the name of each + from_python() member functions is made unique by appending a few + characters (derived in a ad-hoc manner from the corresponding type). + */ +template +struct export_converter_object_base +{ + virtual int get_api_major() const { return detail::export_converters_api_major; } + virtual int get_api_minor() const { return detail::export_converters_api_minor; } + + virtual PyObject* to_python(const T& x) = 0; + + virtual T* from_python_Ts(PyObject* p, boost::python::type t) = 0; + virtual const T* from_python_cTs(PyObject* p, boost::python::type t) = 0; + virtual const T* from_python_cTscr(PyObject* p, boost::python::type t) = 0; + virtual T* from_python_Tscr(PyObject* p, boost::python::type t) = 0; + virtual T& from_python_Tr(PyObject* p, boost::python::type t) = 0; + virtual const T& from_python_cTr(PyObject* p, boost::python::type t) = 0; + virtual const T& from_python_T(PyObject* p, boost::python::type t) = 0; + + virtual std::auto_ptr& from_python_aTr(PyObject* p, boost::python::type&> t) = 0; + virtual std::auto_ptr from_python_aT(PyObject* p, boost::python::type > t) = 0; + virtual const std::auto_ptr& from_python_caTr(PyObject* p, boost::python::type&> t) = 0; + virtual PyObject* to_python(std::auto_ptr x) = 0; + + virtual boost::shared_ptr& from_python_sTr(PyObject* p, boost::python::type&> t) = 0; + virtual const boost::shared_ptr& from_python_sT(PyObject* p, boost::python::type > t) = 0; + virtual const boost::shared_ptr& from_python_csTr(PyObject* p, boost::python::type&> t) = 0; + virtual PyObject* to_python(boost::shared_ptr x) = 0; +}; + +// Converters to be used if T is not copyable. +template +struct export_converter_object_noncopyable : export_converter_object_base +{ + virtual PyObject* to_python(const T& x) { + PyErr_SetString(PyExc_RuntimeError, + "to_python(const T&) converter not exported"); + throw import_error(); + } + + virtual T* from_python_Ts(PyObject* p, boost::python::type t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + virtual const T* from_python_cTs(PyObject* p, boost::python::type t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + virtual const T* from_python_cTscr(PyObject* p, boost::python::type t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + virtual T* from_python_Tscr(PyObject* p, boost::python::type t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + virtual T& from_python_Tr(PyObject* p, boost::python::type t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + virtual const T& from_python_cTr(PyObject* p, boost::python::type t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + virtual const T& from_python_T(PyObject* p, boost::python::type t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + + virtual std::auto_ptr& from_python_aTr(PyObject* p, boost::python::type&> t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + virtual std::auto_ptr from_python_aT(PyObject* p, boost::python::type > t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + virtual const std::auto_ptr& from_python_caTr(PyObject* p, boost::python::type&> t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + virtual PyObject* to_python(std::auto_ptr x) { + return BOOST_PYTHON_CONVERSION::to_python(x); + } + + virtual boost::shared_ptr& from_python_sTr(PyObject* p, boost::python::type&> t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + virtual const boost::shared_ptr& from_python_sT(PyObject* p, boost::python::type > t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + virtual const boost::shared_ptr& from_python_csTr(PyObject* p, boost::python::type&> t) { + return BOOST_PYTHON_CONVERSION::from_python(p, t); + } + virtual PyObject* to_python(boost::shared_ptr x) { + return BOOST_PYTHON_CONVERSION::to_python(x); + } +}; + +// The addditional to_python() converter that can be used if T is copyable. +template +struct export_converter_object : export_converter_object_noncopyable +{ + virtual PyObject* to_python(const T& x) { + return BOOST_PYTHON_CONVERSION::py_extension_class_converters(boost::python::type()).to_python(x); + } +}; + +namespace detail { + +/* This class template is instantiated by import_converters. + Its purpose is to import the converter_object via the Python API. + The actual import is only done once. The pointer to the + imported converter object is kept in the static data member + imported_converters. + */ +template +class import_extension_class + : public python_import_extension_class_converters +{ + public: + inline import_extension_class(const char* module, const char* py_class) { + m_module = module; + m_py_class = py_class; + } + + static boost::python::export_converter_object_base* get_converters(); + + private: + static std::string m_module; + static std::string m_py_class; + static boost::python::export_converter_object_base* imported_converters; +}; + +template std::string import_extension_class::m_module; +template std::string import_extension_class::m_py_class; +template +boost::python::export_converter_object_base* +import_extension_class::imported_converters = 0; + +template +boost::python::export_converter_object_base* +import_extension_class::get_converters() { + if (imported_converters == 0) { + void* cobject + = import_converter_object(m_module, m_py_class, + converters_attribute_name); + imported_converters + = static_cast*>(cobject); + check_export_converters_api( + export_converters_api_major, + export_converters_api_minor, + imported_converters->get_api_major(), + imported_converters->get_api_minor()); + } + return imported_converters; +} + +}}} // namespace boost::python::detail + +namespace boost { namespace python { + +// Implementation of export_converters(). +template +void export_converters(class_builder& cb) +{ + static export_converter_object export_cvts; + cb.add( + ref(PyCObject_FromVoidPtr(reinterpret_cast(&export_cvts), NULL)), + detail::converters_attribute_name); +} + +// Implementation of export_converters_noncopyable(). +template +void export_converters_noncopyable(class_builder& cb) +{ + static export_converter_object_noncopyable export_cvts; + cb.add( + ref(PyCObject_FromVoidPtr(reinterpret_cast(&export_cvts), NULL)), + detail::converters_attribute_name); +} + +// Implementation of import_converters. +template +class import_converters + : python_import_extension_class_converters // Works around MSVC6.x/GCC2.95.2 bug described + // at the bottom of class_builder.hpp. +{ + public: + import_converters(const char* module, const char* py_class) + : m_class(new detail::import_extension_class(module, py_class)) + { } + private: + boost::shared_ptr > m_class; +}; + +}} // namespace boost::python + +#endif // CROSS_MODULE_HPP diff --git a/include/boost/python/detail/base_object.hpp b/include/boost/python/detail/base_object.hpp index f8ed665b..bf0faa7b 100644 --- a/include/boost/python/detail/base_object.hpp +++ b/include/boost/python/detail/base_object.hpp @@ -5,6 +5,9 @@ // // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. +// +// Revision History: +// Mar 01 01 Use PyObject_INIT() instead of trying to hand-initialize (David Abrahams) #ifndef BASE_OBJECT_DWA051600_H_ # define BASE_OBJECT_DWA051600_H_ @@ -46,9 +49,8 @@ base_object::base_object(PyTypeObject* type_obj) std:: #endif memset(bp, 0, sizeof(base_python_type)); - ob_refcnt = 1; - ob_type = type_obj; Py_INCREF(type_obj); + PyObject_INIT(bp, type_obj); } template diff --git a/include/boost/python/detail/config.hpp b/include/boost/python/detail/config.hpp index 2488bd8e..b6075368 100644 --- a/include/boost/python/detail/config.hpp +++ b/include/boost/python/detail/config.hpp @@ -6,6 +6,9 @@ // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. +// Revision History: +// 04 Mar 01 Some fixes so it will compile with Intel C++ (Dave Abrahams) + #ifndef CONFIG_DWA052200_H_ # define CONFIG_DWA052200_H_ @@ -21,7 +24,7 @@ # else # define BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE namespace boost { namespace python { # define BOOST_PYTHON_END_CONVERSION_NAMESPACE }} // namespace boost::python -# define BOOST_PYTHON_CONVERSION python +# define BOOST_PYTHON_CONVERSION boost::python # define BOOST_PYTHON_IMPORT_CONVERSION(x) void never_defined() // so we can follow the macro with a ';' # endif @@ -46,11 +49,18 @@ # endif // The STLport puts all of the standard 'C' library names in std (as far as the -// user is concerned), but without it you need a fix if you're using MSVC. -# if defined(BOOST_MSVC6_OR_EARLIER) && !defined(__STLPORT) +// user is concerned), but without it you need a fix if you're using MSVC or +// Intel C++ +# if defined(BOOST_MSVC_STD_ITERATOR) # define BOOST_CSTD_ # else # define BOOST_CSTD_ std # endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define BOOST_PYTHON_MODULE_INIT(name) extern "C" __declspec(dllexport) void init##name() +#else +# define BOOST_PYTHON_MODULE_INIT(name) extern "C" void init##name() +#endif + #endif // CONFIG_DWA052200_H_ diff --git a/include/boost/python/detail/extension_class.hpp b/include/boost/python/detail/extension_class.hpp index 20d48796..5c8e720a 100644 --- a/include/boost/python/detail/extension_class.hpp +++ b/include/boost/python/detail/extension_class.hpp @@ -6,9 +6,14 @@ // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. // -// This file automatically generated for 5-argument constructors by +// This file automatically generated for 10-argument constructors by // gen_extclass.python +// Revision History: +// 17 Apr 01 Comment added with reference to cross_module.hpp (R.W. Grosse-Kunstleve) +// 05 Mar 01 Fixed a bug which prevented auto_ptr values from being converted +// to_python (Dave Abrahams) + #ifndef EXTENSION_CLASS_DWA052000_H_ # define EXTENSION_CLASS_DWA052000_H_ @@ -22,6 +27,7 @@ # include # include # include +# include namespace boost { namespace python { @@ -61,7 +67,7 @@ T* check_non_null(T* p) return p; } -template class held_instance; +template class held_instance; typedef void* (*conversion_function_ptr)(void*); @@ -133,6 +139,26 @@ class class_registry static std::vector static_derived_class_info; }; +template +struct is_null_helper +{ + template + static bool test(Ptr x) { return x == 0; } +}; + +template <> +struct is_null_helper +{ + template + static bool test(const Ptr& x) { return x.get() == 0; } +}; + +template +bool is_null(const Ptr& x) +{ + return is_null_helper<(is_pointer::value)>::test(x); +} + }}} // namespace boost::python::detail BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE @@ -141,6 +167,14 @@ BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // and U. T is the class the user really intends to wrap. U is a class derived // from T with some virtual function overriding boilerplate, or if there are no // virtual functions, U = held_instance. +// +// A look-alike of this class in root/boost/python/cross_module.hpp +// is used for the implementation of the cross-module support +// (export_converters and import_converters). If from_python +// and to_python converters are added or removed from the class +// below, the class python_import_extension_class_converters has +// to be modified accordingly. +// template > class python_extension_class_converters { @@ -178,9 +212,9 @@ class python_extension_class_converters new boost::python::detail::instance_value_holder(result.get(), x))); return result.release(); } - - // Convert to T* - friend T* from_python(PyObject* obj, boost::python::type) + + friend + T* non_null_from_python(PyObject* obj, boost::python::type) { // downcast to an extension_instance, then find the actual T boost::python::detail::extension_instance* self = boost::python::detail::get_extension_instance(obj); @@ -201,9 +235,18 @@ class python_extension_class_converters throw boost::python::argument_error(); } - // Convert to PtrType, where PtrType can be dereferenced to obtain a T. + // Convert to T* + friend T* from_python(PyObject* obj, boost::python::type) + { + if (obj == Py_None) + return 0; + else + return non_null_from_python(obj, boost::python::type()); + } + + // Extract from obj a mutable reference to the PtrType object which is holding a T. template - static PtrType& ptr_from_python(PyObject* obj, boost::python::type) + static PtrType& smart_ptr_reference(PyObject* obj, boost::python::type) { // downcast to an extension_instance, then find the actual T boost::python::detail::extension_instance* self = boost::python::detail::get_extension_instance(obj); @@ -220,9 +263,29 @@ class python_extension_class_converters throw boost::python::argument_error(); } + // Extract from obj a reference to the PtrType object which is holding a + // T. If it weren't for auto_ptr, it would be a constant reference. Do not + // modify the referent except by copying an auto_ptr! If obj is None, the + // reference denotes a default-constructed PtrType template - static PyObject* ptr_to_python(PtrType x) + static PtrType& smart_ptr_value(PyObject* obj, boost::python::type) { + if (obj == Py_None) + { + static PtrType null_ptr; + return null_ptr; + } + return smart_ptr_reference(obj, boost::python::type()); + } + + template + static PyObject* smart_ptr_to_python(PtrType x) + { + if (boost::python::detail::is_null(x)) + { + return boost::python::detail::none(); + } + boost::python::reference result(create_instance()); result->add_implementation( std::auto_ptr( @@ -254,7 +317,7 @@ class python_extension_class_converters // Convert to T& friend T& from_python(PyObject* p, boost::python::type) - { return *boost::python::detail::check_non_null(from_python(p, boost::python::type())); } + { return *boost::python::detail::check_non_null(non_null_from_python(p, boost::python::type())); } // Convert to const T& friend const T& from_python(PyObject* p, boost::python::type) @@ -265,28 +328,28 @@ class python_extension_class_converters { return from_python(p, boost::python::type()); } friend std::auto_ptr& from_python(PyObject* p, boost::python::type&>) - { return ptr_from_python(p, boost::python::type >()); } + { return smart_ptr_reference(p, boost::python::type >()); } - friend std::auto_ptr& from_python(PyObject* p, boost::python::type >) - { return ptr_from_python(p, boost::python::type >()); } + friend std::auto_ptr from_python(PyObject* p, boost::python::type >) + { return smart_ptr_value(p, boost::python::type >()); } friend const std::auto_ptr& from_python(PyObject* p, boost::python::type&>) - { return ptr_from_python(p, boost::python::type >()); } + { return smart_ptr_value(p, boost::python::type >()); } friend PyObject* to_python(std::auto_ptr x) - { return ptr_to_python(x); } + { return smart_ptr_to_python(x); } friend boost::shared_ptr& from_python(PyObject* p, boost::python::type&>) - { return ptr_from_python(p, boost::python::type >()); } + { return smart_ptr_reference(p, boost::python::type >()); } - friend boost::shared_ptr& from_python(PyObject* p, boost::python::type >) - { return ptr_from_python(p, boost::python::type >()); } + friend const boost::shared_ptr& from_python(PyObject* p, boost::python::type >) + { return smart_ptr_value(p, boost::python::type >()); } friend const boost::shared_ptr& from_python(PyObject* p, boost::python::type&>) - { return ptr_from_python(p, boost::python::type >()); } + { return smart_ptr_value(p, boost::python::type >()); } friend PyObject* to_python(boost::shared_ptr x) - { return ptr_to_python(x); } + { return smart_ptr_to_python(x); } }; // Convert T to_python, instantiated on demand and only if there isn't a @@ -363,8 +426,8 @@ class extension_class ~extension_class(); // define constructors - template - inline void def(constructor) + template + inline void def(constructor) // The following incantation builds a signature1, signature2,... object. It // should _all_ get optimized away. { add_constructor( @@ -373,7 +436,12 @@ class extension_class prepend(type::id(), prepend(type::id(), prepend(type::id(), - signature0())))))); + prepend(type::id(), + prepend(type::id(), + prepend(type::id(), + prepend(type::id(), + prepend(type::id(), + signature0()))))))))))); } @@ -608,23 +676,33 @@ class extension_class // A simple wrapper over a T which allows us to use extension_class with a // single template parameter only. See extension_class, above. -template -class held_instance : public T +template +class held_instance : public Held { // There are no member functions: we want to avoid inadvertently overriding - // any virtual functions in T. + // any virtual functions in Held. public: - held_instance(PyObject*) : T() {} + held_instance(PyObject*) : Held() {} template - held_instance(PyObject*, A1 a1) : T(a1) {} + held_instance(PyObject*, A1 a1) : Held(a1) {} template - held_instance(PyObject*, A1 a1, A2 a2) : T(a1, a2) {} + held_instance(PyObject*, A1 a1, A2 a2) : Held(a1, a2) {} template - held_instance(PyObject*, A1 a1, A2 a2, A3 a3) : T(a1, a2, a3) {} + held_instance(PyObject*, A1 a1, A2 a2, A3 a3) : Held(a1, a2, a3) {} template - held_instance(PyObject*, A1 a1, A2 a2, A3 a3, A4 a4) : T(a1, a2, a3, a4) {} + held_instance(PyObject*, A1 a1, A2 a2, A3 a3, A4 a4) : Held(a1, a2, a3, a4) {} template - held_instance(PyObject*, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) : T(a1, a2, a3, a4, a5) {} + held_instance(PyObject*, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) : Held(a1, a2, a3, a4, a5) {} + template + held_instance(PyObject*, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) : Held(a1, a2, a3, a4, a5, a6) {} + template + held_instance(PyObject*, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) : Held(a1, a2, a3, a4, a5, a6, a7) {} + template + held_instance(PyObject*, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) : Held(a1, a2, a3, a4, a5, a6, a7, a8) {} + template + held_instance(PyObject*, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) : Held(a1, a2, a3, a4, a5, a6, a7, a8, a9) {} + template + held_instance(PyObject*, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) : Held(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {} }; // Abstract base class for all obj holders. Base for template class @@ -676,6 +754,21 @@ public: template instance_value_holder(extension_instance* p, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) : m_held(p, a1, a2, a3, a4, a5) {} + template + instance_value_holder(extension_instance* p, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) : + m_held(p, a1, a2, a3, a4, a5, a6) {} + template + instance_value_holder(extension_instance* p, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) : + m_held(p, a1, a2, a3, a4, a5, a6, a7) {} + template + instance_value_holder(extension_instance* p, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) : + m_held(p, a1, a2, a3, a4, a5, a6, a7, a8) {} + template + instance_value_holder(extension_instance* p, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) : + m_held(p, a1, a2, a3, a4, a5, a6, a7, a8, a9) {} + template + instance_value_holder(extension_instance* p, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) : + m_held(p, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {} public: // implementation of instance_holder_base required interface bool held_by_value() { return true; } @@ -721,8 +814,6 @@ class extension_instance : public instance // Template function implementations // -tuple extension_class_coerce(ref l, ref r); - template extension_class::extension_class() : extension_class_base(typeid(T).name()) @@ -743,7 +834,7 @@ void extension_class::def_standard_coerce() ref coerce_fct = dict().get_item(string("__coerce__")); if(coerce_fct.get() == 0) // not yet defined - this->def(&extension_class_coerce, "__coerce__"); + this->def(&standard_coerce, "__coerce__"); } template @@ -831,4 +922,3 @@ std::vector class_registry::static_derived_class_info; }}} // namespace boost::python::detail #endif // EXTENSION_CLASS_DWA052000_H_ - diff --git a/include/boost/python/detail/init_function.hpp b/include/boost/python/detail/init_function.hpp index e53f8aa9..c0c50272 100644 --- a/include/boost/python/detail/init_function.hpp +++ b/include/boost/python/detail/init_function.hpp @@ -73,7 +73,8 @@ namespace detail { struct parameter_traits { private: - typedef const_ref_selector::value> selector; + enum { is_ref = boost::is_reference::value }; + typedef const_ref_selector selector; public: typedef typename selector::template const_ref::type const_reference; }; @@ -110,11 +111,11 @@ template struct init2; template struct init3; template struct init4; template struct init5; -template struct Init6; -template struct Init7; -template struct Init8; -template struct Init9; -template struct Init10; +template struct init6; +template struct init7; +template struct init8; +template struct init9; +template struct init10; template struct init_function @@ -165,7 +166,7 @@ struct init_function template static init* create(signature6) { - return new Init6::const_reference, detail::parameter_traits::const_reference, detail::parameter_traits::const_reference, @@ -176,7 +177,7 @@ struct init_function template static init* create(signature7) { - return new Init7::const_reference, detail::parameter_traits::const_reference, detail::parameter_traits::const_reference, @@ -188,7 +189,7 @@ struct init_function template static init* create(signature8) { - return new Init8::const_reference, detail::parameter_traits::const_reference, detail::parameter_traits::const_reference, @@ -201,7 +202,7 @@ struct init_function template static init* create(signature9) { - return new Init9::const_reference, detail::parameter_traits::const_reference, detail::parameter_traits::const_reference, @@ -215,7 +216,7 @@ struct init_function template static init* create(signature10) { - return new Init10::const_reference, detail::parameter_traits::const_reference, detail::parameter_traits::const_reference, @@ -353,7 +354,7 @@ struct init5 : init }; template -struct Init6 : init +struct init6 : init { virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const { @@ -379,7 +380,7 @@ struct Init6 : init }; template -struct Init7 : init +struct init7 : init { virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const { @@ -407,7 +408,7 @@ struct Init7 : init }; template -struct Init8 : init +struct init8 : init { virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const { @@ -437,7 +438,7 @@ struct Init8 : init }; template -struct Init9 : init +struct init9 : init { virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const { @@ -469,7 +470,7 @@ struct Init9 : init }; template -struct Init10 : init +struct init10 : init { virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const { diff --git a/include/boost/python/detail/types.hpp b/include/boost/python/detail/types.hpp index ee33ce04..5f0c8f97 100644 --- a/include/boost/python/detail/types.hpp +++ b/include/boost/python/detail/types.hpp @@ -375,8 +375,12 @@ PyObject* reprable::instance_repr(PyObject* obj) const // This macro gets the length of an array as a compile-time constant, and will // fail to compile if the parameter is a pointer. +#ifdef __BORLANDC__ // smart implementation doesn't work for borland; maybe someone knows a workaround? +# define PY_ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0])) +#else # define PY_ARRAY_LENGTH(a) \ (sizeof(::boost::python::detail::countof_validate(a, &(a))) ? sizeof(a) / sizeof((a)[0]) : 0) +#endif template inline void countof_validate(T* const, T* const*); diff --git a/include/boost/python/detail/wrap_python.hpp b/include/boost/python/detail/wrap_python.hpp index 7c6dd5be..b7a47513 100644 --- a/include/boost/python/detail/wrap_python.hpp +++ b/include/boost/python/detail/wrap_python.hpp @@ -1,5 +1,30 @@ +// (C) Copyright David Abrahams 2000. Permission to copy, use, modify, sell and +// distribute this software is granted provided this copyright notice appears +// in all copies. This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +// The author gratefully acknowleges the support of Dragon Systems, Inc., in +// producing this work. + +// This file serves as a wrapper around which allows it to be +// compiled with GCC 2.95.2 under Win32 and which disables the default MSVC +// behavior so that a program may be compiled in debug mode without requiring a +// special debugging build of the Python library. + + +// To use the Python debugging library, #define BOOST_DEBUG_PYTHON on the +// compiler command-line. + +// Revision History: +// 05 Mar 01 Suppress warnings under Cygwin with Python 2.0 (Dave Abrahams) +// 04 Mar 01 Rolled in some changes from the Dragon fork (Dave Abrahams) +// 01 Mar 01 define PyObject_INIT() for Python 1.x (Dave Abrahams) + + +#include + #ifdef _DEBUG -# ifndef DEBUG_PYTHON +# ifndef BOOST_DEBUG_PYTHON # undef _DEBUG // Don't let Python force the debug library just because we're debugging. # define DEBUG_UNDEFINED_FROM_WRAP_PYTHON_H # endif @@ -16,9 +41,11 @@ typedef int pid_t; # define WORD_BIT 32 # define hypot _hypot # include -# define HAVE_CLOCK -# define HAVE_STRFTIME -# define HAVE_STRERROR +# if !defined(PY_MAJOR_VERSION) || PY_MAJOR_VERSION < 2 +# define HAVE_CLOCK +# define HAVE_STRFTIME +# define HAVE_STRERROR +# endif # define NT_THREADS # define WITH_THREAD # ifndef NETSCAPE_PI @@ -44,6 +71,8 @@ typedef int pid_t; # define _MSC_VER 900 # endif +# elif defined(_MSC_VER) +# include // prevents Python.h from defining LONGLONG_MAX, LONGLONG_MIN, and ULONGLONG_MAX # endif #endif // _WIN32 @@ -59,3 +88,7 @@ typedef int pid_t; # define _DEBUG #endif +#if !defined(PY_MAJOR_VERSION) || PY_MAJOR_VERSION < 2 +# define PyObject_INIT(op, typeobj) \ + ( (op)->ob_type = (typeobj), _Py_NewReference((PyObject *)(op)), (op) ) +#endif diff --git a/include/boost/python/module_builder.hpp b/include/boost/python/module_builder.hpp index 57507e59..6a8a9b4b 100644 --- a/include/boost/python/module_builder.hpp +++ b/include/boost/python/module_builder.hpp @@ -18,11 +18,10 @@ namespace boost { namespace python { class module_builder { - typedef PyObject * (*raw_function_ptr)(boost::python::tuple const &, boost::python::dictionary const &); - public: // Create a module. REQUIRES: only one module_builder is created per module. module_builder(const char* name); + ~module_builder(); // Add elements to the module void add(detail::function* x, const char* name); @@ -41,13 +40,29 @@ class module_builder add(detail::new_wrapped_function(fn), name); } - static string name(); + // Return true iff a module is currently being built. + static bool initializing(); + // Return the name of the module currently being built. + // REQUIRES: initializing() == true + static string name(); + + // Return a pointer to the Python module object being built + PyObject* module() const; + private: PyObject* m_module; static PyMethodDef initial_methods[1]; }; +// +// inline implementations +// +inline PyObject* module_builder::module() const +{ + return m_module; +} + }} // namespace boost::python #endif diff --git a/include/boost/python/operators.hpp b/include/boost/python/operators.hpp index d0c06895..da88ec6c 100644 --- a/include/boost/python/operators.hpp +++ b/include/boost/python/operators.hpp @@ -1,15 +1,38 @@ +// (C) Copyright Ullrich Koethe and David Abrahams 2000-2001. Permission to +// copy, use, modify, sell and distribute this software is granted provided +// this copyright notice appears in all copies. This software is provided "as +// is" without express or implied warranty, and with no claim as to its +// suitability for any purpose. +// +// The authors gratefully acknowlege the support of Dragon Systems, Inc., in +// producing this work. +// +// Revision History: +// 23 Jan 2001 - Another stupid typo fix by Ralf W. Grosse-Kunstleve (David Abrahams) +// 20 Jan 2001 - Added a fix from Ralf W. Grosse-Kunstleve (David Abrahams) #ifndef OPERATORS_UK112000_H_ #define OPERATORS_UK112000_H_ -#include -#if !defined(__GNUC__) || defined(__SGI_STL_PORT) -# include -#else -# include -#endif +# include +# include + +// When STLport is used with native streams, _STL::ostringstream().str() is not +// _STL::string, but std::string. This confuses to_python(), so we'll use +// strstream instead. Also, GCC 2.95.2 doesn't have sstream. +# if defined(__SGI_STL_PORT) ? defined(__SGI_STL_OWN_IOSTREAMS) : (!defined(__GNUC__) || __GNUC__ > 2) +# define BOOST_PYTHON_USE_SSTREAM +# endif + +#if defined(BOOST_PYTHON_USE_SSTREAM) +# include +# else +# include +# endif namespace boost { namespace python { +tuple standard_coerce(ref l, ref r); + namespace detail { // helper class for automatic operand type detection @@ -461,6 +484,16 @@ namespace detail static const char * rname() { return "__rcmp__"; } }; +# ifndef BOOST_PYTHON_USE_SSTREAM + class unfreezer { + public: + unfreezer(std::ostrstream& s) : m_stream(s) {} + ~unfreezer() { m_stream.freeze(false); } + private: + std::ostrstream& m_stream; + }; +# endif + // str(): Manual specialization needed because the string conversion does not follow // the standard pattern relized by the macros. template <> @@ -473,19 +506,18 @@ namespace detail { tuple args(ref(arguments, ref::increment_count)); -#if !defined(__GNUC__) || defined(__SGI_STL_PORT) +// When STLport is used with native streams, _STL::ostringstream().str() is not +// _STL::string, but std::string. +# ifdef BOOST_PYTHON_USE_SSTREAM std::ostringstream s; s << BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()); -#else + return BOOST_PYTHON_CONVERSION::to_python(s.str()); +# else std::ostrstream s; s << BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) << char(); -#endif - -#if !defined(__GNUC__) || defined(__SGI_STL_PORT) - return BOOST_PYTHON_CONVERSION::to_python(s.str()); -#else + auto unfreezer unfreeze(s); return BOOST_PYTHON_CONVERSION::to_python(const_cast(s.str())); -#endif +# endif } const char* description() const @@ -501,4 +533,5 @@ namespace detail }} // namespace boost::python +# undef BOOST_PYTHON_USE_SSTREAM #endif /* OPERATORS_UK112000_H_ */ diff --git a/release_notes.txt b/release_notes.txt new file mode 100644 index 00000000..8932a0aa --- /dev/null +++ b/release_notes.txt @@ -0,0 +1,217 @@ +2000-11-22 10:00 + Ullrich fixed bug in operator_dispatcher. + +2000-11-21 10:00 + Changed all class and function names into lower_case. + + Ullrich updated documentation for operator wrapping. + +2000-11-20 10:00 + Ullrich renamed ExtensionClass:register_coerce() into + ExtensionClass:def_standard_coerce() and made it public + + Ullrich improved shared_pod_manager. + +2000-11-17 15:04 + Changed allocation strategy of shared_pod_manager to make it portable. + + Added pickling support + tests thanks to "Ralf W. Grosse-Kunstleve" + + + Added a specialization of Callback to prevent unsafe usage. + + Fixed Ullrich's operator_dispatcher refcount bug + + Removed const char* return values from virtual functions in tests; that + usage was unsafe. + + Ullrich changed Module::add() so that it steals a reference (fix of refcount bug) + + Ullrich added operator_dispatcher::create() optimization + + Ullrich changed design and implementation of TypeObjectBase::enable() (to eliminate low-level + code) and added shared_pod_manager optimization. + + +2000-11-15 12:01 + Fixed refcount bugs in operator calls. + + Added callback_adjust_refcount(PyObject*, Type) to account for different ownership + semantics of Callback's return types and Caller's arguments (which both use from_python()) + This bug caused refcount errors during operator calls. + + Moved operator_dispatcher into extclass.cpp + Gave it shared ownership of the objects it wraps + + Introduced sequence points in extension_class_coerce for exception-safety + + UPPER_CASE_MACRO_NAMES + + MixedCase template type argument names + + Changed internal error reporting to use Python exceptions so we don't force the + user to link in iostreams code + + Changed error return value of call_cmp to -1 + + Moved unwrap_* functions out of operator_dispatcher. This was transitional: when + I realized they didn't need to be declared in extclass.h I moved them out, but + now that operator_dispatcher itself is in extclass.cpp they could go back in. + + Numerous formatting tweaks + + Updated the BoundFunction::create() optimization and enabled it so it could actually be used! + +2000-11-15 00:26 + + Made Ullrich's operators support work with MSVC + + Cleaned up operators.h such that invalid define_operator<0> is no longer needed. + + Ullrich created operators.h to support wrapping of C++ operators (including the "__r*__" forms). + He added several auxiliary classes to extclass.h and extclass.cpp (most importantly, + py::detail::operator_dispatcher and py::operators) + +2000-11-13 22:29 + + removed obsolete ExtensionClassFromPython for good. + + removed unused class ExtensionType forward declaration + +2000-11-12 13:08 + + Added enum_as_int_converters for easier enum wrapping + + Introduced new conversion namespace macros: + PY_BEGIN_CONVERSION_NAMESPACE, + PY_END_CONVERSION_NAMESPACE, + PY_CONVERSION + + callback.h, gen_callback.py: + Added call() function so that a regular python function (as opposed to + method or other function-as-attribute) can be called. + + Added newlines for readability. + + class_wrapper.h: + Fixed a bug in add(), which allows non-method class attributes + + Ullrich has added def_raw for simple varargs and keyword support. + + Fixed version number check for __MWERKS__ + + Added tests for enums and non-method class attributes + + objects.h/objects.cpp: + Added py::String operator*= and operator* for repetition + + Change Dict::items(), keys(), and values() to return a List + + Added template versions of set_item, etc., methods so that users can optionally + use C++ types that have to_python() functions as parameters. + + Changed various Ptr by-value parameters to const Ptr& + + +======= Release ======= +2000-11-06 0:22 + Lots of documentation updates + + added 4-argument template constructor to py::Tuple + + added "add" member function to ClassWrapper<> to allow arbitrary Python + objects to be added to an extension class. + + gen_all.py now generates support for n argument member functions and n+1 + argument member functions at the suggestion of "Ralf W. Grosse-Kunstleve" + + + Added regression tests and re-ordered declare_base calls to verify that the + phantom base class issue is resolved. + +2000-11-04 17:35 + + Integrated Ullrich Koethe's brilliant from_python_experiment for better + error-reporting in many cases. + + extclass.h, gen_extclass.py: + removed special-case MSVC code + added much commentary + removed unused py_copy_to_new_value_holder + + init_function.h, gen_init_function.py: + added missing 'template' keyword on type-dependent template member usage + removed special-case MSVC code + added much commentary + +2000-11-04 0:36 + + Removed the need for the phantom base class that screwed up inheritance + hierarchies, introduced error-prone ordering dependencies, and complexified + logic in many places! + + extclass.h: Added some explanatory comments, removed wasteful m_self member + of HeldInstance + + extclass_demo.cpp: Added #pragmas which allow compilation in ansi strict + mode under Metrowerks + + functions.h: Added virtual_function as part of phantom base class removal; + expanded commentary + + pyptr.h: Added some missing 'typename's and a GCC workaround fix + + subclass.cpp: Added missing string literal const_cast<>s. + +2000-11-03 10:58 + + Fix friend function instantiation bug caught by Metrowerks (thanks + Metrowerks!) + + Add proof-of-concept for one technique of wrapping function that return a + pointer + + Worked around MSVC optimizer bug by writing to_python(double) and + to_python(float) out-of-line + +2000-11-02 23:25 + + Add /Zm200 option to vc6_prj to deal with MSVC resource limitations + + Remove conflicting /Ot option from vc6_prj release build + +======= Release ======= +2000-11-02 17:42 + + Added a fix for interactions between default virtual function + implementations and declare_base(). You still need to write your + declare_base() /after/ all member functions have been def()d for the two + classes concerned. Many, many thanks to Ullrich Koethe + for all his work on this. + + Added missing conversions: + to_python(float) + from_python(const char* const&) + from_python(const double&) + from_python(const float&) + + Added a Regression test for a reference-counting bug thanks to Mark Evans + () + + const-ify ClassBase::getattr() + + Add repr() function to Class + + Add to_python/from_python conversions for PyPtr + + Standardize set_item/get_item interfaces (instead of proxies) for Dict and List + + Add Reprable<> template to newtypes.h + + Fix a bug wherein the __module__ attribute would be lost for classes that have a + default virtual function implementation. + + Remove extra ';' in module.cpp thanks to "Ralf W. Grosse-Kunstleve" + + + Fix a bug in the code of example1.html diff --git a/src/classes.cpp b/src/classes.cpp index 34b42c22..8755bb34 100644 --- a/src/classes.cpp +++ b/src/classes.cpp @@ -5,6 +5,11 @@ // // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. +// +// Revision History: +// 04 Mar 01 Rolled in const_cast from Dragon fork (Dave Abrahams) +// 03 Mar 01 added: pickle safety measures (Ralf W. Grosse-Kunstleve) +// 03 Mar 01 bug fix: use bound_function::create() (instead of new bound_function) #include #include @@ -67,8 +72,7 @@ namespace { ref global_class_reduce() { - static ref result(detail::new_wrapped_function(class_reduce)); - return result; + return ref(detail::new_wrapped_function(class_reduce)); } @@ -93,17 +97,41 @@ namespace { ref getstate(PyObject_GetAttrString(obj, const_cast("__getstate__")), ref::null_ok); PyErr_Clear(); + + ref dict(PyObject_GetAttrString(obj, const_cast("__dict__")), ref::null_ok); + PyErr_Clear(); + if (getstate.get() != 0) { + if (dict.get() != 0 && dictionary(dict).size() > 0) + { + ref getstate_manages_dict(PyObject_GetAttrString(instance_class.get(), const_cast("__getstate_manages_dict__")), ref::null_ok); + PyErr_Clear(); + if (getstate_manages_dict.get() == 0) + { + PyErr_SetString(PyExc_RuntimeError, "Incomplete pickle support (__getstate_manages_dict__ not set)"); + throw error_already_set(); + } + } + ref state = ref(PyEval_CallObject(getstate.get(), NULL)); return tuple(instance_class, initargs, state); } - ref state(PyObject_GetAttrString(obj, const_cast("__dict__")), ref::null_ok); - PyErr_Clear(); - if (state.get() != 0 && dictionary(state).size() > 0) + if (getinitargs.get() == 0) { - return tuple(instance_class, initargs, state); + ref dict_defines_state(PyObject_GetAttrString(instance_class.get(), const_cast("__dict_defines_state__")), ref::null_ok); + PyErr_Clear(); + if (dict_defines_state.get() == 0) + { + PyErr_SetString(PyExc_RuntimeError, "Incomplete pickle support (__dict_defines_state__ not set)"); + throw error_already_set(); + } + } + + if (dict.get() != 0 && dictionary(dict).size() > 0) + { + return tuple(instance_class, initargs, dict); } return tuple(instance_class, initargs); @@ -111,8 +139,7 @@ namespace { ref global_instance_reduce() { - static ref result(detail::new_wrapped_function(instance_reduce)); - return result; + return ref(detail::new_wrapped_function(instance_reduce)); } } @@ -176,8 +203,9 @@ namespace detail { } if (!BOOST_CSTD_::strcmp(name, "__reduce__")) { - ref target(as_object(this), ref::increment_count); - return new bound_function(target, global_class_reduce()); + PyObject* self = as_object(this); + ref target(self, ref::increment_count); + return bound_function::create(target, global_class_reduce()); } ref local_attribute = m_name_space.get_item(string(name).reference()); @@ -348,7 +376,7 @@ PyObject* instance::getattr(const char* name, bool use_special_function) if (!BOOST_CSTD_::strcmp(name, "__reduce__")) { - return new detail::bound_function(ref(this, ref::increment_count), global_instance_reduce()); + return detail::bound_function::create(ref(this, ref::increment_count), global_instance_reduce()); } ref local_attribute = m_name_space.get_item(string(name).reference()); @@ -781,7 +809,7 @@ namespace detail { // Enable the special handler for methods of the given name, if any. void enable_named_method(boost::python::detail::class_base* type_obj, const char* name) { - const std::size_t num_enablers = sizeof(enablers) / sizeof(enablers[0]); + const std::size_t num_enablers = PY_ARRAY_LENGTH(enablers); // Make sure this ends with "__" since we'll only compare the head of the // string. This is done to make the __getattr____/__setattr____ @@ -840,7 +868,27 @@ namespace { void add_current_module_name(dictionary& name_space) { static string module_key("__module__", string::interned); - name_space.set_item(module_key, module_builder::name()); + + // If the user didn't specify a __module__ attribute already + if (name_space.get_item(module_key).get() == 0) + { + if (module_builder::initializing()) + { + // The global __name__ is not properly set in this case + name_space.set_item(module_key, module_builder::name()); + } + else + { + // Get the module name from the global __name__ + PyObject *globals = PyEval_GetGlobals(); + if (globals != NULL) + { + PyObject *module_name = PyDict_GetItemString(globals, const_cast("__name__")); + if (module_name != NULL) + name_space.set_item(module_key, module_name); + } + } + } } } diff --git a/src/conversions.cpp b/src/conversions.cpp index ded01e0e..4bfe2011 100644 --- a/src/conversions.cpp +++ b/src/conversions.cpp @@ -5,6 +5,13 @@ // // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. +// +// Revision History: +// 05 Apr 01 added: from_python std::string type checking (rwgk) +// 12 Mar 01 Python 1.5.2 fixes (Ralf W. Grosse-Kunstleve) +// 11 Mar 01 std::string *MAY* include nulls (Alex Martelli) +// 04 Mar 01 std::complex<> fixes for MSVC (Dave Abrahams) +// 03 Mar 01 added: converters for [plain] char (Ralf W. Grosse-Kunstleve) #include #include @@ -44,6 +51,19 @@ void handle_exception() } } +namespace detail { + + void expect_complex(PyObject* p) + { + if (!PyComplex_Check(p)) + { + PyErr_SetString(PyExc_TypeError, "expected a complex number"); + throw boost::python::argument_error(); + } + } + +} // namespace boost::python::detail + }} // namespace boost::python BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE @@ -132,7 +152,7 @@ int from_python(PyObject* p, boost::python::type type) PyObject* to_python(unsigned int i) { - return integer_to_python(i); + return integer_to_python(i); } unsigned int from_python(PyObject* p, boost::python::type type) @@ -152,7 +172,7 @@ float from_python(PyObject* p, boost::python::type) PyObject* to_python(unsigned short i) { - return integer_to_python(i); + return integer_to_python(i); } unsigned short from_python(PyObject* p, boost::python::type type) @@ -160,9 +180,27 @@ unsigned short from_python(PyObject* p, boost::python::type type return integer_from_python(p, type); } +PyObject* to_python(char c) +{ + if (c == '\0') return PyString_FromString(""); + return PyString_FromStringAndSize(&c, 1); +} + +char from_python(PyObject* p, boost::python::type) +{ + int l = -1; + if (PyString_Check(p)) l = PyString_Size(p); + if (l < 0 || l > 1) { + PyErr_SetString(PyExc_TypeError, "expected string of length 0 or 1"); + throw boost::python::argument_error(); + } + if (l == 0) return '\0'; + return PyString_AsString(p)[0]; +} + PyObject* to_python(unsigned char i) { - return integer_to_python(i); + return integer_to_python(i); } unsigned char from_python(PyObject* p, boost::python::type type) @@ -172,7 +210,7 @@ unsigned char from_python(PyObject* p, boost::python::type type) PyObject* to_python(signed char i) { - return integer_to_python(i); + return integer_to_python(i); } signed char from_python(PyObject* p, boost::python::type type) @@ -208,12 +246,16 @@ const char* from_python(PyObject* p, boost::python::type) PyObject* to_python(const std::string& s) { - return PyString_FromString(s.c_str()); + return PyString_FromStringAndSize(s.data(), s.size()); } std::string from_python(PyObject* p, boost::python::type) { - return std::string(from_python(p, boost::python::type())); + if (! PyString_Check(p)) { + PyErr_SetString(PyExc_TypeError, "expected a string"); + throw boost::python::argument_error(); + } + return std::string(PyString_AsString(p), PyString_Size(p)); } bool from_python(PyObject* p, boost::python::type) diff --git a/src/cross_module.cpp b/src/cross_module.cpp new file mode 100644 index 00000000..ea5a08de --- /dev/null +++ b/src/cross_module.cpp @@ -0,0 +1,87 @@ +/* (C) Copyright Ralf W. Grosse-Kunstleve 2001. Permission to copy, use, + modify, sell and distribute this software is granted provided this + copyright notice appears in all copies. This software is provided + "as is" without express or implied warranty, and with no claim as to + its suitability for any purpose. + + Revision History: + 17 Apr 01 merged into boost CVS trunk (Ralf W. Grosse-Kunstleve) +*/ + +# include +namespace python = boost::python; +# include // MSVC6.0SP4 does not know std::fprintf +# include // MSVC6.0SP4 does not know std::strcmp + +namespace { + + PyObject* get_module_dict(const char* module_name) + { + python::ref module_obj(PyImport_ImportModule((char*) module_name)); + PyObject* module_dict = PyModule_GetDict(module_obj.get()); + if (module_dict == 0) throw python::import_error(); + return module_dict; + } +} + +namespace boost { namespace python { namespace detail { + +const char* converters_attribute_name = "__converters__"; + +void* import_converter_object(const std::string& module_name, + const std::string& py_class_name, + const std::string& attribute_name) +{ + static std::string err; + PyObject* module_dict = get_module_dict(const_cast(module_name.c_str())); + PyObject* py_class = PyDict_GetItemString(module_dict, const_cast(py_class_name.c_str())); + if (py_class == 0) { + err = std::string("module ") + module_name + " has no attribute " + py_class_name; + PyErr_SetString(PyExc_RuntimeError, const_cast(err.c_str())); + throw python::import_error(); + } + python::ref c_obj(PyObject_GetAttrString(py_class, const_cast(attribute_name.c_str())), ref::null_ok); + if (c_obj.get() == 0) { + err = std::string("object ") + module_name + "." + py_class_name + + " has no attribute " + attribute_name; + PyErr_SetString(PyExc_RuntimeError, const_cast(err.c_str())); + throw python::import_error(); + } + if (! PyCObject_Check(c_obj.get())) { + err = std::string("object ") + module_name + "." + py_class_name + "." + + attribute_name + " is not a PyCObject"; + PyErr_SetString(PyExc_RuntimeError, const_cast(err.c_str())); + throw python::import_error(); + } + return PyCObject_AsVoidPtr(c_obj.get()); +} + +void check_export_converters_api(const int importing_major, + const int importing_minor, + const int imported_major, + const int imported_minor) +{ + if (importing_major != imported_major) { + // Python uses fprintf(stderr, ...) for API warnings. + fprintf(stderr, + "Fatal: export_converters_api mismatch:" + " Importing module = %d.%d" + " Imported module = %d.%d\n", + importing_major, importing_minor, + imported_major, imported_minor); + PyErr_SetString(PyExc_RuntimeError, + "Fatal: export_converters_api mismatch"); + throw import_error(); + } + if (importing_minor != imported_minor) { + // Python uses fprintf(stderr, ...) for API warnings. + fprintf(stderr, + "Warning: export_converters_api mismatch:" + " Importing module = %d.%d" + " Imported module = %d.%d\n", + importing_major, importing_minor, + imported_major, imported_minor); + } +} + +}}} // namespace boost::python::detail diff --git a/src/extension_class.cpp b/src/extension_class.cpp index cde3d836..f71976b9 100644 --- a/src/extension_class.cpp +++ b/src/extension_class.cpp @@ -5,6 +5,9 @@ // // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. +// +// Revision History: +// 04 Mar 01 Use PyObject_INIT() instead of trying to hand-initialize (David Abrahams) #include #include @@ -46,24 +49,19 @@ BOOST_PYTHON_END_CONVERSION_NAMESPACE namespace boost { namespace python { -namespace detail { +tuple standard_coerce(ref l, ref r) +{ + // Introduced sequence points for exception-safety. + ref first(detail::operator_dispatcher::create(l, l)); + + ref second(r->ob_type == &detail::operator_dispatcher::type_obj + ? r + : ref(detail::operator_dispatcher::create(r, ref()))); - tuple extension_class_coerce(ref l, ref r) - { - // Introduced sequence points for exception-safety. - ref first(operator_dispatcher::create(l, l)); - ref second; - - if(r->ob_type == &operator_dispatcher::type_obj) - { - second = r; - } - else - { - second = ref(operator_dispatcher::create(r, ref())); - } - return boost::python::tuple(first, second); - } + return tuple(first, second); +} + +namespace detail { enum { unwrap_exception_code = -1000 }; @@ -451,8 +449,8 @@ operator_dispatcher::operator_dispatcher(const ref& o, const ref& s) : m_object(o), m_self(s), m_free_list_link(0) { - ob_refcnt = 1; - ob_type = &type_obj; + PyObject* self = this; + PyObject_INIT(self, &type_obj); } operator_dispatcher* @@ -465,7 +463,9 @@ operator_dispatcher::create(const ref& object, const ref& self) free_list = result->m_free_list_link; result->m_object = object; result->m_self = self; - Py_INCREF(result); + + PyObject* result_as_pyobject = result; + PyObject_INIT(result_as_pyobject, &type_obj); return result; } diff --git a/src/functions.cpp b/src/functions.cpp index e166c91c..71b59136 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -5,6 +5,9 @@ // // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. +// +// Revision History: +// Mar 01 01 Use PyObject_INIT() instead of trying to hand-initialize (David Abrahams) #include #include @@ -97,19 +100,6 @@ PyObject* function::call(PyObject* args, PyObject* keywords) const return 0; } -bound_function* bound_function::create(const ref& target, const ref& fn) -{ - bound_function* const result = free_list; - if (result == 0) - return new bound_function(target, fn); - - free_list = result->m_free_list_link; - result->m_target = target; - result->m_unbound_function = fn; - Py_INCREF(result); - return result; -} - // The instance class whose obj represents the type of bound_function // objects in Python. bound_functions must be GetAttrable so the __doc__ // attribute of built-in Python functions can be accessed when bound. @@ -123,6 +113,21 @@ private: // type_object hook override void dealloc(bound_function*) const; }; +bound_function* bound_function::create(const ref& target, const ref& fn) +{ + bound_function* const result = free_list; + if (result == 0) + return new bound_function(target, fn); + + free_list = result->m_free_list_link; + result->m_target = target; + result->m_unbound_function = fn; + + PyObject* self = result; + PyObject_INIT(self, type_object::instance()); + return result; +} + bound_function::bound_function(const ref& target, const ref& fn) : python_object(type_object::instance()), m_target(target), diff --git a/src/gen_all.py b/src/gen_all.py index fd8d78cc..3877d181 100644 --- a/src/gen_all.py +++ b/src/gen_all.py @@ -6,12 +6,12 @@ from gen_singleton import * from gen_extclass import * def gen_all(args): - open('callback.h', 'w').write(gen_callback(args)) - open('caller.h', 'w').write(gen_caller(args)) - open('init_function.h', 'w').write(gen_init_function(args)) - open('signatures.h', 'w').write(gen_signatures(args)) - open('instance.h', 'w').write(gen_singleton(args)) - open('extclass.h', 'w').write(gen_extclass(args)) + open('callback.hpp', 'w').write(gen_callback(args)) + open('caller.hpp', 'w').write(gen_caller(args)) + open('init_function.hpp', 'w').write(gen_init_function(args)) + open('signatures.hpp', 'w').write(gen_signatures(args)) + open('singleton.hpp', 'w').write(gen_singleton(args)) + open('extension_class.hpp', 'w').write(gen_extclass(args)) if __name__ == '__main__': import sys diff --git a/src/gen_extclass.py b/src/gen_extclass.py index 5180a3de..8de8a1ae 100644 --- a/src/gen_extclass.py +++ b/src/gen_extclass.py @@ -14,6 +14,10 @@ def gen_extclass(args): // This file automatically generated for %d-argument constructors by // gen_extclass.python +// Revision History: +// 05 Mar 01 Fixed a bug which prevented auto_ptr values from being converted +// to_python (Dave Abrahams) + #ifndef EXTENSION_CLASS_DWA052000_H_ # define EXTENSION_CLASS_DWA052000_H_ @@ -27,6 +31,7 @@ def gen_extclass(args): # include # include # include +# include namespace boost { namespace python { @@ -66,7 +71,7 @@ T* check_non_null(T* p) return p; } -template class held_instance; +template class held_instance; typedef void* (*conversion_function_ptr)(void*); @@ -138,6 +143,26 @@ class class_registry static std::vector static_derived_class_info; }; +template +struct is_null_helper +{ + template + static bool test(Ptr x) { return x == 0; } +}; + +template <> +struct is_null_helper +{ + template + static bool test(const Ptr& x) { return x.get() == 0; } +}; + +template +bool is_null(const Ptr& x) +{ + return is_null_helper<(is_pointer::value)>::test(x); +} + }}} // namespace boost::python::detail BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE @@ -183,9 +208,9 @@ class python_extension_class_converters new boost::python::detail::instance_value_holder(result.get(), x))); return result.release(); } - - // Convert to T* - friend T* from_python(PyObject* obj, boost::python::type) + + friend + T* non_null_from_python(PyObject* obj, boost::python::type) { // downcast to an extension_instance, then find the actual T boost::python::detail::extension_instance* self = boost::python::detail::get_extension_instance(obj); @@ -206,9 +231,18 @@ class python_extension_class_converters throw boost::python::argument_error(); } - // Convert to PtrType, where PtrType can be dereferenced to obtain a T. + // Convert to T* + friend T* from_python(PyObject* obj, boost::python::type) + { + if (obj == Py_None) + return 0; + else + return non_null_from_python(obj, boost::python::type()); + } + + // Extract from obj a mutable reference to the PtrType object which is holding a T. template - static PtrType& ptr_from_python(PyObject* obj, boost::python::type) + static PtrType& smart_ptr_reference(PyObject* obj, boost::python::type) { // downcast to an extension_instance, then find the actual T boost::python::detail::extension_instance* self = boost::python::detail::get_extension_instance(obj); @@ -225,9 +259,29 @@ class python_extension_class_converters throw boost::python::argument_error(); } + // Extract from obj a reference to the PtrType object which is holding a + // T. If it weren't for auto_ptr, it would be a constant reference. Do not + // modify the referent except by copying an auto_ptr! If obj is None, the + // reference denotes a default-constructed PtrType template - static PyObject* ptr_to_python(PtrType x) + static PtrType& smart_ptr_value(PyObject* obj, boost::python::type) { + if (obj == Py_None) + { + static PtrType null_ptr; + return null_ptr; + } + return smart_ptr_reference(obj, boost::python::type()); + } + + template + static PyObject* smart_ptr_to_python(PtrType x) + { + if (boost::python::detail::is_null(x)) + { + return boost::python::detail::none(); + } + boost::python::reference result(create_instance()); result->add_implementation( std::auto_ptr( @@ -259,7 +313,7 @@ class python_extension_class_converters // Convert to T& friend T& from_python(PyObject* p, boost::python::type) - { return *boost::python::detail::check_non_null(from_python(p, boost::python::type())); } + { return *boost::python::detail::check_non_null(non_null_from_python(p, boost::python::type())); } // Convert to const T& friend const T& from_python(PyObject* p, boost::python::type) @@ -270,28 +324,28 @@ class python_extension_class_converters { return from_python(p, boost::python::type()); } friend std::auto_ptr& from_python(PyObject* p, boost::python::type&>) - { return ptr_from_python(p, boost::python::type >()); } + { return smart_ptr_reference(p, boost::python::type >()); } - friend std::auto_ptr& from_python(PyObject* p, boost::python::type >) - { return ptr_from_python(p, boost::python::type >()); } + friend std::auto_ptr from_python(PyObject* p, boost::python::type >) + { return smart_ptr_value(p, boost::python::type >()); } friend const std::auto_ptr& from_python(PyObject* p, boost::python::type&>) - { return ptr_from_python(p, boost::python::type >()); } + { return smart_ptr_value(p, boost::python::type >()); } friend PyObject* to_python(std::auto_ptr x) - { return ptr_to_python(x); } + { return smart_ptr_to_python(x); } friend boost::shared_ptr& from_python(PyObject* p, boost::python::type&>) - { return ptr_from_python(p, boost::python::type >()); } + { return smart_ptr_reference(p, boost::python::type >()); } - friend boost::shared_ptr& from_python(PyObject* p, boost::python::type >) - { return ptr_from_python(p, boost::python::type >()); } + friend const boost::shared_ptr& from_python(PyObject* p, boost::python::type >) + { return smart_ptr_value(p, boost::python::type >()); } friend const boost::shared_ptr& from_python(PyObject* p, boost::python::type&>) - { return ptr_from_python(p, boost::python::type >()); } + { return smart_ptr_value(p, boost::python::type >()); } friend PyObject* to_python(boost::shared_ptr x) - { return ptr_to_python(x); } + { return smart_ptr_to_python(x); } }; // Convert T to_python, instantiated on demand and only if there isn't a @@ -613,15 +667,15 @@ class extension_class // A simple wrapper over a T which allows us to use extension_class with a // single template parameter only. See extension_class, above. -template -class held_instance : public T +template +class held_instance : public Held { // There are no member functions: we want to avoid inadvertently overriding - // any virtual functions in T. + // any virtual functions in Held. public:""" + gen_functions("""%{ template <%(class A%n%:, %)>%} - held_instance(PyObject*%(, A%n% a%n%)) : T(%(a%n%:, %)) {}""", args) + held_instance(PyObject*%(, A%n% a%n%)) : Held(%(a%n%:, %)) {}""", args) + """ }; @@ -707,8 +761,6 @@ class extension_instance : public instance // Template function implementations // -tuple extension_class_coerce(ref l, ref r); - template extension_class::extension_class() : extension_class_base(typeid(T).name()) @@ -729,7 +781,7 @@ void extension_class::def_standard_coerce() ref coerce_fct = dict().get_item(string("__coerce__")); if(coerce_fct.get() == 0) // not yet defined - this->def(&extension_class_coerce, "__coerce__"); + this->def(&standard_coerce, "__coerce__"); } template diff --git a/src/module_builder.cpp b/src/module_builder.cpp index ea00b71c..57eec75f 100644 --- a/src/module_builder.cpp +++ b/src/module_builder.cpp @@ -14,10 +14,15 @@ namespace { ref name_holder; } +bool module_builder::initializing() +{ + return name_holder.get() != 0; +} + string module_builder::name() { // If this fails, you haven't created a module_builder object - assert(name_holder.get() != 0); + assert(initializing()); return string(name_holder); } @@ -29,6 +34,11 @@ module_builder::module_builder(const char* name) name_holder = ref(PyObject_GetAttrString(m_module, const_cast("__name__"))); } +module_builder::~module_builder() +{ + name_holder.reset(); +} + void module_builder::add(detail::function* x, const char* name) { diff --git a/test/comprehensive.cpp b/test/comprehensive.cpp index cae924e6..427ed2d8 100644 --- a/test/comprehensive.cpp +++ b/test/comprehensive.cpp @@ -5,13 +5,23 @@ // // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. + +// Revision History: +// 04 Mar 01 Changed name of extension module so it would work with DebugPython, +// eliminated useless test that aggravated MSVC (David Abrahams) #include "comprehensive.hpp" #include #include // used for portability on broken compilers #include // for pow() #include -namespace extclass_demo { +#if defined(__sgi) \ + && ( (defined(_COMPILER_VERSION) && _COMPILER_VERSION <= 730) \ + && !defined(__GNUC__)) +inline double pow(int x, int y) { return pow(static_cast(x), y); } +#endif + +namespace bpl_test { FooCallback::FooCallback(PyObject* self, int x) : Foo(x), m_self(self) @@ -238,6 +248,23 @@ boost::shared_ptr Baz::create_foo() return boost::shared_ptr(new DerivedFromFoo(0)); } +// Used to check conversion to None +boost::shared_ptr foo_factory(bool create) +{ + return boost::shared_ptr(create ? new DerivedFromFoo(0) : 0); +} + +// Used to check conversion from None +bool foo_ptr_is_null(Foo* p) +{ + return p == 0; +} + +bool foo_shared_ptr_is_null(boost::shared_ptr p) +{ + return p.get() == 0; +} + // We can accept smart pointer parameters int Baz::get_foo_value(boost::shared_ptr foo) { @@ -404,7 +431,7 @@ static int testUpcast(Base* b) static std::auto_ptr derived1Factory(int i) { - return std::auto_ptr(new Derived1(i)); + return std::auto_ptr(i < 0 ? 0 : new Derived1(i)); } static std::auto_ptr derived2Factory(int i) @@ -714,12 +741,14 @@ const Record* get_record() return &v; } -template class boost::python::class_builder; // explicitly instantiate +} // namespace bpl_test -} // namespace extclass_demo +namespace boost { namespace python { + template class class_builder; // explicitly instantiate +}} // namespace boost::python BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE -inline PyObject* to_python(const extclass_demo::Record* p) +inline PyObject* to_python(const bpl_test::Record* p) { return to_python(*p); } @@ -731,7 +760,7 @@ BOOST_PYTHON_END_CONVERSION_NAMESPACE /* */ /************************************************************/ -namespace extclass_demo { +namespace bpl_test { struct EnumOwner { @@ -753,8 +782,8 @@ struct EnumOwner } namespace boost { namespace python { - template class enum_as_int_converters; - using extclass_demo::pow; + template class enum_as_int_converters; + using bpl_test::pow; }} // namespace boost::python // This is just a way of getting the converters instantiated @@ -763,7 +792,7 @@ namespace boost { namespace python { //{ //}; -namespace extclass_demo { +namespace bpl_test { /************************************************************/ /* */ @@ -813,6 +842,32 @@ namespace extclass_demo { w.set_secret_number(number); } + // Test plain char converters. + char get_plain_char() { return 'x'; } + std::string use_plain_char(char c) { return std::string(3, c); } + + // This doesn't test anything but the compiler, since it has the same signature as the above. + // Since MSVC is broken and gets the signature wrong, we'll skip it. + std::string use_const_plain_char( +#ifndef BOOST_MSVC6_OR_EARLIER + const +#endif + char c) { return std::string(5, c); } + + // Test std::complex converters. + std::complex dpolar(double rho, double theta) { + return std::polar(rho, theta); + } + double dreal(const std::complex& c) { return c.real(); } + double dimag(std::complex c) { return c.imag(); } + + // Test std::complex converters. + std::complex fpolar(float rho, float theta) { + return std::polar(rho, theta); + } + double freal(const std::complex& c) { return c.real(); } + double fimag(std::complex c) { return c.imag(); } + /************************************************************/ /* */ /* init the module */ @@ -1034,9 +1089,29 @@ void init_module(boost::python::module_builder& m) world_class.def(world_getinitargs, "__getinitargs__"); world_class.def(world_getstate, "__getstate__"); world_class.def(world_setstate, "__setstate__"); + + // Test plain char converters. + m.def(get_plain_char, "get_plain_char"); + m.def(use_plain_char, "use_plain_char"); + m.def(use_const_plain_char, "use_const_plain_char"); + + // Test std::complex converters. + m.def(dpolar, "dpolar"); + m.def(dreal, "dreal"); + m.def(dimag, "dimag"); + + // Test std::complex converters. + m.def(fpolar, "fpolar"); + m.def(freal, "freal"); + m.def(fimag, "fimag"); + + // Test new null-pointer<->None conversions + m.def(foo_factory, "foo_factory"); + m.def(foo_ptr_is_null, "foo_ptr_is_null"); + m.def(foo_shared_ptr_is_null, "foo_shared_ptr_is_null"); } -PyObject* raw(boost::python::tuple const& args, boost::python::dictionary const& keywords) +PyObject* raw(const boost::python::tuple& args, const boost::python::dictionary& keywords) { if(args.size() != 2 || keywords.size() != 2) { @@ -1055,21 +1130,17 @@ PyObject* raw(boost::python::tuple const& args, boost::python::dictionary const& void init_module() { - boost::python::module_builder demo("demo"); - init_module(demo); + boost::python::module_builder boost_python_test("boost_python_test"); + init_module(boost_python_test); // Just for giggles, add a raw metaclass. - demo.add(new boost::python::meta_class); + boost_python_test.add(new boost::python::meta_class); } -extern "C" -#ifdef _WIN32 -__declspec(dllexport) -#endif -void initdemo() +BOOST_PYTHON_MODULE_INIT(boost_python_test) { try { - extclass_demo::init_module(); + bpl_test::init_module(); } catch(...) { boost::python::handle_exception(); @@ -1083,7 +1154,7 @@ CompareIntPairPythonClass::CompareIntPairPythonClass(boost::python::module_build def(&CompareIntPair::operator(), "__call__"); } -} // namespace extclass_demo +} // namespace bpl_test #if defined(_WIN32) @@ -1124,7 +1195,7 @@ BOOL WINAPI DllMain( switch(fdwReason) { case DLL_PROCESS_DETACH: - assert(extclass_demo::total_Ints == 0); + assert(bpl_test::total_Ints == 0); } #endif diff --git a/test/comprehensive.hpp b/test/comprehensive.hpp index d8a8e8ea..ed90f061 100644 --- a/test/comprehensive.hpp +++ b/test/comprehensive.hpp @@ -6,8 +6,8 @@ // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. -#ifndef EXTCLASS_DEMO_DWA052200_H_ -# define EXTCLASS_DEMO_DWA052200_H_ +#ifndef BPL_TEST_DWA052200_H_ +# define BPL_TEST_DWA052200_H_ // // Example code demonstrating extension class usage // @@ -21,7 +21,7 @@ # include # include -namespace extclass_demo { +namespace bpl_test { // // example: Foo, Bar, and Baz are C++ classes we want to wrap. @@ -226,6 +226,6 @@ struct CompareIntPairPythonClass CompareIntPairPythonClass(boost::python::module_builder&); }; -} // namespace extclass_demo +} // namespace bpl_test -#endif // EXTCLASS_DEMO_DWA052200_H_ +#endif // BPL_TEST_DWA052200_H_ diff --git a/test/comprehensive.py b/test/comprehensive.py index 578197f1..013fae96 100644 --- a/test/comprehensive.py +++ b/test/comprehensive.py @@ -7,19 +7,31 @@ r''' // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. +// Revision History: +// 04 Mar 01 Changed name of extension module so it would work with DebugPython, +// fixed exception message checking to work with Python 2.0 +// (Dave Abrahams) + +Load up the extension module + + >>> from boost_python_test import * + Automatic checking of the number and type of arguments. Foo's constructor takes a single long parameter. - >>> ext = Foo() - Traceback (innermost last): - File "", line 1, in ? - TypeError: function requires exactly 1 argument; 0 given + >>> try: + ... ext = Foo() + ... except TypeError, err: + ... assert re.match(r'function .* exactly 1 argument;? \(?0 given\)?', + ... str(err)) + ... else: + ... print 'no exception' >>> try: ext = Foo('foo') ... except TypeError, err: - ... assert re.match( - ... '(illegal argument type for built-in operation)|(an integer is required)', str(err)) - ... else: print 'no exception' + ... assert_integer_expected(err) + ... else: + ... print 'no exception' >>> ext = Foo(1) @@ -64,6 +76,21 @@ We can subclass Foo. >>> b.call_pure() 'not pure anymore!' +None corresponds to a NULL pointer or smart pointer + >>> f = foo_factory(1) + >>> f.add_len('xxx') + 1000 + >>> foo_factory(0) is None + 1 + >>> foo_ptr_is_null(None) + 1 + >>> foo_ptr_is_null(f) + 0 + >>> foo_shared_ptr_is_null(None) + 1 + >>> foo_shared_ptr_is_null(f) + 0 + If no __init__ function is defined, the one from the base class takes effect, just like in a Python class. @@ -209,7 +236,7 @@ Polymorphism also works: Pickling tests: >>> world.__module__ - 'demo' + 'boost_python_test' >>> world.__safe_for_unpickling__ 1 >>> world.__reduce__() @@ -239,6 +266,47 @@ Pickling tests: Hello from California! 42 Hello from California! 0 +Pickle safety measures: + >>> r=Rational(3, 4) + >>> r + Rational(3, 4) + >>> try: s=pickle.dumps(r) + ... except RuntimeError, err: print err[0] + ... + Incomplete pickle support (__dict_defines_state__ not set) + >>> class myrational(Rational): + ... __dict_defines_state__ = 1 # this is a lie but good enough for testing. + ... + >>> r=myrational(3, 4) + >>> r + Rational(3, 4) + >>> s=pickle.dumps(r) + + >>> class myworld(world): + ... def __init__(self): + ... world.__init__(self, 'anywhere') + ... self.x = 1 + ... + >>> w = myworld() + >>> w.greet() + 'Hello from anywhere!' + >>> w.__dict__ + {'x': 1} + >>> try: s=pickle.dumps(w) + ... except RuntimeError, err: print err[0] + ... + Incomplete pickle support (__getstate_manages_dict__ not set) + + >>> class myunsafeworld(myworld): + ... __getstate_manages_dict__ = 1 # this is a lie but good enough for testing. + ... + >>> w = myunsafeworld() + >>> w.greet() + 'Hello from anywhere!' + >>> w.__dict__ + {'x': 1} + >>> s=pickle.dumps(w) + Special member attributes. Tests courtesy of Barry Scott >>> class DerivedFromFoo(Foo): @@ -656,10 +724,11 @@ Testing interaction between callbacks, base declarations, and overloading >>> c = CallbackTest() >>> c.testCallback(1) 2 - >>> c.testCallback('foo') - Traceback (innermost last): - File "", line 1, in ? - TypeError: illegal argument type for built-in operation + + >>> try: c.testCallback('foo') + ... except TypeError, err: assert_integer_expected(err) + ... else: print 'no exception' + >>> c.callback(1) 2 >>> c.callback('foo') @@ -678,10 +747,11 @@ Testing interaction between callbacks, base declarations, and overloading -1 >>> r.callback('foo') 'foo 1' - >>> r.testCallback('foo') - Traceback (innermost last): - File "", line 1, in ? - TypeError: illegal argument type for built-in operation + + >>> try: r.testCallback('foo') + ... except TypeError, err: assert_integer_expected(err) + ... else: print 'no exception' + >>> r.testCallback(1) -1 >>> testCallback(r, 1) @@ -947,9 +1017,12 @@ test inheritB2 -2 >>> str(i) '2' - >>> j = i/i - Traceback (innermost last): - TypeError: bad operand type(s) for / + >>> try: j = i/i + ... except TypeError, err: + ... assert re.match(r'(bad|unsupported) operand type\(s\) for /', + ... str(err)) + ... else: print 'no exception' + >>> j = abs(i) Traceback (innermost last): TypeError: bad operand type for abs() @@ -1070,9 +1143,49 @@ test methodologies for wrapping functions that return a pointer 3 >>> eo.second 1 -''' -from demo import * +======== test [plain] char converters ============== + >>> get_plain_char() + 'x' + >>> use_plain_char('a') + 'aaa' + >>> use_const_plain_char('b') + 'bbbbb' + +======== test std::complex converters ============== + >>> c = dpolar(3, 5) + >>> type(c) + + >>> '%.3g' % (dreal(c)) + '0.851' + >>> '%.3g' % (dimag(c)) + '-2.88' + >>> '%.3g' % (freal(c)) + '0.851' + >>> '%.3g' % (fimag(c)) + '-2.88' + >>> c = fpolar(7, 13) + >>> type(c) + + >>> '%.3g' % (fimag(c)) + '2.94' + >>> '%.3g' % (freal(c)) + '6.35' + >>> '%.3g' % (dimag(c)) + '2.94' + >>> '%.3g' % (dreal(c)) + '6.35' + +''' +#' + +def assert_integer_expected(err): + """Handle a common error report which appears differently in Python 1.5.x and 2.0""" + assert isinstance(err, TypeError) + message = str(err) + assert (message == "illegal argument type for built-in operation" + or message == "an integer is required") + import string import re import sys @@ -1080,8 +1193,8 @@ import sys def run(args = None): if args is not None: sys.argv = args - import doctest, test_extclass - doctest.testmod(test_extclass) + import doctest, comprehensive + return doctest.testmod(comprehensive) if __name__ == '__main__': - run() + sys.exit(run()[0]) diff --git a/test/doctest.py b/test/doctest.py new file mode 100644 index 00000000..248da82a --- /dev/null +++ b/test/doctest.py @@ -0,0 +1,1112 @@ +# Module doctest version 0.9.4 +# Released to the public domain 27-Mar-1999, +# by Tim Peters (tim_one@email.msn.com). + +# Provided as-is; use at your own risk; no warranty; no promises; enjoy! + +"""module_builder doctest -- a framework for running examples in docstrings. + +NORMAL USAGE + +In normal use, end each module M with: + +def _test(): + import doctest, M # replace M with your module's name + return doctest.testmod(M) # ditto + +if __name__ == "__main__": + _test() + +Then running the module as a script will cause the examples in the +docstrings to get executed and verified: + +python M.python + +This won't display anything unless an example fails, in which case +the failing example(s) and the cause(s) of the failure(s) are printed +to stdout (why not stderr? because stderr is a lame hack <0.2 wink>), +and the final line of output is "Test failed.". + +Run it with the -v switch instead: + +python M.python -v + +and a detailed report of all examples tried is printed to stdout, along +with assorted summaries at the end. + +You can force verbose mode by passing "verbose=1" to testmod, or prohibit +it by passing "verbose=0". In either of those cases, sys.argv is not +examined by testmod. + +In any case, testmod returns a 2-tuple of ints (f, t), where f is the +number of docstring examples that failed and t is the total number of +docstring examples attempted. + + +WHICH DOCSTRINGS ARE EXAMINED? + ++ M.__doc__. + ++ f.__doc__ for all functions f in M.__dict__.values(), except those + with private names. + ++ C.__doc__ for all classes C in M.__dict__.values(), except those with + private names. + ++ If M.__test__ exists and "is true", it must be a dict, and + each entry maps a (string) name to a function object, class object, or + string. function and class object docstrings found from M.__test__ + are searched even if the name is private, and strings are searched + directly as if they were docstrings. In output, a key K in M.__test__ + appears with name + .__test__.K + +Any classes found are recursively searched similarly, to test docstrings +in their contained methods and nested classes. Private names reached +from M's globals are skipped, but all names reached from M.__test__ are +searched. + +By default, a name is considered to be private if it begins with an +underscore (like "_my_func") but doesn't both begin and end with (at +least) two underscores (like "__init__"). You can change the default +by passing your own "isprivate" function to testmod. + +If you want to test docstrings in objects with private names too, stuff +them into an M.__test__ dict, or see ADVANCED USAGE below (e.g., pass your +own isprivate function to Tester's constructor, or call the rundoc method +of a Tester obj). + +Warning: imports can cause trouble; e.g., if you do + +from XYZ import XYZclass + +then XYZclass is a name in M.__dict__ too, and doctest has no way to +know that XYZclass wasn't *defined* in M. So it may try to execute the +examples in XYZclass's docstring, and those in turn may require a +different set of globals to work correctly. I prefer to do "import *"- +friendly imports, a la + +import XYY +_XYZclass = XYZ.XYZclass +del XYZ + +and then the leading underscore stops testmod from going nuts. You may +prefer the method in the next section. + + +WHAT'S THE EXECUTION CONTEXT? + +By default, each time testmod finds a docstring to test, it uses a +*copy* of M's globals (so that running tests on a module doesn't change +the module's real globals, and so that one test in M can't leave behind +crumbs that accidentally allow another test to work). This means +examples can freely use any names defined at top-level in M. It also +means that sloppy imports (see above) can cause examples in external +docstrings to use globals inappropriate for them. + +You can force use of your own dict as the execution context by passing +"globs=your_dict" to testmod instead. Presumably this would be a copy +of M.__dict__ merged with the globals from other imported modules. + + +WHAT IF I WANT TO TEST A WHOLE PACKAGE? + +Piece o' cake, provided the modules do their testing from docstrings. +Here's the test.python I use for the world's most elaborate Rational/ +floating-base-conversion pkg (which I'll distribute some day): + +from Rational import Cvt +from Rational import Format +from Rational import machprec +from Rational import Rat +from Rational import Round +from Rational import utils + +modules = (Cvt, + Format, + machprec, + Rat, + Round, + utils) + +def _test(): + import doctest + import sys + verbose = "-v" in sys.argv + for mod in modules: + doctest.testmod(mod, verbose=verbose, report=0) + doctest.master.summarize() + +if __name__ == "__main__": + _test() + +IOW, it just runs testmod on all the pkg modules. testmod remembers the +names and outcomes (# of failures, # of tries) for each item it's seen, +and passing "report=0" prevents it from printing a summary in verbose +mode. Instead, the summary is delayed until all modules have been +tested, and then "doctest.master.summarize()" forces the summary at the +end. + +So this is very nice in practice: each module can be tested individually +with almost no work beyond writing up docstring examples, and collections +of modules can be tested too as a unit with no more work than the above. + + +WHAT ABOUT EXCEPTIONS? + +No problem, as long as the only output generated by the example is the +traceback itself. For example: + + >>> 1/0 + Traceback (innermost last): + File "", line 1, in ? + ZeroDivisionError: integer division or modulo + >>> + +Note that only the exception type and value are compared (specifically, +only the last line in the traceback). + + +ADVANCED USAGE + +doctest.testmod() captures the testing policy I find most useful most +often. You may want other policies. + +testmod() actually creates a local obj of class doctest.Tester, +runs appropriate methods of that class, and merges the results into +global Tester obj doctest.master. + +You can create your own instances of doctest.Tester, and so build your +own policies, or even run methods of doctest.master directly. See +doctest.Tester.__doc__ for details. + + +SO WHAT DOES A DOCSTRING EXAMPLE LOOK LIKE ALREADY!? + +Oh ya. It's easy! In most cases a copy-and-paste of an interactive +console session works fine -- just make sure the leading whitespace +is rigidly consistent (you can mix tabs and spaces if you're too lazy +to do it right, but doctest is not in the business of guessing what +you think a tab means). + + >>> # comments are ignored + >>> x = 12 + >>> x + 12 + >>> if x == 13: + ... print "yes" + ... else: + ... print "no" + ... print "NO" + ... print "NO!!!" + ... + no + NO + NO!!! + >>> + +Any expected output must immediately follow the final ">>>" or "..." +line containing the code, and the expected output (if any) extends +to the next ">>>" or all-whitespace line. That's it. + +Bummers: + ++ Expected output cannot contain an all-whitespace line, since such a + line is taken to signal the end of expected output. + ++ Output to stdout is captured, but not output to stderr (exception + tracebacks are captured via a different means). + ++ If you continue a line via backslashing in an interactive session, + or for any other reason use a backslash, you need to double the + backslash in the docstring version. This is simply because you're + in a string, and so the backslash must be escaped for it to survive + intact. Like: + +>>> if "yes" == \\ +... "y" + \\ +... "es": # in the source code you'll see the doubled backslashes +... print 'yes' +yes + +The starting column doesn't matter: + +>>> assert "Easy!" + >>> import math + >>> math.floor(1.9) + 1.0 + +and as many leading whitespace characters are stripped from the expected +output as appeared in the initial ">>>" line that triggered it. + +If you execute this very file, the examples above will be found and +executed, leading to this output in verbose mode: + +Running doctest.__doc__ +Trying: 1/0 +Expecting: +Traceback (innermost last): + File "", line 1, in ? +ZeroDivisionError: integer division or modulo +ok +Trying: x = 12 +Expecting: nothing +ok +Trying: x +Expecting: 12 +ok +Trying: +if x == 13: + print "yes" +else: + print "no" + print "NO" + print "NO!!!" +Expecting: +no +NO +NO!!! +ok +... and a bunch more like that, with this summary at the end: + +5 items had no tests: + doctest.Tester.__init__ + doctest.Tester.run__test__ + doctest.Tester.summarize + doctest.run_docstring_examples + doctest.testmod +12 items passed all tests: + 8 tests in doctest + 6 tests in doctest.Tester + 10 tests in doctest.Tester.merge + 7 tests in doctest.Tester.rundict + 3 tests in doctest.Tester.rundoc + 3 tests in doctest.Tester.runstring + 2 tests in doctest.__test__._TestClass + 2 tests in doctest.__test__._TestClass.__init__ + 2 tests in doctest.__test__._TestClass.get + 1 tests in doctest.__test__._TestClass.square + 2 tests in doctest.__test__.string + 7 tests in doctest.is_private +53 tests in 17 items. +53 passed and 0 failed. +Test passed. +""" + +# 0,0,1 06-Mar-1999 +# initial version posted +# 0,0,2 06-Mar-1999 +# loosened parsing: +# cater to stinkin' tabs +# don't insist on a blank after PS2 prefix +# so trailing "... " line from a compound stmt no longer +# breaks if the file gets whitespace-trimmed +# better error msgs for inconsistent leading whitespace +# 0,9,1 08-Mar-1999 +# exposed the Tester class and added client methods +# plus docstring examples of their use (eww - head-twisting!) +# fixed logic error in reporting total # of tests & failures +# added __test__ support to testmod (a pale reflection of Christian +# Tismer's vision ...) +# removed the "deep" argument; fiddle __test__ instead +# simplified endcase logic for extracting tests, and running them. +# before, if no output was expected but some was produced +# anyway via an eval'ed result, the discrepancy wasn't caught +# made TestClass private and used __test__ to get at it +# many doc updates +# speed _SpoofOut for long expected outputs +# 0,9,2 09-Mar-1999 +# throw out comments from examples, enabling use of the much simpler +# exec compile(... "single") ... +# for simulating the runtime; that barfs on comment-only lines +# used the traceback module to do a much better job of reporting +# exceptions +# run __doc__ values thru str(), "just in case" +# privateness of names now determined by an overridable "isprivate" +# function +# by default a name now considered to be private iff it begins with +# an underscore but doesn't both begin & end with two of 'em; so +# e.g. class_t.__init__ etc are searched now -- as they always +# should have been +# 0,9,3 18-Mar-1999 +# added .flush stub to _SpoofOut (JPython buglet diagnosed by +# Hugh Emberson) +# repaired ridiculous docs about backslashes in examples +# minor internal changes +# changed source to Unix line-end conventions +# moved __test__ logic into new Tester.run__test__ method +# 0,9,4 27-Mar-1999 +# report item name and line # in failing examples +# 0,9,5 29-Jun-1999 +# allow straightforward exceptions in examples - thanks to Mark Hammond! +# 0,9,5,1 31-Mar-2000 +# break cyclic references to functions which are defined in docstrings, +# avoiding cyclic trash +# 0,9,5,2 11-Apr-2000 +# made module argument to testmod optional; it runs testmod on the __main__ +# module in that case. + +__version__ = 0, 9, 5 + +import types +_FunctionType = types.FunctionType +_ClassType = types.ClassType +_ModuleType = types.ModuleType +_StringType = types.StringType +del types + +import string +_string_find = string.find +_string_join = string.join +_string_split = string.split +_string_rindex = string.rindex +del string + +import re +PS1 = ">>>" +PS2 = "..." +_isPS1 = re.compile(r"(\s*)" + re.escape(PS1)).match +_isPS2 = re.compile(r"(\s*)" + re.escape(PS2)).match +_isEmpty = re.compile(r"\s*$").match +_isComment = re.compile(r"\s*#").match +del re + +# Extract interactive examples from a string. Return a list of triples, +# (source, outcome, lineno). "source" is the source code, and ends +# with a newline iff the source spans more than one line. "outcome" is +# the expected output if any, else an empty string. When not empty, +# outcome always ends with a newline. "lineno" is the line number, +# 0-based wrt the start of the string, of the first source line. + +def _extract_examples(s): + isPS1, isPS2 = _isPS1, _isPS2 + isEmpty, isComment = _isEmpty, _isComment + examples = [] + lines = _string_split(s, "\n") + i, n = 0, len(lines) + while i < n: + line = lines[i] + i = i + 1 + m = isPS1(line) + if m is None: + continue + j = m.end(0) # beyond the prompt + if isEmpty(line, j) or isComment(line, j): + # a bare prompt or comment -- not interesting + continue + lineno = i - 1 + if line[j] != " ": + raise ValueError("line " + `lineno` + " of docstring lacks " + "blank after " + PS1 + ": " + line) + j = j + 1 + blanks = m.group(1) + nblanks = len(blanks) + # suck up this and following PS2 lines + source = [] + while 1: + source.append(line[j:]) + line = lines[i] + m = isPS2(line) + if m: + if m.group(1) != blanks: + raise ValueError("inconsistent leading whitespace " + "in line " + `i` + " of docstring: " + line) + i = i + 1 + else: + break + if len(source) == 1: + source = source[0] + else: + # get rid of useless null line from trailing empty "..." + if source[-1] == "": + del source[-1] + source = _string_join(source, "\n") + "\n" + # suck up response + if isPS1(line) or isEmpty(line): + expect = "" + else: + expect = [] + while 1: + if line[:nblanks] != blanks: + raise ValueError("inconsistent leading whitespace " + "in line " + `i` + " of docstring: " + line) + expect.append(line[nblanks:]) + i = i + 1 + line = lines[i] + if isPS1(line) or isEmpty(line): + break + expect = _string_join(expect, "\n") + "\n" + examples.append( (source, expect, lineno) ) + return examples + +# Capture stdout when running examples. + +class _SpoofOut: + def __init__(self): + self.clear() + def write(self, s): + self.buf.append(s) + def get(self): + return _string_join(self.buf, "") + def clear(self): + self.buf = [] + def flush(self): + # JPython calls flush + pass + +# Display some tag-and-msg pairs nicely, keeping the tag and its msg +# on the same line when that makes sense. + +def _tag_out(printer, *tag_msg_pairs): + for tag, msg in tag_msg_pairs: + printer(tag + ":") + msg_has_nl = msg[-1:] == "\n" + msg_has_two_nl = msg_has_nl and \ + _string_find(msg, "\n") < len(msg) - 1 + if len(tag) + len(msg) < 76 and not msg_has_two_nl: + printer(" ") + else: + printer("\n") + printer(msg) + if not msg_has_nl: + printer("\n") + +# Run list of examples, in context globs. "out" can be used to display +# stuff to "the real" stdout, and fakeout is an obj of _SpoofOut +# that captures the examples' std output. Return (#failures, #tries). + +def _run_examples_inner(out, fakeout, examples, globs, verbose, name): + import sys, traceback + OK, BOOM, FAIL = range(3) + NADA = "nothing" + stderr = _SpoofOut() + failures = 0 + for source, want, lineno in examples: + if verbose: + _tag_out(out, ("Trying", source), + ("Expecting", want or NADA)) + fakeout.clear() + try: + exec compile(source, "", "single") in globs + got = fakeout.get() + state = OK + except: + # See whether the exception was expected. + if _string_find(want, "Traceback (innermost last):\n") == 0: + # Only compare exception type and value - the rest of + # the traceback isn't necessary. + want = _string_split(want, '\n')[-2] + '\n' + exc_type, exc_val, exc_tb = sys.exc_info() + got = traceback.format_exception_only(exc_type, exc_val)[0] + state = OK + else: + # unexpected exception + stderr.clear() + traceback.print_exc(file=stderr) + state = BOOM + + if state == OK: + if got == want: + if verbose: + out("ok\n") + continue + state = FAIL + + assert state in (FAIL, BOOM) + failures = failures + 1 + out("*" * 65 + "\n") + _tag_out(out, ("Failure in example", source)) + out("from line #" + `lineno` + " of " + name + "\n") + if state == FAIL: + _tag_out(out, ("Expected", want or NADA), ("Got", got)) + else: + assert state == BOOM + _tag_out(out, ("Exception raised", stderr.get())) + return failures, len(examples) + +# Run list of examples, in context globs. Return (#failures, #tries). + +def _run_examples(examples, globs, verbose, name): + import sys + saveout = sys.stdout + try: + sys.stdout = fakeout = _SpoofOut() + x = _run_examples_inner(saveout.write, fakeout, examples, + globs, verbose, name) + finally: + sys.stdout = saveout + return x + +def run_docstring_examples(f, globs, verbose=0, name="NoName"): + """f, globs, verbose=0, name="NoName" -> run examples from f.__doc__. + + Use dict globs as the globals for execution. + Return (#failures, #tries). + + If optional arg verbose is true, print stuff even if there are no + failures. + Use string name in failure msgs. + """ + + try: + doc = f.__doc__ + if not doc: + # docstring empty or None + return 0, 0 + # just in case CT invents a doc object that has to be forced + # to look like a string <0.9 wink> + doc = str(doc) + except: + return 0, 0 + + e = _extract_examples(doc) + if not e: + return 0, 0 + return _run_examples(e, globs, verbose, name) + +def is_private(prefix, base): + """prefix, base -> true iff name prefix + "." + base is "private". + + Prefix may be an empty string, and base does not contain a period. + Prefix is ignored (although functions you write conforming to this + protocol may make use of it). + Return true iff base begins with an (at least one) underscore, but + does not both begin and end with (at least) two underscores. + + >>> is_private("a.b", "my_func") + 0 + >>> is_private("____", "_my_func") + 1 + >>> is_private("someclass", "__init__") + 0 + >>> is_private("sometypo", "__init_") + 1 + >>> is_private("x.y.z", "_") + 1 + >>> is_private("_x.y.z", "__") + 0 + >>> is_private("", "") # senseless but consistent + 0 + """ + + return base[:1] == "_" and not base[:2] == "__" == base[-2:] + +class Tester: + """class_t Tester -- runs docstring examples and accumulates stats. + +In normal use, function doctest.testmod() hides all this from you, +so use that if you can. Create your own instances of Tester to do +fancier things. + +Methods: + runstring(s, name) + Search string s for examples to run; use name for logging. + Return (#failures, #tries). + + rundoc(object, name=None) + Search object.__doc__ for examples to run; use name (or + object.__name__) for logging. Return (#failures, #tries). + + rundict(d, name) + Search for examples in docstrings in all of d.values(); use name + for logging. Return (#failures, #tries). + + run__test__(d, name) + Treat dict d like module.__test__. Return (#failures, #tries). + + summarize(verbose=None) + Display summary of testing results, to stdout. Return + (#failures, #tries). + + merge(other) + Merge in the test results from Tester obj "other". + +>>> from doctest import Tester +>>> t = Tester(globs={'x': 42}, verbose=0) +>>> t.runstring(r''' +... >>> x = x * 2 +... >>> print x +... 42 +... ''', 'XYZ') +***************************************************************** +Failure in example: print x +from line #2 of XYZ +Expected: 42 +Got: 84 +(1, 2) +>>> t.runstring(">>> x = x * 2\\n>>> print x\\n84\\n", 'example2') +(0, 2) +>>> t.summarize() +1 items had failures: + 1 of 2 in XYZ +***Test Failed*** 1 failures. +(1, 4) +>>> t.summarize(verbose=1) +1 items passed all tests: + 2 tests in example2 +1 items had failures: + 1 of 2 in XYZ +4 tests in 2 items. +3 passed and 1 failed. +***Test Failed*** 1 failures. +(1, 4) +>>> +""" + + def __init__(self, mod=None, globs=None, verbose=None, + isprivate=None): + """mod=None, globs=None, verbose=None, isprivate=None + +See doctest.__doc__ for an overview. + +Optional keyword arg "mod" is a module, whose globals are used for +executing examples. If not specified, globs must be specified. + +Optional keyword arg "globs" gives a dict to be used as the globals +when executing examples; if not specified, use the globals from +module mod. + +In either case, a copy of the dict is used for each docstring +examined. + +Optional keyword arg "verbose" prints lots of stuff if true, only +failures if false; by default, it's true iff "-v" is in sys.argv. + +Optional keyword arg "isprivate" specifies a function used to determine +whether a name is private. The default function is doctest.is_private; +see its docs for details. +""" + + if mod is None and globs is None: + raise TypeError("Tester.__init__: must specify mod or globs") + if mod is not None and type(mod) is not _ModuleType: + raise TypeError("Tester.__init__: mod must be a module; " + + `mod`) + if globs is None: + globs = mod.__dict__ + self.globs = globs + + if verbose is None: + import sys + verbose = "-v" in sys.argv + self.verbose = verbose + + if isprivate is None: + isprivate = is_private + self.isprivate = isprivate + + self.name2ft = {} # map name to (#failures, #trials) pair + + def runstring(self, s, name): + """ + s, name -> search string s for examples to run, logging as name. + + Use string name as the key for logging the outcome. + Return (#failures, #examples). + + >>> t = Tester(globs={}, verbose=1) + >>> test = r''' + ... # just an example + ... >>> x = 1 + 2 + ... >>> x + ... 3 + ... ''' + >>> t.runstring(test, "Example") + Running string Example + Trying: x = 1 + 2 + Expecting: nothing + ok + Trying: x + Expecting: 3 + ok + 0 of 2 examples failed in string Example + (0, 2) + """ + + if self.verbose: + print "Running string", name + f = t = 0 + e = _extract_examples(s) + if e: + globs = self.globs.copy() + f, t = _run_examples(e, globs, self.verbose, name) + globs.clear() # DWA - break cyclic references to functions defined in docstrings + if self.verbose: + print f, "of", t, "examples failed in string", name + self.__record_outcome(name, f, t) + return f, t + + def rundoc(self, object, name=None): + """ + object, name=None -> search object.__doc__ for examples to run. + + Use optional string name as the key for logging the outcome; + by default use object.__name__. + Return (#failures, #examples). + If object is a class object, search recursively for method + docstrings too. + object.__doc__ is examined regardless of name, but if object is + a class, whether private names reached from object are searched + depends on the constructor's "isprivate" argument. + + >>> t = Tester(globs={}, verbose=0) + >>> def _f(): + ... '''Trivial docstring example. + ... >>> assert 2 == 2 + ... ''' + ... return 32 + ... + >>> t.rundoc(_f) # expect 0 failures in 1 example + (0, 1) + """ + + if name is None: + try: + name = object.__name__ + except AttributeError: + raise ValueError("Tester.rundoc: name must be given " + "when object.__name__ doesn't exist; " + `object`) + if self.verbose: + print "Running", name + ".__doc__" + globs = self.globs.copy() + f, t = run_docstring_examples(object, globs, + self.verbose, name) + globs.clear() # DWA - break cyclic references to functions defined in docstrings + + if self.verbose: + print f, "of", t, "examples failed in", name + ".__doc__" + self.__record_outcome(name, f, t) + if type(object) is _ClassType: + f2, t2 = self.rundict(object.__dict__, name) + f = f + f2 + t = t + t2 + return f, t + + def rundict(self, d, name): + """ + d. name -> search for docstring examples in all of d.values(). + + For k, v in d.items() such that v is a function or class, + do self.rundoc(v, name + "." + k). Whether this includes + objects with private names depends on the constructor's + "isprivate" argument. + Return aggregate (#failures, #examples). + + >>> def _f(): + ... '''>>> assert 1 == 1 + ... ''' + >>> def g(): + ... '''>>> assert 2 != 1 + ... ''' + >>> d = {"_f": _f, "g": g} + >>> t = Tester(globs={}, verbose=0) + >>> t.rundict(d, "rundict_test") # _f is skipped + (0, 1) + >>> t = Tester(globs={}, verbose=0, isprivate=lambda x,y: 0) + >>> t.rundict(d, "rundict_test_pvt") # both are searched + (0, 2) + """ + + if not hasattr(d, "items"): + raise TypeError("Tester.rundict: d must support .items(); " + + `d`) + f = t = 0 + for thisname, value in d.items(): + if type(value) in (_FunctionType, _ClassType): + f2, t2 = self.__runone(value, name + "." + thisname) + f = f + f2 + t = t + t2 + return f, t + + def run__test__(self, d, name): + """d, name -> Treat dict d like module.__test__. + + Return (#failures, #tries). + See testmod.__doc__ for details. + """ + + failures = tries = 0 + prefix = name + "." + savepvt = self.isprivate + try: + self.isprivate = lambda *args: 0 + for k, v in d.items(): + thisname = prefix + k + if type(v) is _StringType: + f, t = self.runstring(v, thisname) + elif type(v) in (_FunctionType, _ClassType): + f, t = self.rundoc(v, thisname) + else: + raise TypeError("Tester.run__test__: values in " + "dict must be strings, functions " + "or classes; " + `v`) + failures = failures + f + tries = tries + t + finally: + self.isprivate = savepvt + return failures, tries + + def summarize(self, verbose=None): + """ + verbose=None -> summarize results, return (#failures, #tests). + + Print summary of test results to stdout. + Optional arg 'verbose' controls how wordy this is. By + default, use the verbose setting established by the + constructor. + """ + + if verbose is None: + verbose = self.verbose + notests = [] + passed = [] + failed = [] + totalt = totalf = 0 + for x in self.name2ft.items(): + name, (f, t) = x + assert f <= t + totalt = totalt + t + totalf = totalf + f + if t == 0: + notests.append(name) + elif f == 0: + passed.append( (name, t) ) + else: + failed.append(x) + if verbose: + if notests: + print len(notests), "items had no tests:" + notests.sort() + for thing in notests: + print " ", thing + if passed: + print len(passed), "items passed all tests:" + passed.sort() + for thing, count in passed: + print " %3d tests in %s" % (count, thing) + if failed: + print len(failed), "items had failures:" + failed.sort() + for thing, (f, t) in failed: + print " %3d of %3d in %s" % (f, t, thing) + if verbose: + print totalt, "tests in", len(self.name2ft), "items." + print totalt - totalf, "passed and", totalf, "failed." + if totalf: + print "***Test Failed***", totalf, "failures." + elif verbose: + print "Test passed." + return totalf, totalt + + def merge(self, other): + """ + other -> merge in test results from the other Tester obj. + + If self and other both have a test result for something + with the same name, the (#failures, #tests) results are + summed, and a warning is printed to stdout. + + >>> from doctest import Tester + >>> t1 = Tester(globs={}, verbose=0) + >>> t1.runstring(''' + ... >>> x = 12 + ... >>> print x + ... 12 + ... ''', "t1example") + (0, 2) + >>> + >>> t2 = Tester(globs={}, verbose=0) + >>> t2.runstring(''' + ... >>> x = 13 + ... >>> print x + ... 13 + ... ''', "t2example") + (0, 2) + >>> common = ">>> assert 1 + 2 == 3\\n" + >>> t1.runstring(common, "common") + (0, 1) + >>> t2.runstring(common, "common") + (0, 1) + >>> t1.merge(t2) + *** Tester.merge: 'common' in both testers; summing outcomes. + >>> t1.summarize(1) + 3 items passed all tests: + 2 tests in common + 2 tests in t1example + 2 tests in t2example + 6 tests in 3 items. + 6 passed and 0 failed. + Test passed. + (0, 6) + >>> + """ + + d = self.name2ft + for name, (f, t) in other.name2ft.items(): + if d.has_key(name): + print "*** Tester.merge: '" + name + "' in both" \ + " testers; summing outcomes." + f2, t2 = d[name] + f = f + f2 + t = t + t2 + d[name] = f, t + + def __record_outcome(self, name, f, t): + if self.name2ft.has_key(name): + print "*** Warning: '" + name + "' was tested before;", \ + "summing outcomes." + f2, t2 = self.name2ft[name] + f = f + f2 + t = t + t2 + self.name2ft[name] = f, t + + def __runone(self, target, name): + if "." in name: + i = _string_rindex(name, ".") + prefix, base = name[:i], name[i+1:] + else: + prefix, base = "", base + if self.isprivate(prefix, base): + return 0, 0 + return self.rundoc(target, name) + +master = None + +def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None, + report=1): + """m=None, name=None, globs=None, verbose=None, isprivate=None, report=1 + + Test examples in docstrings in functions and classes reachable from + module m, starting with m.__doc__. Private names are skipped. + + Also test examples reachable from dict m.__test__ if it exists and is + not None. m.__dict__ maps names to functions, classes and strings; + function and class docstrings are tested even if the name is private; + strings are tested directly, as if they were docstrings. + + Return (#failures, #tests). + + See doctest.__doc__ for an overview. + + Optional keyword arg "name" gives the name of the module; by default + use m.__name__. + + Optional keyword arg "globs" gives a dict to be used as the globals + when executing examples; by default, use m.__dict__. A copy of this + dict is actually used for each docstring, so that each docstring's + examples start with a clean slate. + + Optional keyword arg "verbose" prints lots of stuff if true, prints + only failures if false; by default, it's true iff "-v" is in sys.argv. + + Optional keyword arg "isprivate" specifies a function used to + determine whether a name is private. The default function is + doctest.is_private; see its docs for details. + + Optional keyword arg "report" prints a summary at the end when true, + else prints nothing at the end. In verbose mode, the summary is + detailed, else very brief (in fact, empty if all tests passed). + + Advanced tomfoolery: testmod runs methods of a local obj of + class doctest.Tester, then merges the results into (or creates) + global Tester obj doctest.master. Methods of doctest.master + can be called directly too, if you want to do something unusual. + Passing report=0 to testmod is especially useful then, to delay + displaying a summary. Invoke doctest.master.summarize(verbose) + when you're done fiddling. + """ + + global master + + if m is None: + import sys + # DWA - m will still be None if this wasn't invoked from the command + # line, in which case the following TypeError is about as good an error + # as we should expect + m = sys.modules.get('__main__') + + if type(m) is not _ModuleType: + raise TypeError("testmod: module required; " + `m`) + if name is None: + name = m.__name__ + tester = Tester(m, globs=globs, verbose=verbose, isprivate=isprivate) + failures, tries = tester.rundoc(m, name) + f, t = tester.rundict(m.__dict__, name) + failures = failures + f + tries = tries + t + if hasattr(m, "__test__"): + testdict = m.__test__ + if testdict: + if not hasattr(testdict, "items"): + raise TypeError("testmod: module.__test__ must support " + ".items(); " + `testdict`) + f, t = tester.run__test__(testdict, name + ".__test__") + failures = failures + f + tries = tries + t + if report: + tester.summarize() + if master is None: + master = tester + else: + master.merge(tester) + return failures, tries + +class _TestClass: + """ + A pointless class, for sanity-checking of docstring testing. + + Methods: + square() + get() + + >>> _TestClass(13).get() + _TestClass(-12).get() + 1 + >>> hex(_TestClass(13).square().get()) + '0xa9' + """ + + def __init__(self, val): + """val -> _TestClass object with associated value val. + + >>> t = _TestClass(123) + >>> print t.get() + 123 + """ + + self.val = val + + def square(self): + """square() -> square TestClass's associated value + + >>> _TestClass(13).square().get() + 169 + """ + + self.val = self.val ** 2 + return self + + def get(self): + """get() -> return TestClass's associated value. + + >>> x = _TestClass(-42) + >>> print x.get() + -42 + """ + + return self.val + +__test__ = {"_TestClass": _TestClass, + "string": r""" + Example of a string object, searched as-is. + >>> x = 1; y = 2 + >>> x + y, x * y + (3, 2) + """ + } + +def _test(): + import doctest + return doctest.testmod(doctest) + +if __name__ == "__main__": + _test() diff --git a/todo.txt b/todo.txt new file mode 100644 index 00000000..d97eade5 --- /dev/null +++ b/todo.txt @@ -0,0 +1,426 @@ +Check for const reference parameters in all from_python functions in py.h, including implementations. +Better python and C++ exception handling/error reporting. +long long support +use Python generic numeric coercion in from_python() for C++ numeric types +Rename PyPtr to Reference. +Report Cygwin linker memory issues +__init__ stuff + Make abstract classes non-instantiable (?) + Call default __init__ functions automatically where applicable (?) +Support for Python LONG types in Objects.h +Throw TypeError after asserting when objects from objects.cpp detect a type mismatch. +Figure out how to package everything as a shared library. +Unicode string support +Add read-only wrapper for __dict__ attribute +Objects.h support for generic objects, Sequence objects, etc. +empty() member functions for objects.hpp + +Testing + Python 2.0 + object revival in __del__ + More thorough tests of objects.h/cpp classes + Better reference-count checking + +Optimizations + Remove one level of indirection on type objects (no vtbl?). + Specializations of Caller<> for commmon combinations of argument types (?) + Replace uses of XXXable classes + Don't allocate instance __dict__ unless used. + + +Documentation: + + differences between Python classes and ExtensionClasses + additional capabilities of ExtensionClasses + slice adjustment + + Why special attributes other than __doc__ and __name__ are immutable. + + An example of the problems with the built-in Python classes. + + >>> class A: + ... def __getattr__(self, name): + ... return 'A.__getattr__' + ... + >>> class B(A): pass + ... + >>> class C(B): pass + ... + >>> C().x + 'A.__getattr__' + >>> B.__bases__ = () + >>> C().x + 'A.__getattr__' + + Smart pointers + #ifndef PY_NO_INLINE_FRIENDS_IN_NAMESPACE + namespace py { + #endif + + template + struct VtkConverters + { + typedef py::PyExtensionClassConverters Converters; + + friend vtk_ptr& from_python(PyObject* p, py::Type&>) + { return Converters::ptr_from_python(p, py::Type >()); } + + friend vtk_ptr& from_python(PyObject* p, py::Type >) + { return Converters::ptr_from_python(p, py::Type >()); } + + friend const vtk_ptr& from_python(PyObject* p, py::Type&>) + { return Converters::ptr_from_python(p, py::Type >()); } + + friend PyObject* to_python(vtk_ptr x) + { return Converters::ptr_to_python(x); } + }; + + #ifndef PY_NO_INLINE_FRIENDS_IN_NAMESPACE + } + #endif + + template + struct VtkWrapper : py::ClassWrapper, py::VtkConverters + { + typedef py::ClassWrapper Base; + VtkWrapper(Module& module, const char* name) + : Base(module, name) {} + }; + + exception handling + + Advanced Topics: + Advanced Type Conversion + adding conversions for fundamental types + generic conversions for template types (with partial spec). + + Interacting with built-in Python objects and types from C++ + + dealing with non-const reference/pointer parameters + + extending multiple-argument support using gen_all.py + + + Fancy wrapping tricks + templates + Yes. If you look at the examples in extclass_demo.cpp you'll see that I have + exposed several template instantiations (e.g. std::pair) in Python. + Keep in mind, however, that you can only expose a template instantiation, + not a template. In other words, MyTemplate can be exposed. MyTemplate + itself cannot. + + Well, that's not strictly true. Wow, this is more complicated to explain + than I thought. + You can't make an ExtensionClass, since after all MyTemplate is + not a type. You can only expose a concrete type to Python. + + What you *can* do (if your compiler supports partial ordering of function + templates - MSVC is broken and does not) is to write appropriate + from_python() and to_python() functions for converting a whole class of + template instantiations to/from Python. That won't let you create an + instance of MyTemplate from Python, but it will let you + pass/return arbitrary MyTemplate instances to/from your + wrapped C++ functions. + + template + MyTemplate from_python(PyObject* x, py::Type >) + { + // code to convert x into a MyTemplate... that part is up to you + } + + template + PyObject* from_python(const MyTemplate&) + { + // code to convert MyTemplate into a PyObject*... that part is up to + you + } + + For example, you could use this to convert Python lists to/from + std::vector automatically. + + Pointer return values + + Case 1: + + > I am now also able to wrap the problematic TextRecordIterator for Python. + > However, one of its function compiles with this warning: + > + > d:\py_cpp/caller.h(33) : warning C4800: 'const class Record *const ' + > : forcing value to bool 'true' or 'false' (performance warning) + > d:\py_cpp/functions.h(54) : see reference to function template + > instantiation 'struct _object *__cdecl py::Caller::call(const class Record + > *const (__thiscall TextRecordIterator::*)(void),struct _object *,struct + > _object *)' being compiled + > + > If you look at the offending code, you'll see that we really do need to + > get back that pointer: + > + > const Record* const TextRecordIterator::Next() { + > if (fStatus != RecordIterator::SUCCESS) { + > return 0; + > } else { + > return &fData; + > } + > } + > + > The point of the TextRecordIterator is to hand over one reord after + > another. A bool wouldn't do us much good here :-) + > + > Do you have any suggestions for fixing this? + + In general, py_cpp doesn't automatically convert pointer return values + to_python because pointers have too many potential meanings. Is it an + iterator? A pointer to a single element? An array? Is ownership being passed + to Python or is the pointer really just a reference? If the latter, what + happens when some C++ code deletes the referent. The only exception to this + rule is const char*, since it has a generally accepted interpretation (could + be trouble with some generic code, though!) + + If you have wrapped the Record class, you could add this to namespace py: + + PyObject* to_python(const Record* p) { + return to_python(*p); + } + + Of course, this will cause the Record class to be copied. If you can't live + with that (Record would have to be /really/ heavyweight to make this + worthwhile), you can follow one of these dangerous approaches: + + 1. Use the technique I described with dangerous_array in + http://www.egroups.com/message/boost/6196. You do not have to expose Record + explicitly in this case. Instead the class you expose will be more of a + Record_proxy + + 2. Wrap Record in the usual way, then add the following to namespace py: + + PyObject* to_python(const Record* p) + { + return ExtensionClass::ptr_to_python(const_cast(p)); + } + + This will cause the Record* to be treated as though it were an owning smart + pointer, even though it's not. Be sure you don't use the reference for + anything from Python once the pointer becomes invalid, though. Don't worry + too much about the const-correctness issue: Const-correctness is completely + lost to Python anyway! + + 3. As above, but instead wrap const Record rather than plain Record. Then + you can avoid the const_cast, but you obviously can't def() any non-const + member functions of Record. + + Case 2: + + > I have yet another question. This is more a general wrapper question. + > Let me say that there is a function that returns a float* which most + > probably is an array. Similarly if I have a function that takes a + > float* as an argument, what is the best way of wrapping this? + + I think you have correctly perceived that it doesn't make sense for me to + automatically convert all pointers, since the ownership semantics are so + blurry. + + > 1) If the array is small it makes sense to convert it to either a + > tuple or list. What is the easiest way to do this?? I am looking + > for a way that makes one write the least code. :) + + How can you tell the length of the array from a single pointer? + Once you've answered that question, you can expose a wrapper function which + returns an instance of the py::Tuple or py::List class from objects.h. If + you are using a List, for example, you could write something like this: + + py::List wrap_f() + { + T* start = f(); + py::List x; + for (T* p = start; p != start + length_constant; ++p) + x.push_back(py::to_python(*p)); + return x; + } + + > 2) If the array is large it may not make sense to use a list/tuple + > esp. if the values are used for computationally intense programs. + + In this case you can do one of several somewhat dangerous things. Why + dangerous? Because python can not control the lifetime of the data, so the + data in the array may be destroyed or become invalid before the last + reference to it disappears. The basic approach is to make a small C++ class + which contains the pointer, and expose that: + + // UNTESTED + template + struct dangerous_array + { + dangerous_array(T* start, T* end) + : m_start(start), m_end(end) {} + + // exposed as "__len__" + std::size_t length() { + return m_end - m_start; + } + + // exposed as "__getitem__" + T get_item(std::size_t n) { + check_range(n); + return start[n]; + } + + // exposed as "__setitem__" if the array is mutable + void set_item(std::size_t n, const T& x) { + check_range(n); + start[n] = x; + } + private: + void check_range(std::size_t n) { + if (n >= m_end - m_start) { + PyErr_SetString(PyExc_IndexError, "array index out of range"); + throw py::ErrorAlreadySet; + } + } + T* m_start; + T* m_end; + }; + + A reasonably safe approach would be to make a wrapper function for each + function that returns a T*, and expose that instead. If you're too lazy and + you really like to live on the edge, though, you can write to_python(T*) in + terms of to_python(const dangerous_array&), and you'll automatically + convert all T* return values to a wrapped dangerous_array. + + > 3) For an arbitrary class "class_A", say, can py_cpp handle + > references to class_A &instance, or class_A *instance?? i.e. will it + > wrap function calls to such objects? This question is obviously + > related to the earlier questions. + + Yes, iff class_A has been exposed to python with a ClassWrapper. + See http://people.ne.mediaone.net/abrahams/downloads/under-the-hood.html for + a few details. + + raw C++ arrays + You could expose a function like this one to get the desired effect: + + #include + void set_len(UnitCell& x, py::Tuple tuple) + { + double len[3]; + for (std::size_t i =0; i < 3; ++i) + len[i] = py::from_python(tuple[i].get(), py::Type()); + x.set_len(len); + } + + Types that are already wrapped by other libraries + + It's not documented yet, but you should be able to use a raw PyObject* or a + py::Ptr as one parameter to your C++ function. Then you can manipulate it as + any other generic Python object. + + Alternatively, If the NTL gives you a C/C++ interface, you can also write + your own converter function: + + some_ntl_type& from_python(PyObject* p, py::Type) + { + // an Example implementation. Basically, you need + // to extract the NTL type from the PyObject*. + if (p->ob_type != NTL_long_type) { + PyErr_SetString(PyExc_TypeErr, "NTL long required"); + throw py::ArgumentError(); + } + return *static_cast(p); + } + + then the C++ functions you're wrapping can take a some_NTL_type& parameter + directly. + + "Thin converting wrappers" for constructors + + hijack some of the functionality + described in the section on Overridable Virtual Functions (even though you + don't have any virtual functions). I suggest this workaround: + + struct UnitCellWrapper : UnitCell + { + UnitCellWrapper(PyObject* self, py::Tuple x, py::Tuple y) + : UnitCell(from_python(x[1], py::Type()), + from_python(x[2], py::Type()), + from_python(x[3], py::Type()), + from_python(y[1], py::Type()), + from_python(y[2], py::Type()), + from_python(y[3], py::Type())) + {} + } + + py::ClassWrapper unit_cell_class; + unit_cell_class.def(py::Constructor()); + ... + + returning references to wrapped objects + + the importance of declaration order of ClassWrappers/ExtensionInstances + + out parameters and non-const pointers + + Calling back into Python: + // caveat: UNTESTED! + #include + #include + #include + #include + int main() + { + try { + py::Ptr module(PyImport_ImportModule("weapons")); + const int strength = 10; + const char* manufacturer = "Vordon Empire"; + py::Ptr a_blaster(py::Callback::call_method( + module.get(), "Blaster", strength, manufacturer)); + py::Callback::call_method(a_blaster.get(), "Fire"); + int old_strength = py::Callback::call_method(a_blaster.get(), "get_strength"); + py::Callback::call_method(a_blaster.get(), "set_strength", 5); + } + catch(...) + { + } + } + + Miscellaneous + About the vc6 project and the debug build + About doctest.py + +Boost remarks: + + > > One of us is completely nuts ;->. How can I move the test + > > (is_prefix(enablers[i].name + 2, name + 2)) outside the loop if it + depends + > > on the loop index, i? + > > + > name += 2; + > for() + > { + > if (is_prefix(enablers[i].name + 2, name)) + > } + + I see now. I guess I should stop pussyfooting and either go for optimization + or clarity here, eh? + + ------ + + > Re: Dict + > Why abbreviate this? Code is read 5 or 6 times for every time its + > written. The few extra characters don't affect compile time or program + > speed. It's part of my personal goal of write what you mean, name them + what + > they are. + + I completely agree. Abbrevs rub me the wrong way, 2 ;-> + + ------- + + + + +Later: + keyword and varargs? + Put explicit Type<> arguments at the beginnings of overloads, to make them look more like template instance specifications. + +Known bugs + can't handle 'const void' return values + Who returns 'const void'? I did it once, by mistake ;)