├── .clang-format ├── .gitignore ├── CITATIONS.bib ├── CMakeLists.txt ├── ChangeLog.md ├── Jenkinsfile ├── LICENSE.txt ├── README.md ├── TODO.txt ├── bin ├── CMakeLists.txt ├── c++2cxx.in ├── c++2py.in ├── c++2rst.in └── cp_rs.py ├── c++ └── cpp2py │ ├── CMakeLists.txt │ ├── converters │ ├── basic_types.hpp │ ├── complex.hpp │ ├── function.hpp │ ├── map.hpp │ ├── optional.hpp │ ├── pair.hpp │ ├── set.hpp │ ├── span.hpp │ ├── std_array.hpp │ ├── string.hpp │ ├── tuple.hpp │ ├── variant.hpp │ └── vector.hpp │ ├── cpp2py.hpp │ ├── exceptions.cpp │ ├── exceptions.hpp │ ├── get_module.hpp │ ├── macros.hpp │ ├── misc.cpp │ ├── misc.hpp │ ├── numpy_proxy.cpp │ ├── numpy_proxy.hpp │ ├── py_converter.hpp │ ├── py_stream.hpp │ ├── pyref.hpp │ ├── signal_handler.cpp │ ├── signal_handler.hpp │ └── traits.hpp ├── cmake ├── CMakeLists.txt ├── Cpp2PyConfig.cmake.in ├── Cpp2PyConfigVersion.cmake.in ├── FindLibClang.cmake └── FindNumPy.cmake ├── cmake_uninstall.cmake.in ├── cpp2cxx ├── CMakeLists.txt ├── __init__.py ├── cpp2cxx.py └── ess.cpp ├── cpp2py.modulefile.in ├── cpp2py ├── CMakeLists.txt ├── __init__.py ├── clang_parser.py ├── compiler.py ├── config.py.in ├── cpp2desc.py ├── cpp2py_info_base.py ├── dependency_analyzer.py ├── doc.py ├── libclang_config.py.in ├── magic.py ├── mako │ ├── converters.cxx │ ├── desc.py │ ├── parameters.rst │ └── wrap.cxx ├── processed_doc.py ├── util.py └── wrap_generator.py ├── cpp2pyvars.sh.in ├── cpp2rst ├── CMakeLists.txt ├── __init__.py ├── cpp2rst.py ├── doc.py ├── example.py ├── global_vars.py ├── processed_doc.py ├── render_fnt.py ├── renderers.py └── synopsis.py ├── doc └── ipynb_magic │ ├── 1-SimpleFunction.ipynb │ ├── 2-TriqsGf.ipynb │ ├── 3-LittleClass.ipynb │ ├── 4-Parameters.ipynb │ └── 5-IsingSpinQMC.ipynb ├── requirements.txt └── test ├── inl.py └── test_onfly.py /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | AccessModifierOffset: 0 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: true 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlinesLeft: false 8 | AlignOperands: false 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: false 11 | AllowShortBlocksOnASingleLine: true 12 | AllowShortCaseLabelsOnASingleLine: true 13 | AllowShortFunctionsOnASingleLine: All 14 | AllowShortIfStatementsOnASingleLine: true 15 | AllowShortLoopsOnASingleLine: true 16 | AlwaysBreakBeforeMultilineStrings: true 17 | AlwaysBreakTemplateDeclarations: false 18 | BinPackArguments: true 19 | BinPackParameters: true 20 | BreakBeforeBinaryOperators: NonAssignment 21 | BreakBeforeBraces: Attach 22 | BreakBeforeTernaryOperators: false 23 | BreakConstructorInitializersBeforeComma: false 24 | BreakStringLiterals: false 25 | ColumnLimit: 150 26 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 27 | ConstructorInitializerIndentWidth: 3 28 | ContinuationIndentWidth: 3 29 | Cpp11BracedListStyle: true 30 | DerivePointerBinding : false 31 | IndentCaseLabels: true 32 | IndentWidth: 2 33 | Language: Cpp 34 | MaxEmptyLinesToKeep: 1 35 | NamespaceIndentation : All 36 | PointerAlignment: Right 37 | ReflowComments: false 38 | SortIncludes: false 39 | SpaceAfterControlStatementKeyword: true 40 | SpaceBeforeAssignmentOperators: true 41 | SpaceInEmptyParentheses: false 42 | SpacesInParentheses: false 43 | Standard: Cpp11 44 | TabWidth: 2 45 | UseTab: Never 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries / C extensions 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Byte-compiled / optimized / DLL files 35 | __pycache__/ 36 | *.py[cod] 37 | *$py.class 38 | 39 | # Distribution / packaging 40 | .Python 41 | build/ 42 | develop-eggs/ 43 | dist/ 44 | downloads/ 45 | eggs/ 46 | .eggs/ 47 | lib/ 48 | lib64/ 49 | parts/ 50 | sdist/ 51 | var/ 52 | wheels/ 53 | pip-wheel-metadata/ 54 | share/python-wheels/ 55 | *.egg-info/ 56 | .installed.cfg 57 | *.egg 58 | MANIFEST 59 | 60 | # PyInstaller 61 | # Usually these files are written by a python script from a template 62 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 63 | *.manifest 64 | *.spec 65 | 66 | # Installer logs 67 | pip-log.txt 68 | pip-delete-this-directory.txt 69 | 70 | # Unit test / coverage reports 71 | htmlcov/ 72 | .tox/ 73 | .nox/ 74 | .coverage 75 | .coverage.* 76 | .cache 77 | nosetests.xml 78 | coverage.xml 79 | *.cover 80 | .hypothesis/ 81 | .pytest_cache/ 82 | 83 | # Translations 84 | *.mo 85 | *.pot 86 | 87 | # Django stuff: 88 | *.log 89 | local_settings.py 90 | db.sqlite3 91 | 92 | # Flask stuff: 93 | instance/ 94 | .webassets-cache 95 | 96 | # Scrapy stuff: 97 | .scrapy 98 | 99 | # Sphinx documentation 100 | docs/_build/ 101 | 102 | # PyBuilder 103 | target/ 104 | 105 | # Jupyter Notebook 106 | .ipynb_checkpoints 107 | 108 | # IPython 109 | profile_default/ 110 | ipython_config.py 111 | 112 | # pyenv 113 | .python-version 114 | 115 | # pipenv 116 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 117 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 118 | # having no cross-platform support, pipenv may install dependencies that don’t work, or not 119 | # install all needed dependencies. 120 | #Pipfile.lock 121 | 122 | # celery beat schedule file 123 | celerybeat-schedule 124 | 125 | # SageMath parsed files 126 | *.sage.py 127 | 128 | # Environments 129 | .env 130 | .venv 131 | env/ 132 | venv/ 133 | ENV/ 134 | env.bak/ 135 | venv.bak/ 136 | 137 | # Spyder project settings 138 | .spyderproject 139 | .spyproject 140 | 141 | # Rope project settings 142 | .ropeproject 143 | 144 | # mkdocs documentation 145 | /site 146 | 147 | # mypy 148 | .mypy_cache/ 149 | .dmypy.json 150 | dmypy.json 151 | 152 | # Pyre type checker 153 | .pyre/ 154 | 155 | # IDE files 156 | .idea/ 157 | -------------------------------------------------------------------------------- /CITATIONS.bib: -------------------------------------------------------------------------------- 1 | 2 | TRIQS/Cpp2Py is a part of our scientific work and we would appreciate if projects 3 | using it will include a citation to the TRIQS paper, where the tool was first introduced. 4 | In order to help you, we provide theBibTeX reference. 5 | 6 | @article{triqs, 7 | title = "TRIQS: A toolbox for research on interacting quantum systems", 8 | journal = "Computer Physics Communications", 9 | volume = "196", 10 | number = "", 11 | pages = "398 - 415", 12 | year = "2015", 13 | note = "", 14 | issn = "0010-4655", 15 | doi = "http://dx.doi.org/10.1016/j.cpc.2015.04.023", 16 | url = "http://www.sciencedirect.com/science/article/pii/S0010465515001666", 17 | author = "Olivier Parcollet and Michel Ferrero and Thomas Ayral and Hartmut Hafermann and Igor Krivenko and Laura Messio and Priyanka Seth", 18 | keywords = "Many-body physics", 19 | keywords = "Strongly-correlated systems", 20 | keywords = "DMFT", 21 | keywords = "Monte Carlo", 22 | keywords = "ab initio calculations", 23 | keywords = "C++", 24 | keywords = "Python" 25 | } 26 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | 2 | Cpp2py Changelog : 3 | - remove the need to use variables of wrapped types by pointer 4 | e.g. when wrapping f( AWrappedType a) 5 | in C code, use a, not *a ... 6 | - Simpler, less error prone. 7 | 8 | - impl : just define a variable __x for parsing and then x is the user variable. 9 | 10 | - Eliminate py_converters.hpp files 11 | - All the information in the module .so itself. 12 | 13 | - Partial rewrite of the parsing part. 14 | I now use directly the libclang AST, 15 | suppressing the intermediate representation that used in version 1. 16 | Code is shorter, simpler in some cases, more standard. 17 | 18 | - introduce a small config.py for Cmake configured var 19 | 20 | - rm notion of view in cpp2py 21 | 22 | - Removing precall, postcall. 23 | 24 | 25 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | properties([ 2 | buildDiscarder(logRotator(numToKeepStr: '10', daysToKeepStr: '30')) 3 | ]) 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cpp2Py is the Python-C++ interfacing tool of the [TRIQS](https://triqs.github.io) project, provided here as a standalone project. 2 | 3 | Installation 4 | ============ 5 | 6 | To install Cpp2Py, follow the installation steps: 7 | 8 | ```bash 9 | git clone https://github.com/TRIQS/cpp2py.git cpp2py 10 | mkdir build && cd build 11 | cmake -DCMAKE_INSTALL_PREFIX=INSTALL_DIR ../cpp2py 12 | make && make install 13 | ``` 14 | 15 | This installs the library in `INSTALL_DIR`. 16 | In order to make Cpp2Py available in your current environment you should run 17 | 18 | ```bash 19 | source INSTALL_DIR/share/cpp2pyvars.sh 20 | ``` 21 | 22 | 23 | Example 24 | ======= 25 | 26 | Make sure that you have loaded Cpp2Py into your environment as instructed above. 27 | Created a C++ source file `mymodule.hpp` in a folder `SRC`: 28 | 29 | ```c++ 30 | ///A wonderful little class 31 | class myclass{ 32 | int a, b; 33 | 34 | public: 35 | myclass(int a_, int b_) : a(a_), b(b_) {} 36 | 37 | ///getter for member a 38 | int get_a() const { return a;} 39 | }; 40 | ``` 41 | 42 | In the same folder, create a file `CMakeLists.txt`: 43 | 44 | ```cmake 45 | cmake_minimum_required(VERSION 3.0.2) 46 | find_package(Cpp2Py REQUIRED) 47 | 48 | add_cpp2py_module(mymodule) 49 | target_compile_options(mymodule PRIVATE -std=c++17) 50 | target_include_directories(mymodule PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) 51 | ``` 52 | 53 | Then, in the `SRC` folder, issue the command 54 | 55 | ``` 56 | c++2py mymodule.hpp 57 | ``` 58 | 59 | This creates a file `mymodule_desc.py`. 60 | 61 | Exit the `SRC` folder and create a `BUILD` folder. Then, issue the following commands: 62 | 63 | ```bash 64 | cd BUILD 65 | cmake ../SRC 66 | make 67 | ``` 68 | 69 | In the `BUILD` dir, you should see a `mymodule.so` file. You can now use your c++ class in Python: 70 | 71 | ```python 72 | import mymodule 73 | A = mymodule.Myclass(4,5) 74 | print(A.get_a()) 75 | ``` 76 | 77 | By convention, c++ classes of the type `my_little_class` are converted in python classes of the type `MyLittleClass`. 78 | 79 | License 80 | =============== 81 | 82 | Before you proceed, make sure you have read the `LICENSE.txt` file. 83 | 84 | Enjoy! 85 | 86 | The TRIQS team 87 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | 2 | - doc: 3 | 4 | - make doc dir 5 | - structure : ? 6 | - ref doc with autodoc 7 | - tutorial 8 | - desc generation 9 | - from slide : desc separation 10 | - cmake macro 11 | 12 | - check that one does not return a const_view 13 | - MAGIC : test 14 | - TESTS 15 | - upgrade skeleton 16 | 17 | - gf : regular converters 18 | 19 | - pip -r pip-requirements.txt for cpp2py or TRIQS ?? 20 | 21 | - triqs_set_rpath_of_all_targets(PATH) 22 | 23 | 24 | -------------------------------------------------------------------------------- /bin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(scripts c++2py c++2rst c++2cxx) 2 | 3 | foreach(script ${scripts}) 4 | configure_file(${script}.in ${script} @ONLY) 5 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${script} DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) 6 | endforeach() 7 | -------------------------------------------------------------------------------- /bin/c++2cxx.in: -------------------------------------------------------------------------------- 1 | #!@CPP2PY_PYTHON_EXECUTABLE@ 2 | import os, sys, argparse 3 | from cpp2cxx import Cpp2Cxx 4 | import cpp2py.libclang_config as config 5 | 6 | # 7 | print("Welcome to C++2cxx") 8 | 9 | # --- Parsing the arguments of the script and options 10 | 11 | parser = argparse.ArgumentParser(description=""" 12 | Generate the C++/Python wrapper desc file from C++ header code 13 | """) 14 | 15 | parser.add_argument('filename', help = "Name of the file to parse") 16 | parser.add_argument('--outputname', '-o', help="Name of the xxx_desc.py file [default is same as the filename]", default = '') 17 | parser.add_argument('--libclang_location', help='Location of the libclang', default = config.LIBCLANG_LOCATION) 18 | parser.add_argument('--includes', '-I', action='append', help='Includes to pass to clang') 19 | parser.add_argument('--system_includes', '-isystem', action='append', help='System includes to pass to clang') 20 | parser.add_argument('--namespace', '-N', action='append', help='namespaces to document', default= []) #specify which namespaces to document, e.g. -N triqs -N applications 21 | parser.add_argument('--cxxflags', default = '', help='Options to pass to clang') 22 | parser.add_argument('--target_file_only', action='store_true', help='Disable recursion into included header files') 23 | 24 | args = parser.parse_args() 25 | 26 | # Add the environment variables 27 | cxx_env = os.getenv('CXXFLAGS').split() if os.getenv('CXXFLAGS') else [] 28 | 29 | # --------------------------------------------------------- 30 | # Create the worker. It parses the file 31 | W= Cpp2Cxx(filename = args.filename, 32 | namespaces= args.namespace, 33 | includes = args.includes or (), 34 | system_includes = args.system_includes or (), 35 | compiler_options = args.cxxflags.split(' ') + cxx_env, 36 | libclang_location = args.libclang_location, 37 | target_file_only = args.target_file_only 38 | ) 39 | 40 | filename_1 = os.path.split(args.filename)[1].split('.',1)[0] 41 | 42 | # Make the desc file 43 | W.run(output_filename = (args.outputname or filename_1) + ".cxx") 44 | -------------------------------------------------------------------------------- /bin/c++2py.in: -------------------------------------------------------------------------------- 1 | #!@CPP2PY_PYTHON_EXECUTABLE@ 2 | import os, sys, argparse, subprocess 3 | from cpp2py.cpp2desc import Cpp2Desc 4 | import cpp2py.libclang_config as config 5 | 6 | # --- Parsing the arguments of the script and options 7 | 8 | parser = argparse.ArgumentParser(description=""" 9 | Generate the C++/Python wrapper desc file from C++ header code 10 | """) 11 | 12 | parser.add_argument('filename', help = "Name of the file to parse") 13 | 14 | parser.add_argument('--regenerate', '-r', action='store_true', help="Rebuild the desc file with the shell command written at its head (ignores all other options") 15 | 16 | parser.add_argument('--converter', '-C', action='append', help='Path to the converter DIRS') 17 | 18 | parser.add_argument('--namespace', '-N', action='append', help="Specify the namespace to explore for classes and function to wrap", default= []) 19 | parser.add_argument('--only', default = '', help="Specify functions or class to be wrapped") 20 | parser.add_argument('--outputname', '-o', help="Name of the xxx_desc.py file [default is same as the filename]", default = '') 21 | parser.add_argument('--modulename', '-m', help="Name of the Python module [default ='', it will be modulename", default = '') 22 | parser.add_argument('--appname', '-a', help="Name of the Python module [default ='', it will take the name of file", default = '') 23 | parser.add_argument('--moduledoc', help="Documentation of the module", default = '') 24 | parser.add_argument('--properties', '-p', action='store_true', 25 | help="""Transforms i) every method with no arguments into read-only property 26 | ii) every method get_X into read-only property 27 | iii) every couple of methods get_X, set_X into rw property 28 | """) 29 | parser.add_argument('--members_read_only', action='store_true', help="""Makes members read only [default = True]""") 30 | parser.add_argument('--parse-all-comments', action='store_true', help="Grab all comments, including non doxygen like [default = True]") 31 | 32 | parser.add_argument('--libclang_location', help='Location of the libclang', default = config.LIBCLANG_LOCATION) 33 | parser.add_argument('--includes', '-I', action='append', help='Includes to pass to clang') 34 | parser.add_argument('--system_includes', '-isystem', action='append', help='System includes to pass to clang') 35 | parser.add_argument('--cxxflags', default = '', help='Options to pass to clang') 36 | parser.add_argument('--target_file_only', action='store_true', help='Disable recursion into included header files') 37 | parser.add_argument('--shell_command', default=None, help='The command issued to run this script') 38 | 39 | args = parser.parse_args() 40 | 41 | if args.regenerate: 42 | assert not (args.cxxflags or args.includes or args.system_includes or args.converter or args.namespace or args.only or args.properties), "-r option must be alone" 43 | f = open(args.filename) 44 | 45 | # Read second line and remove leading '# ' 46 | f.readline() 47 | shell_command = f.readline()[2:].strip() 48 | assert "c++2py" in shell_command.split(" ", 1)[0], "Invalid regeneration command in {}".format(args.filename) 49 | 50 | # Forward the initial shell_command 51 | shell_command += r" --shell_command='{}'".format(shell_command) 52 | 53 | # Send command as subprocess for proper treatment of e.g. $ENVVAR 54 | normal = subprocess.run(shell_command, 55 | stdout=subprocess.PIPE, stderr=subprocess.PIPE, 56 | shell=True, 57 | text=True) 58 | print(normal.stdout) 59 | print(normal.stderr) 60 | sys.exit() 61 | else: 62 | print("Welcome to C++2py") 63 | if args.shell_command is None: 64 | # Rebuild command that created this file 65 | f = lambda x : x if not (x.startswith('--cxxflags') or x.startswith('--only') or x.startswith('--moduledoc')) else '%s="%s"'%tuple(x.split('=', 1)) 66 | shell_command = 'c++2py ' + ' '.join(f(x) for x in sys.argv[1:]) 67 | else: 68 | shell_command = args.shell_command 69 | 70 | # A few variables 71 | args.includes = (args.includes or []) 72 | args.system_includes = (args.system_includes or []) 73 | filename_1 = os.path.split(args.filename)[1].split('.',1)[0] 74 | 75 | # Add the environment variables 76 | cxx_env = os.getenv('CXXFLAGS').split() if os.getenv('CXXFLAGS') else [] 77 | 78 | # --------------------------------------------------------- 79 | # Create the worker. It parses the file 80 | W= Cpp2Desc(filename = args.filename, 81 | converters = args.converter or (), 82 | namespaces= args.namespace, 83 | classes= args.only.split(), 84 | use_properties = args.properties, 85 | modulename = args.modulename or filename_1, 86 | appname = args.appname or args.modulename or filename_1, 87 | moduledoc = args.moduledoc, 88 | members_read_only = args.members_read_only, 89 | includes = args.includes or (), 90 | system_includes = args.system_includes or (), 91 | compiler_options = args.cxxflags.split(' ') + cxx_env, 92 | libclang_location = args.libclang_location, 93 | shell_command = shell_command, 94 | parse_all_comments = args.parse_all_comments, 95 | namespace_to_factor= (), # unused now 96 | target_file_only = args.target_file_only 97 | ) 98 | 99 | # Make the desc file 100 | W.generate_desc_file(output_filename = (args.outputname or filename_1) + "_desc.py") 101 | 102 | -------------------------------------------------------------------------------- /bin/c++2rst.in: -------------------------------------------------------------------------------- 1 | #!@CPP2PY_PYTHON_EXECUTABLE@ 2 | import os, sys, argparse 3 | from cpp2rst import Cpp2Rst 4 | import cpp2py.libclang_config as config 5 | 6 | print("Welcome to the C++ doc generator !") 7 | 8 | # --- Parsing the arguments of the script and options 9 | parser = argparse.ArgumentParser(description=""" Generate the rst doc file from C++ header code """) 10 | 11 | parser.add_argument('filename', help = "Name of the file") 12 | parser.add_argument('--output_directory', '-o', help="Where to put the files [default './']", default = './') 13 | parser.add_argument('--libclang_location', help='Location of the libclang', default = config.LIBCLANG_LOCATION) 14 | parser.add_argument('--includes', '-I', action='append', help='Includes to pass to clang') 15 | parser.add_argument('--system_includes', '-isystem', action='append', help='System includes to pass to clang') 16 | parser.add_argument('--namespace', '-N', action='append', help='namespaces to document', default= []) #specify which namespaces to document, e.g. -N triqs -N applications 17 | parser.add_argument('--parse-all-comments', action='store_true', help="Grab all comments, including non doxygen like [FALSE]") 18 | parser.add_argument('--cxxflags', default = '', help='Options to pass to clang') 19 | parser.add_argument('--target_file_only', action='store_true', help='Disable recursion into included header files') 20 | 21 | args = parser.parse_args() 22 | args.includes = (args.includes or []) 23 | args.system_includes = (args.system_includes or []) 24 | 25 | # Add the environment variables 26 | cxx_env = os.getenv('CXXFLAGS').split() if os.getenv('CXXFLAGS') else [] 27 | 28 | # FIXME cmake puts some '\ ' in the argument ??? 29 | args.cxxflags = args.cxxflags.replace('\\ ',' ') 30 | 31 | # --------------------------------------------------------- 32 | # Create the worker. It parses the file 33 | W = Cpp2Rst(filename = args.filename, 34 | namespaces= args.namespace, 35 | includes = args.includes or (), 36 | system_includes = args.system_includes or (), 37 | compiler_options = args.cxxflags.split(' ') + cxx_env, 38 | parse_all_comments = args.parse_all_comments, 39 | libclang_location = args.libclang_location, 40 | target_file_only = args.target_file_only 41 | ) 42 | 43 | # profile for testing 44 | W.run(args.output_directory) 45 | -------------------------------------------------------------------------------- /bin/cp_rs.py: -------------------------------------------------------------------------------- 1 | # Acts as cp -rs on Linux 2 | # Workaround for the absence of this option on OS X 3 | # Usage cp_rs SRC DEST 4 | # It makes in DEST the subdirectory structure of SRC and links all files with symbolic links. 5 | 6 | import os, sys 7 | 8 | rootDir, destDir = sys.argv[1:3] 9 | extension_kept = sys.argv[3].split() 10 | 11 | def ensuredirs(d): 12 | if not os.path.exists(d): 13 | os.makedirs(d) 14 | 15 | ensuredirs(destDir) 16 | os.chdir(destDir) 17 | 18 | for dirName, ignored_subdirList, fileList in os.walk(rootDir, followlinks = False): 19 | d = os.path.relpath(dirName, rootDir) 20 | ensuredirs(d) 21 | cwd = os.getcwd() 22 | os.chdir(d) 23 | for fname in fileList: 24 | if fname.startswith('CMakeLists'): continue 25 | ext = os.path.splitext(fname)[1] 26 | if ext[1:] not in extension_kept : continue 27 | if not os.path.exists(fname): 28 | os.symlink(os.path.join(rootDir, d, fname), fname) 29 | os.chdir(cwd) 30 | 31 | -------------------------------------------------------------------------------- /c++/cpp2py/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(cpp2py signal_handler.cpp exceptions.cpp numpy_proxy.cpp misc.cpp) 2 | add_library(cpp2py::cpp2py ALIAS cpp2py) 3 | 4 | target_compile_options(cpp2py PRIVATE -std=c++17 -fPIC) 5 | target_include_directories(cpp2py 6 | PUBLIC 7 | $ 8 | $ 9 | ) 10 | 11 | # Install the library in lib and export the cpp2py target 12 | install(TARGETS cpp2py EXPORT Cpp2PyTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}) 13 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION include FILES_MATCHING PATTERN "*.hpp" PATTERN "*.h" PATTERN "*.hxx") 14 | 15 | # --- Python --- 16 | 17 | target_link_libraries(cpp2py PUBLIC python_and_numpy) 18 | 19 | # --------------- 20 | 21 | # Install the exported targets 22 | install(EXPORT Cpp2PyTargets NAMESPACE cpp2py:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Cpp2Py) 23 | -------------------------------------------------------------------------------- /c++/cpp2py/converters/basic_types.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include 22 | #include "../py_converter.hpp" 23 | #include "./complex.hpp" 24 | 25 | namespace cpp2py { 26 | 27 | // PyObject * 28 | template <> struct py_converter { 29 | static PyObject *c2py(PyObject *ob) { return ob; } 30 | static PyObject *py2c(PyObject *ob) { return ob; } 31 | static bool is_convertible(PyObject *ob, bool raise_exception) { return true; } 32 | }; 33 | 34 | // --- bool 35 | template <> struct py_converter { 36 | static PyObject *c2py(bool b) { 37 | if (b) 38 | Py_RETURN_TRUE; 39 | else 40 | Py_RETURN_FALSE; 41 | } 42 | static bool py2c(PyObject *ob) { return ob == Py_True; } 43 | static bool is_convertible(PyObject *ob, bool raise_exception) { 44 | if (PyBool_Check(ob)) return true; 45 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to bool"s).c_str()); } 46 | return false; 47 | } 48 | }; 49 | 50 | // --- long 51 | 52 | namespace details { 53 | template struct py_converter_impl { 54 | static PyObject *c2py(I i) { return PyLong_FromLong(long(i)); } 55 | static I py2c(PyObject *ob) { 56 | if (PyLong_Check(ob)) { return I(PyLong_AsLong(ob)); } 57 | // Convert NPY Scalar Type to Builtin Type 58 | pyref py_builtin = PyObject_CallMethod(ob, "item", NULL); 59 | return I(PyLong_AsLong(py_builtin)); 60 | } 61 | static bool is_convertible(PyObject *ob, bool raise_exception) { 62 | // first check if ob is a python long 63 | if (PyLong_Check(ob)) { 64 | // now check that the int from python is within the limits of the C++ type 65 | if (PyLong_AsLong(ob) < std::numeric_limits::min() or PyLong_AsLong(ob) > std::numeric_limits::max()) { 66 | if (raise_exception) { 67 | PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to integer type, out of bounds"s).c_str()); 68 | } 69 | // write also to std err to show the conversion error 70 | std::cerr << ("Cannot convert "s + to_string(ob) + " to integer type, out of bounds"s).c_str() << std::endl; 71 | return false; 72 | } else 73 | return true; 74 | } 75 | if (PyArray_CheckScalar(ob)) { 76 | pyref py_arr = PyArray_FromScalar(ob, NULL); 77 | if (PyArray_ISINTEGER((PyArrayObject *)(PyObject *)py_arr)) return true; 78 | } 79 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to integer type"s).c_str()); } 80 | return false; 81 | } 82 | }; 83 | } // namespace details 84 | 85 | template <> struct py_converter : details::py_converter_impl {}; 86 | template <> struct py_converter : details::py_converter_impl {}; 87 | template <> struct py_converter : details::py_converter_impl {}; 88 | template <> struct py_converter : details::py_converter_impl {}; 89 | template <> struct py_converter : details::py_converter_impl {}; 90 | 91 | // --- byte 92 | 93 | template <> struct py_converter { 94 | static PyObject *c2py(std::byte b) { return PyBytes_FromStringAndSize(reinterpret_cast(&b), 1); } 95 | static std::byte py2c(PyObject *ob) { return static_cast(PyBytes_AsString(ob)[0]); } 96 | static bool is_convertible(PyObject *ob, bool raise_exception) { 97 | if (PyBytes_Check(ob) and PyBytes_Size(ob) == 1) return true; 98 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to byte"s).c_str()); } 99 | return false; 100 | } 101 | }; 102 | 103 | // --- double 104 | 105 | template <> struct py_converter { 106 | static PyObject *c2py(double x) { return PyFloat_FromDouble(x); } 107 | static double py2c(PyObject *ob) { 108 | if (PyFloat_Check(ob) || PyLong_Check(ob)) { return PyFloat_AsDouble(ob); } 109 | // Convert NPY Scalar Type to Builtin Type 110 | pyref py_builtin = PyObject_CallMethod(ob, "item", NULL); 111 | return PyFloat_AsDouble(py_builtin); 112 | } 113 | static bool is_convertible(PyObject *ob, bool raise_exception) { 114 | if (PyFloat_Check(ob) || PyLong_Check(ob)) return true; 115 | if (PyArray_CheckScalar(ob)) { 116 | pyref py_arr = PyArray_FromScalar(ob, NULL); 117 | if (PyArray_ISINTEGER((PyArrayObject *)(PyObject *)py_arr) or PyArray_ISFLOAT((PyArrayObject *)(PyObject *)py_arr)) return true; 118 | } 119 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to double"s).c_str()); } 120 | return false; 121 | } 122 | }; 123 | 124 | } // namespace cpp2py 125 | -------------------------------------------------------------------------------- /c++/cpp2py/converters/complex.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include 22 | #include "../py_converter.hpp" 23 | 24 | namespace cpp2py { 25 | 26 | // --- complex 27 | 28 | template <> struct py_converter> { 29 | static PyObject *c2py(std::complex x) { return PyComplex_FromDoubles(x.real(), x.imag()); } 30 | static std::complex py2c(PyObject *ob) { 31 | if (PyArray_CheckScalar(ob)) { 32 | // Convert NPY Scalar Type to Builtin Type 33 | pyref py_builtin = PyObject_CallMethod(ob, "item", NULL); 34 | if (PyComplex_Check(py_builtin)) { 35 | auto r = PyComplex_AsCComplex(py_builtin); 36 | return {r.real, r.imag}; 37 | } else { 38 | return PyFloat_AsDouble(py_builtin); 39 | } 40 | } 41 | 42 | if (PyComplex_Check(ob)) { 43 | auto r = PyComplex_AsCComplex(ob); 44 | return {r.real, r.imag}; 45 | } 46 | return PyFloat_AsDouble(ob); 47 | } 48 | static bool is_convertible(PyObject *ob, bool raise_exception) { 49 | if (PyComplex_Check(ob) || PyFloat_Check(ob) || PyLong_Check(ob)) return true; 50 | if (PyArray_CheckScalar(ob)) { 51 | pyref py_arr = PyArray_FromScalar(ob, NULL); 52 | if (PyArray_ISINTEGER((PyArrayObject *)(PyObject *)py_arr) or PyArray_ISFLOAT((PyArrayObject *)(PyObject *)py_arr) 53 | or PyArray_ISCOMPLEX((PyArrayObject *)(PyObject *)py_arr)) 54 | return true; 55 | } 56 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to complex"s).c_str()); } 57 | return false; 58 | } 59 | }; 60 | 61 | } // namespace cpp2py 62 | -------------------------------------------------------------------------------- /c++/cpp2py/converters/map.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include "../traits.hpp" 22 | #include "../py_converter.hpp" 23 | 24 | namespace cpp2py { 25 | 26 | template struct py_converter> { 27 | 28 | template static PyObject *c2py(M &&m) { 29 | static_assert(is_instantiation_of_v>, "Logic Error"); 30 | 31 | PyObject *d = PyDict_New(); 32 | for (auto &[key, val] : m) { 33 | pyref k, v; 34 | if constexpr (std::is_reference_v) { 35 | k = convert_to_python(key); 36 | v = convert_to_python(val); 37 | } else { // Map passed as rvalue 38 | k = convert_to_python(std::move(key)); 39 | v = convert_to_python(std::move(val)); 40 | } 41 | 42 | // if the K is a list, we transform into a tuple 43 | if (PyList_Check(k)) k = PyList_AsTuple(k); 44 | 45 | if (k.is_null() or v.is_null() or (PyDict_SetItem(d, k, v) == -1)) { 46 | Py_DECREF(d); 47 | return NULL; 48 | } // error 49 | } 50 | return d; 51 | } 52 | 53 | // ---------------------------------------------- 54 | 55 | static bool is_convertible(PyObject *ob, bool raise_exception) { 56 | if (!PyDict_Check(ob)) goto _false; 57 | { 58 | pyref keys = PyDict_Keys(ob); 59 | pyref values = PyDict_Values(ob); 60 | int len = PyDict_Size(ob); 61 | for (int i = 0; i < len; i++) { 62 | if (!py_converter::is_convertible(PyList_GET_ITEM((PyObject *)keys, i), raise_exception)) goto _false; //borrowed ref 63 | if (!py_converter::is_convertible(PyList_GET_ITEM((PyObject *)values, i), raise_exception)) goto _false; //borrowed ref 64 | } 65 | return true; 66 | } 67 | _false: 68 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::map"s).c_str()); } 69 | return false; 70 | } 71 | 72 | // ---------------------------------------------- 73 | 74 | static std::map py2c(PyObject *ob) { 75 | pyref keys = PyDict_Keys(ob); 76 | pyref values = PyDict_Values(ob); 77 | std::map res; 78 | int len = PyDict_Size(ob); 79 | for (int i = 0; i < len; i++) 80 | res.emplace(py_converter::py2c(PyList_GET_ITEM((PyObject *)keys, i)), //borrowed ref 81 | py_converter::py2c(PyList_GET_ITEM((PyObject *)values, i))); //borrowed ref 82 | return res; 83 | } 84 | }; 85 | } // namespace cpp2py 86 | -------------------------------------------------------------------------------- /c++/cpp2py/converters/optional.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include "../py_converter.hpp" 21 | #include "../traits.hpp" 22 | 23 | namespace cpp2py { 24 | 25 | template struct py_converter> { 26 | 27 | using conv = py_converter>; 28 | 29 | template static PyObject *c2py(O &&op) { 30 | static_assert(is_instantiation_of_v>, "Logic Error"); 31 | if (!bool(op)) Py_RETURN_NONE; 32 | return conv::c2py(*(std::forward(op))); 33 | } 34 | 35 | static bool is_convertible(PyObject *ob, bool raise_exception) { return ((ob == Py_None) or conv::is_convertible(ob, raise_exception)); } 36 | 37 | static std::optional py2c(PyObject *ob) { 38 | if (ob == Py_None) return {}; 39 | return std::optional{conv::py2c(ob)}; 40 | } 41 | }; 42 | } // namespace cpp2py 43 | -------------------------------------------------------------------------------- /c++/cpp2py/converters/pair.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include "../py_converter.hpp" 21 | #include "../traits.hpp" 22 | 23 | namespace cpp2py { 24 | 25 | template struct py_converter> { 26 | 27 | template static PyObject *c2py(P &&p) { 28 | static_assert(is_instantiation_of_v>, "Logic error"); 29 | pyref x1 = convert_to_python(std::get<0>(std::forward

(p))); 30 | pyref x2 = convert_to_python(std::get<1>(std::forward

(p))); 31 | 32 | if (x1.is_null() or x2.is_null()) return NULL; 33 | return PyTuple_Pack(2, (PyObject *)x1, (PyObject *)x2); 34 | } 35 | 36 | static bool is_convertible(PyObject *ob, bool raise_exception) { 37 | if (!PySequence_Check(ob)) goto _false; 38 | { 39 | pyref seq = PySequence_Fast(ob, "expected a sequence"); 40 | if (!py_converter::is_convertible(PySequence_Fast_GET_ITEM((PyObject *)seq, 0), raise_exception)) goto _false; // borrowed ref 41 | if (!py_converter::is_convertible(PySequence_Fast_GET_ITEM((PyObject *)seq, 1), raise_exception)) goto _false; // borrowed ref 42 | return true; 43 | } 44 | _false: 45 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::pair"s).c_str()); } 46 | return false; 47 | } 48 | 49 | static std::pair py2c(PyObject *ob) { 50 | pyref seq = PySequence_Fast(ob, "expected a sequence"); 51 | return std::make_pair(py_converter::py2c(PySequence_Fast_GET_ITEM((PyObject *)seq, 0)), 52 | py_converter::py2c(PySequence_Fast_GET_ITEM((PyObject *)seq, 1))); 53 | } 54 | }; 55 | } // namespace cpp2py 56 | -------------------------------------------------------------------------------- /c++/cpp2py/converters/set.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include "../py_converter.hpp" 22 | #include "../traits.hpp" 23 | 24 | namespace cpp2py { 25 | 26 | template struct py_converter> { 27 | 28 | template static PyObject *c2py(S &&s) { 29 | static_assert(is_instantiation_of_v>, "Logic error"); 30 | PyObject *set = PySet_New(NULL); 31 | for (auto &x : s) { 32 | pyref y; 33 | if constexpr (std::is_reference_v) { 34 | y = convert_to_python(x); 35 | } else { // s passed as rvalue 36 | y = convert_to_python(std::move(x)); 37 | } 38 | if (y.is_null() or (PySet_Add(set, y) == -1)) { 39 | Py_DECREF(set); 40 | return NULL; 41 | } // error 42 | } 43 | return set; 44 | } 45 | 46 | static bool is_convertible(PyObject *ob, bool raise_exception) { 47 | if (!PySet_Check(ob)) goto _false; 48 | { 49 | pyref keys_it = PyObject_GetIter(ob); 50 | pyref key; 51 | while (bool(key = PyIter_Next(keys_it))) { 52 | if (!py_converter::is_convertible(key, raise_exception)) goto _false; //borrowed ref 53 | } 54 | return true; 55 | } 56 | _false: 57 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::set"s).c_str()); } 58 | return false; 59 | } 60 | 61 | static std::set py2c(PyObject *ob) { 62 | std::set res; 63 | pyref keys_it = PyObject_GetIter(ob); 64 | pyref key; 65 | while (bool(key = PyIter_Next(keys_it))) { 66 | res.insert(py_converter::py2c(key)); //borrowed ref 67 | } 68 | return res; 69 | } 70 | }; 71 | } // namespace cpp2py 72 | -------------------------------------------------------------------------------- /c++/cpp2py/converters/span.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2018 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2018-2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include 22 | #include "../py_converter.hpp" 23 | 24 | namespace cpp2py { 25 | 26 | template <> struct py_converter> { 27 | // -------------------------------------- 28 | 29 | static bool is_convertible(PyObject *ob, bool raise_exception) { 30 | bool is_bytes_ob = PyBytes_Check(ob); 31 | if (raise_exception and not is_bytes_ob) { 32 | PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::span as it is not a python bytes object"s).c_str()); 33 | } 34 | return is_bytes_ob; 35 | } 36 | 37 | // -------------------------------------- 38 | 39 | static std::span py2c(PyObject *ob) { 40 | auto size = PyBytes_Size(ob); 41 | auto *buffer = reinterpret_cast(PyBytes_AsString(ob)); 42 | return {buffer, size_t(size)}; 43 | } 44 | }; 45 | 46 | } // namespace cpp2py 47 | -------------------------------------------------------------------------------- /c++/cpp2py/converters/std_array.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2018 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2018-2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include "../py_converter.hpp" 22 | 23 | namespace cpp2py { 24 | 25 | template struct py_converter> { 26 | // -------------------------------------- 27 | 28 | static PyObject *c2py(std::array const &v) { 29 | PyObject *list = PyList_New(0); 30 | for (auto const &x : v) { 31 | pyref y = py_converter::c2py(x); 32 | if (y.is_null() or (PyList_Append(list, y) == -1)) { 33 | Py_DECREF(list); 34 | return NULL; 35 | } // error 36 | } 37 | return list; 38 | } 39 | 40 | // -------------------------------------- 41 | 42 | static bool is_convertible(PyObject *ob, bool raise_exception) { 43 | if (!PySequence_Check(ob)) goto _false; 44 | { 45 | pyref seq = PySequence_Fast(ob, "expected a sequence"); 46 | int len = PySequence_Size(ob); 47 | if (len != R) { 48 | if (raise_exception) { 49 | auto s = std::string{"Convertion to std::array failed : the length of the sequence ( = "} + std::to_string(len) 50 | + " does not match R = " + std::to_string(R); 51 | PyErr_SetString(PyExc_TypeError, s.c_str()); 52 | } 53 | return false; 54 | } 55 | for (int i = 0; i < len; i++) 56 | if (!py_converter::is_convertible(PySequence_Fast_GET_ITEM((PyObject *)seq, i), raise_exception)) goto _false; // borrowed ref 57 | 58 | return true; 59 | } 60 | _false: 61 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::array"s).c_str()); } 62 | return false; 63 | } 64 | 65 | // -------------------------------------- 66 | 67 | static std::array py2c(PyObject *ob) { 68 | pyref seq = PySequence_Fast(ob, "expected a sequence"); 69 | std::array res; 70 | for (int i = 0; i < R; i++) res[i] = py_converter::py2c(PySequence_Fast_GET_ITEM((PyObject *)seq, i)); // borrowed ref 71 | return res; 72 | } 73 | }; 74 | 75 | } // namespace cpp2py 76 | -------------------------------------------------------------------------------- /c++/cpp2py/converters/string.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include "../py_converter.hpp" 22 | 23 | namespace cpp2py { 24 | 25 | template <> struct py_converter { 26 | 27 | static PyObject *c2py(std::string const &x) { return PyUnicode_FromString(x.c_str()); } 28 | 29 | static std::string py2c(PyObject *ob) { return PyUnicode_AsUTF8(ob); } 30 | 31 | static bool is_convertible(PyObject *ob, bool raise_exception) { 32 | if (PyUnicode_Check(ob) or PyUnicode_Check(ob)) return true; 33 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to string"s).c_str()); } 34 | return false; 35 | } 36 | }; 37 | 38 | template <> struct py_converter { 39 | 40 | static PyObject *c2py(char c) { return PyUnicode_FromString(&c); } 41 | 42 | static char py2c(PyObject *ob) { return PyUnicode_AsUTF8(ob)[0]; } 43 | 44 | static bool is_convertible(PyObject *ob, bool raise_exception) { 45 | if (PyUnicode_Check(ob) and PyUnicode_GET_LENGTH(ob) == 1) return true; 46 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to char"s).c_str()); } 47 | return false; 48 | } 49 | }; 50 | 51 | template <> struct py_converter { 52 | 53 | static PyObject *c2py(unsigned char c) { return PyBytes_FromStringAndSize(reinterpret_cast(&c), 1); } 54 | 55 | static unsigned char py2c(PyObject *ob) { return static_cast(PyBytes_AsString(ob)[0]); } 56 | 57 | static bool is_convertible(PyObject *ob, bool raise_exception) { 58 | if (PyBytes_Check(ob) and PyBytes_Size(ob) == 1) return true; 59 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to unsigned char"s).c_str()); } 60 | return false; 61 | } 62 | }; 63 | 64 | template <> struct py_converter { 65 | 66 | static PyObject *c2py(const char *x) { return PyUnicode_FromString(x); } 67 | 68 | static const char *py2c(PyObject *ob) { return PyUnicode_AsUTF8(ob); } 69 | 70 | static bool is_convertible(PyObject *ob, bool raise_exception) { 71 | if (PyUnicode_Check(ob)) return true; 72 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to string"s).c_str()); } 73 | return false; 74 | } 75 | }; 76 | 77 | } // namespace cpp2py 78 | -------------------------------------------------------------------------------- /c++/cpp2py/converters/tuple.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include 22 | #include "../py_converter.hpp" 23 | #include "../traits.hpp" 24 | 25 | namespace cpp2py { 26 | 27 | template struct py_converter> { 28 | 29 | private: 30 | using tuple_t = std::tuple; 31 | 32 | // c2py implementation 33 | template static PyObject *c2py_impl(T &&t, std::index_sequence) { 34 | auto objs = std::array{convert_to_python(std::get(std::forward(t)))...}; 35 | bool one_is_null = std::any_of(std::begin(objs), std::end(objs), [](PyObject *a) { return a == NULL; }); 36 | if (one_is_null) return NULL; 37 | return PyTuple_Pack(sizeof...(Types), (PyObject *)(objs[Is])...); 38 | } 39 | 40 | public: 41 | template static PyObject *c2py(T &&t) { 42 | static_assert(is_instantiation_of_v>, "Logic Error"); 43 | return c2py_impl(std::forward(t), std::make_index_sequence()); 44 | } 45 | 46 | // ----------------------------------------- 47 | 48 | private: 49 | // Helper function needed due to clang v6 and v7 parameter pack issue 50 | static auto py_seq_fast_get_item(PyObject *seq, Py_ssize_t i) { return PySequence_Fast_GET_ITEM(seq, i); } 51 | 52 | // is_convertible implementation 53 | template static bool is_convertible_impl(PyObject *seq, bool raise_exception, std::index_sequence) { 54 | return (py_converter>::is_convertible(py_seq_fast_get_item(seq, Is), raise_exception) and ...); 55 | } 56 | 57 | public: 58 | static bool is_convertible(PyObject *ob, bool raise_exception) { 59 | if (not PySequence_Check(ob)) { 60 | if (raise_exception) { 61 | PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::tuple as it is not a sequence"s).c_str()); 62 | } 63 | return false; 64 | } 65 | pyref seq = PySequence_Fast(ob, "expected a sequence"); 66 | // Sizes must match! Could we relax this condition to '<'? 67 | if (PySequence_Fast_GET_SIZE((PyObject *)seq) != std::tuple_size::value) { 68 | if (raise_exception) { 69 | PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::tuple due to improper length"s).c_str()); 70 | } 71 | return false; 72 | } 73 | return is_convertible_impl((PyObject *)seq, raise_exception, std::make_index_sequence()); 74 | } 75 | 76 | // ----------------------------------------- 77 | 78 | private: 79 | template static auto py2c_impl(PyObject *seq, std::index_sequence) { 80 | return std::make_tuple(py_converter>::py2c(py_seq_fast_get_item(seq, Is))...); 81 | } 82 | 83 | public: 84 | static tuple_t py2c(PyObject *ob) { 85 | pyref seq = PySequence_Fast(ob, "expected a sequence"); 86 | return py2c_impl((PyObject *)seq, std::make_index_sequence()); 87 | } 88 | }; 89 | } // namespace cpp2py 90 | -------------------------------------------------------------------------------- /c++/cpp2py/converters/variant.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include 22 | #include "../py_converter.hpp" 23 | #include "../exceptions.hpp" 24 | #include "../traits.hpp" 25 | 26 | namespace cpp2py { 27 | 28 | // std::variant converter 29 | // converts in the first possible type 30 | template struct py_converter> { 31 | 32 | private: 33 | // using variant_t = std::variant; 34 | template using types_t = std::tuple_element_t>; 35 | //constexpr static int n_types = sizeof...(T); 36 | 37 | // c2py_visitor 38 | //struct c2py_visitor { 39 | //template PyObject *operator()(T const &x) { return py_converter::c2py(x); } 40 | //}; 41 | 42 | // is_convertible_impl 43 | /* template static std::enable_if_t<(N < n_types), bool> is_convertible_impl(PyObject *ob) {*/ 44 | //return py_converter>::is_convertible(ob, false) || is_convertible_impl(ob); 45 | //} 46 | //template static std::enable_if_t is_convertible_impl(PyObject *ob) { return false; } 47 | 48 | // py2c_impl 49 | //template static std::enable_if_t<(N < n_types), variant_t> py2c_impl(PyObject *ob) { 50 | //if (py_converter>::is_convertible(ob, false)) 51 | //return py_converter>::py2c(ob); 52 | //else 53 | //return py2c_impl(ob); 54 | //} 55 | //template static std::enable_if_t py2c_impl(PyObject *ob) { 56 | //CPP2PY_RUNTIME_ERROR << "Internal error: py2c called for a Python object incompatible with std::variant"; 57 | //} 58 | 59 | template static std::variant py2c_impl(PyObject *ob) { 60 | using conv = py_converter>>; 61 | if (conv::is_convertible(ob, false)) return conv::py2c(ob); 62 | if constexpr (N < sizeof...(T) - 1) 63 | return py2c_impl(ob); 64 | else 65 | CPP2PY_RUNTIME_ERROR << "Internal error: py2c called for a Python object incompatible with std::variant"; 66 | } 67 | 68 | struct _visitor { 69 | template PyObject *operator()(U &&x) { return convert_to_python(std::forward(x)); } 70 | }; 71 | 72 | public: 73 | template static PyObject *c2py(V &&v) { 74 | static_assert(is_instantiation_of_v>); 75 | return visit(_visitor{}, std::forward(v)); 76 | } 77 | 78 | static bool is_convertible(PyObject *ob, bool raise_exception) { 79 | if ((... or py_converter>::is_convertible(ob, false))) return true; 80 | if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::variant"s).c_str()); } 81 | return false; 82 | } 83 | 84 | static std::variant py2c(PyObject *ob) { return py2c_impl<0>(ob); } 85 | }; 86 | 87 | } // namespace cpp2py 88 | -------------------------------------------------------------------------------- /c++/cpp2py/converters/vector.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "../py_converter.hpp" 25 | #include "../traits.hpp" 26 | #include "../macros.hpp" 27 | #include "../numpy_proxy.hpp" 28 | 29 | namespace cpp2py { 30 | 31 | // Convert vector to numpy_proxy, WARNING: Deep Copy 32 | template numpy_proxy make_numpy_proxy_from_vector(V &&v) { 33 | static_assert(is_instantiation_of_v>, "Logic error"); 34 | using value_type = typename std::remove_reference_t::value_type; 35 | 36 | auto *vec_heap = new std::vector{std::forward(v)}; 37 | auto delete_pycapsule = [](PyObject *capsule) { 38 | auto *ptr = static_cast *>(PyCapsule_GetPointer(capsule, "guard")); 39 | delete ptr; 40 | }; 41 | PyObject *capsule = PyCapsule_New(vec_heap, "guard", delete_pycapsule); 42 | 43 | return {1, // rank 44 | npy_type, 45 | (void *)vec_heap->data(), 46 | std::is_const_v, 47 | std::vector{long(vec_heap->size())}, // extents 48 | std::vector{sizeof(value_type)}, // strides 49 | capsule}; 50 | } 51 | 52 | // Make a new vector from numpy view 53 | template std::vector make_vector_from_numpy_proxy(numpy_proxy const &p) { 54 | EXPECTS(p.extents.size() == 1); 55 | EXPECTS(p.strides[0] % sizeof(T) == 0); 56 | 57 | long size = p.extents[0]; 58 | long step = p.strides[0] / sizeof(T); 59 | 60 | std::vector v(size); 61 | 62 | T *data = static_cast(p.data); 63 | for (long i = 0; i < size; ++i) v[i] = *(data + i * step); 64 | 65 | return v; 66 | } 67 | 68 | // --- Special Case: vector <-> PyBytes 69 | 70 | template <> struct py_converter> { 71 | 72 | static PyObject *c2py(std::vector const &v) { 73 | auto *char_ptr = reinterpret_cast(v.data()); 74 | return PyBytes_FromStringAndSize(char_ptr, v.size()); 75 | } 76 | 77 | // -------------------------------------- 78 | 79 | static bool is_convertible(PyObject *ob, bool raise_exception) { 80 | bool is_bytes_ob = PyBytes_Check(ob); 81 | if (raise_exception and not is_bytes_ob) { 82 | PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::vector as it is not a python bytes object"s).c_str()); 83 | } 84 | return is_bytes_ob; 85 | } 86 | 87 | // -------------------------------------- 88 | 89 | static std::vector py2c(PyObject *ob) { 90 | auto size = PyBytes_Size(ob); 91 | auto *buffer = reinterpret_cast(PyBytes_AsString(ob)); 92 | return {buffer, buffer + size}; 93 | } 94 | }; 95 | 96 | // -------------------------------------- 97 | 98 | template struct py_converter> { 99 | 100 | template static PyObject *c2py(V &&v) { 101 | static_assert(is_instantiation_of_v>, "Logic error"); 102 | using value_type = typename std::remove_reference_t::value_type; 103 | 104 | if constexpr (has_npy_type) { 105 | return make_numpy_proxy_from_vector(std::forward(v)).to_python(); 106 | } else { // Convert to Python List 107 | PyObject *list = PyList_New(0); 108 | for (auto &x : v) { 109 | pyref y; 110 | if constexpr (std::is_reference_v) { 111 | y = py_converter::c2py(x); 112 | } else { // Vector passed as rvalue 113 | y = py_converter::c2py(std::move(x)); 114 | } 115 | if (y.is_null() or (PyList_Append(list, y) == -1)) { 116 | Py_DECREF(list); 117 | return NULL; 118 | } // error 119 | } 120 | return list; 121 | } 122 | } 123 | 124 | // -------------------------------------- 125 | 126 | static bool is_convertible(PyObject *ob, bool raise_exception) { 127 | // Special case: 1-d ndarray of builtin type 128 | if (PyArray_Check(ob)) { 129 | PyArrayObject *arr = (PyArrayObject *)(ob); 130 | #ifdef PYTHON_NUMPY_VERSION_LT_17 131 | int rank = arr->nd; 132 | #else 133 | int rank = PyArray_NDIM(arr); 134 | #endif 135 | if (PyArray_TYPE(arr) == npy_type and rank == 1) return true; 136 | } 137 | 138 | if (!PySequence_Check(ob)) { 139 | if (raise_exception) { 140 | PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::vector as it is not a sequence"s).c_str()); 141 | } 142 | return false; 143 | } 144 | 145 | pyref seq = PySequence_Fast(ob, "expected a sequence"); 146 | int len = PySequence_Size(ob); 147 | for (int i = 0; i < len; i++) { 148 | if (!py_converter::is_convertible(PySequence_Fast_GET_ITEM((PyObject *)seq, i), raise_exception)) { // borrowed ref 149 | return false; 150 | } 151 | } 152 | return true; 153 | } 154 | 155 | // -------------------------------------- 156 | 157 | static std::vector py2c(PyObject *ob) { 158 | // Special case: 1-d ndarray of builtin type 159 | if (PyArray_Check(ob)) { 160 | PyArrayObject *arr = (PyArrayObject *)(ob); 161 | #ifdef PYTHON_NUMPY_VERSION_LT_17 162 | int rank = arr->nd; 163 | #else 164 | int rank = PyArray_NDIM(arr); 165 | #endif 166 | if (rank == 1) return make_vector_from_numpy_proxy(make_numpy_proxy(ob)); 167 | } 168 | 169 | ASSERT(PySequence_Check(ob)); 170 | std::vector res; 171 | pyref seq = PySequence_Fast(ob, "expected a sequence"); 172 | int len = PySequence_Size(ob); 173 | for (int i = 0; i < len; i++) res.push_back(py_converter>::py2c(PySequence_Fast_GET_ITEM((PyObject *)seq, i))); //borrowed ref 174 | return res; 175 | } 176 | }; 177 | 178 | } // namespace cpp2py 179 | -------------------------------------------------------------------------------- /c++/cpp2py/cpp2py.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017-2018 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2018-2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | 21 | #define CPP2PY_IGNORE __attribute__((annotate("ignore_in_python"))) 22 | #define CPP2PY_ARG_AS_DICT __attribute__((annotate("use_parameter_class"))) 23 | 24 | #include 25 | 26 | #include "./cpp2py/signal_handler.hpp" 27 | #include "./cpp2py/exceptions.hpp" 28 | #include "./cpp2py/pyref.hpp" 29 | #include "./cpp2py/py_converter.hpp" 30 | #include "./cpp2py/misc.hpp" 31 | #include "./cpp2py/converters/basic_types.hpp" 32 | -------------------------------------------------------------------------------- /c++/cpp2py/exceptions.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2019-2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #include 20 | #include "cpp2py/exceptions.hpp" 21 | 22 | #ifndef CPP2PY_TRACE_MAX_FRAMES 23 | #define CPP2PY_TRACE_MAX_FRAMES 50 24 | #endif 25 | 26 | #ifdef __GNUC__ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | namespace cpp2py { 42 | 43 | std::string demangle(const char *name) { 44 | std::stringstream fs; 45 | int status; 46 | char *demangled = abi::__cxa_demangle(name, NULL, NULL, &status); 47 | if (!status) { 48 | std::string res(demangled); 49 | fs << res; 50 | free(demangled); 51 | } else 52 | fs << name; 53 | return fs.str(); 54 | } 55 | 56 | std::string demangle(std::string const &name) { return demangle(name.c_str()); } 57 | 58 | std::string stack_trace() { 59 | std::ostringstream buffer; 60 | void *stack[CPP2PY_TRACE_MAX_FRAMES + 1]; 61 | std::size_t depth = backtrace(stack, CPP2PY_TRACE_MAX_FRAMES + 1); 62 | if (!depth) 63 | buffer << " empty " << std::endl; 64 | else { 65 | char **symbols = backtrace_symbols(stack, depth); 66 | for (std::size_t i = 1; i < depth; ++i) { 67 | std::string symbol = symbols[i]; 68 | std::istringstream iss(symbol); 69 | std::vector strs{std::istream_iterator{iss}, std::istream_iterator{}}; 70 | for (auto const &x : strs) buffer << " " << demangle(x); 71 | buffer << std::endl; 72 | } 73 | free(symbols); 74 | } 75 | return buffer.str(); 76 | } 77 | 78 | } // namespace cpp2py 79 | #else 80 | 81 | namespace cpp2py { 82 | std::string stack_trace() { return std::string("stacktrace only available in gcc"); } 83 | } // namespace cpp2py 84 | 85 | #endif 86 | 87 | namespace cpp2py { 88 | 89 | bool exception::show_cpp_trace = false; 90 | 91 | exception::exception() : std::exception() { _trace = stack_trace(); } 92 | 93 | const char *exception::what() const noexcept { 94 | std::stringstream out; 95 | out << acc.str() << "\n.. Error occurred "; 96 | int is_init; 97 | //MPI_Initialized(&is_init); 98 | //if (is_init) { 99 | // int rank; 100 | // MPI_Comm_rank(MPI_COMM_WORLD, &rank); 101 | // out << " on node " << rank; 102 | // } 103 | out << "\n"; 104 | if (show_cpp_trace) out << ".. C++ trace is : " << _trace << "\n"; 105 | _what = out.str(); 106 | return _what.c_str(); 107 | } 108 | 109 | } // namespace cpp2py 110 | -------------------------------------------------------------------------------- /c++/cpp2py/exceptions.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include 22 | #include 23 | 24 | #define CPP2PY_ERROR(CLASS, NAME) throw CLASS() << ".. Triqs " << NAME << " at " << __FILE__ << " : " << __LINE__ << "\n\n" 25 | #define CPP2PY_RUNTIME_ERROR CPP2PY_ERROR(cpp2py::runtime_error, "runtime error") 26 | #define CPP2PY_KEYBOARD_INTERRUPT CPP2PY_ERROR(cpp2py::keyboard_interrupt, "Ctrl-C") 27 | #define CPP2PY_ASSERT(X) \ 28 | if (!(X)) CPP2PY_RUNTIME_ERROR << BOOST_PP_STRINGIZE(X) 29 | 30 | namespace cpp2py { 31 | 32 | /** 33 | * Exception with a << stream operator for simple error message. 34 | */ 35 | class exception : public std::exception { 36 | std::stringstream acc; 37 | std::string _trace; 38 | mutable std::string _what; 39 | 40 | public: 41 | exception(); 42 | exception(exception const &e) throw() : acc(e.acc.str()), _trace(e._trace), _what(e._what) {} 43 | exception(exception &&e) = default; 44 | virtual ~exception() {} 45 | 46 | static bool show_cpp_trace; 47 | 48 | template exception &operator<<(T const &x) { return acc << x, *this; } 49 | 50 | // ??? 51 | //exception &operator<<(const char *mess) { 52 | // (*this) << std::string(mess); 53 | // return *this; 54 | //} // to limit code size 55 | 56 | virtual const char *what() const noexcept; 57 | //virtual const char *trace() const { return _trace.c_str(); } 58 | }; 59 | 60 | class runtime_error : public exception { 61 | public: 62 | runtime_error() : exception() {} 63 | 64 | virtual ~runtime_error() {} 65 | 66 | template runtime_error &operator<<(T &&x) { 67 | exception::operator<<(x); 68 | return *this; 69 | } 70 | }; 71 | 72 | class keyboard_interrupt : public exception { 73 | public: 74 | keyboard_interrupt() : exception() {} 75 | 76 | virtual ~keyboard_interrupt() {} 77 | 78 | template keyboard_interrupt &operator<<(T &&x) { 79 | exception::operator<<(x); 80 | return *this; 81 | } 82 | }; 83 | 84 | } // namespace cpp2py 85 | -------------------------------------------------------------------------------- /c++/cpp2py/get_module.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Simons Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0.txt 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Authors: Nils Wentzell 16 | 17 | #pragma once 18 | 19 | #if (PY_MAJOR_VERSION == 3) and (PY_MINOR_VERSION <= 6) 20 | 21 | inline PyObject *PyImport_GetModule(PyObject *name) { 22 | 23 | PyObject *modules = PyImport_GetModuleDict(); /* borrowed */ 24 | 25 | if (modules == NULL) { 26 | PyErr_SetString(PyExc_RuntimeError, "unable to get sys.modules"); 27 | return NULL; 28 | } 29 | 30 | PyObject *m; 31 | if (PyDict_CheckExact(modules)) { 32 | m = PyDict_GetItemWithError(modules, name); /* borrowed */ 33 | Py_XINCREF(m); 34 | } else { 35 | m = PyObject_GetItem(modules, name); 36 | if (m == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) { PyErr_Clear(); } 37 | } 38 | 39 | return m; 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /c++/cpp2py/macros.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Simons Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0.txt 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Authors: Olivier Parcollet, Nils Wentzell 16 | 17 | #ifndef _CCQ_MACROS_GUARD_H 18 | #define _CCQ_MACROS_GUARD_H 19 | 20 | // CCQ, TRIQS general macros 21 | // GUARD IT do not use pragma once 22 | // hence one can simply include them in every projects 23 | 24 | // --- Stringify macros ----- 25 | 26 | #define AS_STRING(...) AS_STRING2(__VA_ARGS__) 27 | #define AS_STRING2(...) #__VA_ARGS__ 28 | 29 | #define PRINT(X) std::cerr << AS_STRING(X) << " = " << X << " at " << __FILE__ << ":" << __LINE__ << '\n' 30 | #define NDA_PRINT(X) std::cerr << AS_STRING(X) << " = " << X << " at " << __FILE__ << ":" << __LINE__ << '\n' 31 | 32 | // --- Concept macros ----- 33 | 34 | #if (__cplusplus > 201703L) 35 | 36 | // C++20 37 | // REQUIRES17 : only in 17, same for 20 38 | // REQUIRES : in both 39 | 40 | #define AUTO(X) X auto 41 | #define CONCEPT(X) X 42 | 43 | #define REQUIRES17(...) 44 | #define REQUIRES requires 45 | #define REQUIRES20 requires 46 | 47 | // C++20 explicit(bool) : degrade it NOTHING in c++17, we can not check easily 48 | #define EXPLICIT explicit 49 | 50 | // WARNING : it is critical for our doctools to have REQUIRES as requires, NOT a (...) with __VA_ARGS__ 51 | // It is the same effect, but raises unnecessary complications in traversing the AST in libtooling with macros. 52 | 53 | #else 54 | 55 | // C++17 backward compat mode 56 | 57 | #define AUTO(X) auto 58 | #define CONCEPT(X) typename 59 | #define REQUIRES20(...) 60 | 61 | #define EXPLICIT(...) 62 | 63 | #ifdef __clang__ 64 | #define REQUIRES17(...) __attribute__((enable_if(__VA_ARGS__, AS_STRING(__VA_ARGS__)))) 65 | #define REQUIRES(...) __attribute__((enable_if(__VA_ARGS__, AS_STRING(__VA_ARGS__)))) 66 | #elif __GNUC__ 67 | // with the -fconcepts TS only. A degraded concept mode, not exactly the C++20. We return to C++17 + basic require 68 | #define REQUIRES17 requires 69 | #define REQUIRES requires 70 | 71 | #endif 72 | 73 | #endif 74 | 75 | // ----------------------------------------------------------- 76 | 77 | #define FORCEINLINE __inline__ __attribute__((always_inline)) 78 | 79 | #ifdef NDEBUG 80 | 81 | #define EXPECTS(X) 82 | #define ASSERT(X) 83 | #define ENSURES(X) 84 | #define EXPECTS_WITH_MESSAGE(X, ...) 85 | #define ASSERT_WITH_MESSAGE(X, ...) 86 | #define ENSURES_WITH_MESSAGE(X, ...) 87 | 88 | #else 89 | 90 | #include 91 | 92 | #define EXPECTS(X) \ 93 | if (!(X)) { \ 94 | std::cerr << "Precondition " << AS_STRING(X) << " violated at " << __FILE__ << ":" << __LINE__ << "\n"; \ 95 | std::terminate(); \ 96 | } 97 | #define ASSERT(X) \ 98 | if (!(X)) { \ 99 | std::cerr << "Assertion " << AS_STRING(X) << " violated at " << __FILE__ << ":" << __LINE__ << "\n"; \ 100 | std::terminate(); \ 101 | } 102 | #define ENSURES(X) \ 103 | if (!(X)) { \ 104 | std::cerr << "Postcondition " << AS_STRING(X) << " violated at " << __FILE__ << ":" << __LINE__ << "\n"; \ 105 | std::terminate(); \ 106 | } 107 | 108 | #define EXPECTS_WITH_MESSAGE(X, ...) \ 109 | if (!(X)) { \ 110 | std::cerr << "Precondition " << AS_STRING(X) << " violated at " << __FILE__ << ":" << __LINE__ << "\n"; \ 111 | std::cerr << "Error message : " << __VA_ARGS__ << std::endl; \ 112 | std::terminate(); \ 113 | } 114 | #define ASSERT_WITH_MESSAGE(X, ...) \ 115 | if (!(X)) { \ 116 | std::cerr << "Assertion " << AS_STRING(X) << " violated at " << __FILE__ << ":" << __LINE__ << "\n"; \ 117 | std::cerr << "Error message : " << __VA_ARGS__ << std::endl; \ 118 | std::terminate(); \ 119 | } 120 | #define ENSURES_WITH_MESSAGE(X, ...) \ 121 | if (!(X)) { \ 122 | std::cerr << "Postcondition " << AS_STRING(X) << " violated at " << __FILE__ << ":" << __LINE__ << "\n"; \ 123 | std::cerr << "Error message : " << __VA_ARGS__ << std::endl; \ 124 | std::terminate(); \ 125 | } 126 | 127 | #endif 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /c++/cpp2py/misc.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #include "./misc.hpp" 20 | 21 | namespace cpp2py { 22 | 23 | // Check python version. 24 | // The version passed here is the version of libpython used to compile the module 25 | // as the module includes this file. 26 | // MUST be in cpp. do NOT put this function in hpp. 27 | bool check_python_version(const char *module_name, long version_major, long version_minor) { 28 | 29 | std::stringstream out; 30 | 31 | // Check that the python version of Python.h used to: 32 | // - compile the module including c2py.hpp 33 | // (arguments of this function and frozen at compile time of the module). 34 | // - compile this file, hence libc2py. 35 | // (PY_MAJOR_VERSION and PY_MINOR_VERSION below, determined by the Python.h used to compile this file) 36 | // are identical. 37 | if (version_major != PY_MAJOR_VERSION or version_minor != PY_MINOR_VERSION) { 38 | out << "\n\n Can not load the c2py module " // 39 | << (module_name ? module_name : "") << " ! \n\n" // 40 | << " The c2py library was compiled with Python version " // 41 | << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION << "\n" // 42 | << " but the python extension is compiled with Python version" // 43 | << version_major << '.' << version_minor << "\n" // 44 | << " They should be identical.\n"; 45 | } 46 | 47 | // Check that the python version of : 48 | // - the interpreter currently running, picked up from the sys module at runtime. 49 | // - Python.h used to compile the module including c2py.hpp 50 | // (arguments of this function and frozen at compile time of the module). 51 | // are identical. 52 | auto sys_version_info = pyref::module("sys").attr("version_info"); 53 | auto rt_version_major = PyLong_AsLong(sys_version_info.attr("major")); 54 | auto rt_version_minor = PyLong_AsLong(sys_version_info.attr("minor")); 55 | if (rt_version_major != PY_MAJOR_VERSION or rt_version_minor != PY_MINOR_VERSION) { 56 | out << "\n\n Can not load the c2py module " // 57 | << (module_name ? module_name : "") << " ! \n\n" // 58 | << " The c2py library was compiled with Python version " // 59 | << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION << "\n" // 60 | << " but the python intepreter has version " // 61 | << rt_version_major << '.' << rt_version_minor << "\n" // 62 | << " They should be identical.\n"; 63 | } 64 | 65 | if (out.str().empty()) return true; 66 | PyErr_SetString(PyExc_ImportError, out.str().c_str()); 67 | return false; 68 | } 69 | 70 | } // namespace cpp2py 71 | -------------------------------------------------------------------------------- /c++/cpp2py/misc.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include "./exceptions.hpp" 22 | #include "./py_converter.hpp" 23 | #include 24 | 25 | // silence warning on intel 26 | #ifndef __INTEL_COMPILER 27 | #pragma clang diagnostic ignored "-Wdeprecated-writable-strings" 28 | #endif 29 | #pragma GCC diagnostic ignored "-Wwrite-strings" 30 | 31 | namespace cpp2py { 32 | 33 | inline char *get_current_time() { // helper function to print the time in the CATCH_AND_RETURN macro 34 | time_t rawtime; 35 | time(&rawtime); 36 | return ctime(&rawtime); 37 | } 38 | 39 | // I can use the trace in cpp2py::exception 40 | #define CATCH_AND_RETURN(MESS, RET) \ 41 | catch (cpp2py::keyboard_interrupt const &e) { \ 42 | PyErr_SetString(PyExc_KeyboardInterrupt, e.what()); \ 43 | return RET; \ 44 | } \ 45 | catch (cpp2py::exception const &e) { \ 46 | auto err = std::string(".. Error occurred at ") + cpp2py::get_current_time() + "\n.. Error " + MESS + "\n.. C++ error was : \n" + e.what(); \ 47 | PyErr_SetString(PyExc_RuntimeError, err.c_str()); \ 48 | return RET; \ 49 | } \ 50 | catch (std::exception const &e) { \ 51 | auto err = std::string(".. Error occurred at ") + cpp2py::get_current_time() + "\n.. Error " + MESS + "\n.. C++ error was : \n" + e.what(); \ 52 | PyErr_SetString(PyExc_RuntimeError, err.c_str()); \ 53 | return RET; \ 54 | } \ 55 | catch (...) { \ 56 | auto err = std::string(".. Error occurred at ") + cpp2py::get_current_time() + "\n.. Error " + MESS; \ 57 | PyErr_SetString(PyExc_RuntimeError, err.c_str()); \ 58 | return RET; \ 59 | } 60 | 61 | // ----------------------------------- 62 | // Tools for the implementation of reduce (V2) 63 | // ----------------------------------- 64 | 65 | // auxiliary object to reduce the object into a tuple 66 | class reductor { 67 | std::vector elem; 68 | PyObject *as_tuple() { 69 | int l = elem.size(); 70 | PyObject *tup = PyTuple_New(l); 71 | for (int pos = 0; pos < l; ++pos) PyTuple_SetItem(tup, pos, elem[pos]); 72 | return tup; 73 | } 74 | 75 | public: 76 | template reductor &operator&(T &x) { 77 | elem.push_back(convert_to_python(x)); 78 | return *this; 79 | } 80 | template PyObject *apply_to(T &x) { 81 | x.serialize(*this, 0); 82 | return as_tuple(); 83 | } 84 | }; 85 | 86 | // inverse : auxiliary object to reconstruct the object from the tuple ... 87 | class reconstructor { 88 | PyObject *tup; // borrowed ref 89 | int pos = 0, pos_max = 0; 90 | 91 | public: 92 | reconstructor(PyObject *borrowed_ref) : tup(borrowed_ref) { pos_max = PyTuple_Size(tup) - 1; } 93 | template reconstructor &operator&(T &x) { 94 | if (pos > pos_max) CPP2PY_RUNTIME_ERROR << " Tuple too short in reconstruction"; 95 | x = convert_from_python(PyTuple_GetItem(tup, pos++)); 96 | return *this; 97 | } 98 | }; 99 | 100 | // no protection for convertion ! 101 | template struct py_converter_from_reductor { 102 | template static PyObject *c2py(U &&x) { return reductor{}.apply_to(x); } 103 | static T py2c(PyObject *ob) { 104 | T res; 105 | auto r = reconstructor{ob}; 106 | res.serialize(r, 0); 107 | return res; 108 | } 109 | static bool is_convertible(PyObject *ob, bool raise_exception) { return true; } 110 | }; 111 | 112 | // Check python version. 113 | // The version passed here is the version of libpython used to compile the module 114 | // as the module includes this file. 115 | // MUST be in cpp. do NOT put this function in hpp. 116 | bool check_python_version(const char *module_name = nullptr, long version_major = PY_MAJOR_VERSION, long version_minor = PY_MINOR_VERSION); 117 | 118 | } // namespace cpp2py 119 | -------------------------------------------------------------------------------- /c++/cpp2py/numpy_proxy.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Simons Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0.txt 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Authors: Nils Wentzell 16 | 17 | // C.f. https://numpy.org/doc/1.21/reference/c-api/array.html#importing-the-api 18 | #define NO_IMPORT_ARRAY 19 | #define PY_ARRAY_UNIQUE_SYMBOL _cpp2py_ARRAY_API 20 | 21 | #include "numpy_proxy.hpp" 22 | 23 | namespace cpp2py { 24 | 25 | // Make a new view_info 26 | PyObject *numpy_proxy::to_python() { 27 | 28 | #ifdef PYTHON_NUMPY_VERSION_LT_17 29 | int flags = NPY_BEHAVED & ~NPY_OWNDATA; 30 | #else 31 | int flags = NPY_ARRAY_BEHAVED & ~NPY_ARRAY_OWNDATA; 32 | #endif 33 | // make the array read only 34 | if (is_const) flags &= ~NPY_ARRAY_WRITEABLE; 35 | PyObject *result = 36 | PyArray_NewFromDescr(&PyArray_Type, PyArray_DescrFromType(element_type), rank, extents.data(), strides.data(), data, flags, NULL); 37 | if (not result) return nullptr; // the Python error is set 38 | 39 | if (!PyArray_Check(result)) { 40 | PyErr_SetString(PyExc_RuntimeError, "The python object is not a numpy array"); 41 | return nullptr; 42 | } 43 | 44 | PyArrayObject *arr = (PyArrayObject *)(result); 45 | #ifdef PYTHON_NUMPY_VERSION_LT_17 46 | arr->base = base; 47 | assert(arr->flags == (arr->flags & ~NPY_OWNDATA)); 48 | #else 49 | int r = PyArray_SetBaseObject(arr, base); 50 | //EXPECTS(r == 0); 51 | //EXPECTS(PyArray_FLAGS(arr) == (PyArray_FLAGS(arr) & ~NPY_ARRAY_OWNDATA)); 52 | #endif 53 | base = nullptr; // ref is stolen by the new object 54 | 55 | return result; 56 | } 57 | 58 | // ---------------------------------------------------------- 59 | 60 | // Extract a view_info from python 61 | numpy_proxy make_numpy_proxy(PyObject *obj) { 62 | 63 | if (obj == NULL) return {}; 64 | if (not PyArray_Check(obj)) return {}; 65 | 66 | numpy_proxy result; 67 | 68 | // extract strides and lengths 69 | PyArrayObject *arr = (PyArrayObject *)(obj); 70 | 71 | #ifdef PYTHON_NUMPY_VERSION_LT_17 72 | result.rank = arr->nd; 73 | #else 74 | result.rank = PyArray_NDIM(arr); 75 | #endif 76 | 77 | result.element_type = PyArray_TYPE(arr); 78 | result.extents.resize(result.rank); 79 | result.strides.resize(result.rank); 80 | result.data = PyArray_DATA(arr); 81 | // base is ignored, stays at nullptr 82 | 83 | #ifdef PYTHON_NUMPY_VERSION_LT_17 84 | for (long i = 0; i < result.rank; ++i) { 85 | result.extents[i] = arr->dimensions[i]; 86 | result.strides[i] = arr->strides[i]; 87 | } 88 | #else 89 | for (size_t i = 0; i < result.rank; ++i) { 90 | result.extents[i] = PyArray_DIMS(arr)[i]; 91 | result.strides[i] = PyArray_STRIDES(arr)[i]; 92 | } 93 | #endif 94 | 95 | return result; 96 | } 97 | 98 | // ---------------------------------------------------------- 99 | 100 | PyObject *make_numpy_copy(PyObject *obj, int rank, long element_type) { 101 | 102 | if (obj == nullptr) return nullptr; 103 | 104 | // From obj, we ask the numpy library to make a numpy, and of the correct type. 105 | // This handles automatically the cases where : 106 | // - we have list, or list of list/tuple 107 | // - the numpy type is not the one we want. 108 | // - adjust the dimension if needed 109 | // If obj is an array : 110 | // - if Order is same, don't change it 111 | // - else impose it (may provoque a copy). 112 | // if obj is not array : 113 | // - Order = FortranOrder or SameOrder - > Fortran order otherwise C 114 | 115 | int flags = 0; //(ForceCast ? NPY_FORCECAST : 0) ;// do NOT force a copy | (make_copy ? NPY_ENSURECOPY : 0); 116 | //if (!(PyArray_Check(obj) )) 117 | //flags |= ( IndexMapType::traversal_order == indexmaps::mem_layout::c_order(rank) ? NPY_C_CONTIGUOUS : NPY_F_CONTIGUOUS); //impose mem order 118 | #ifdef PYTHON_NUMPY_VERSION_LT_17 119 | flags |= (NPY_C_CONTIGUOUS); //impose mem order 120 | flags |= (NPY_ENSURECOPY); 121 | #else 122 | flags |= (NPY_ARRAY_C_CONTIGUOUS); // impose mem order 123 | flags |= (NPY_ARRAY_ENSURECOPY); 124 | #endif 125 | return PyArray_FromAny(obj, PyArray_DescrFromType(element_type), rank, rank, flags, NULL); // new ref 126 | } 127 | 128 | } // namespace cpp2py 129 | -------------------------------------------------------------------------------- /c++/cpp2py/numpy_proxy.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Simons Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0.txt 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Authors: Olivier Parcollet, Nils Wentzell 16 | 17 | #pragma once 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "pyref.hpp" 26 | 27 | namespace cpp2py { 28 | 29 | // the basic information for a numpy array 30 | struct numpy_proxy { 31 | int rank = 0; 32 | long element_type = 0; 33 | void *data = nullptr; 34 | bool is_const = false; 35 | std::vector extents, strides; 36 | PyObject *base = nullptr; // The ref. counting guard typically 37 | 38 | // Returns a new ref (or NULL if failure) with a new numpy. 39 | // If failure, return null with the Python exception set 40 | PyObject *to_python(); 41 | }; 42 | 43 | // From a numpy, extract the info. Better than a constructor, I want to use the aggregate constructor of the struct also. 44 | numpy_proxy make_numpy_proxy(PyObject *); 45 | 46 | // Make a copy in Python with the given rank and element_type 47 | // If failure, return null with the Python exception set 48 | PyObject *make_numpy_copy(PyObject *obj, int rank, long elements_type); 49 | 50 | // 51 | template inline constexpr long npy_type = -1; 52 | template inline constexpr long npy_type = npy_type; 53 | template inline constexpr bool has_npy_type = (npy_type >= 0); 54 | 55 | #define NPY_CONVERT(C, P) template <> inline constexpr long npy_type = P; 56 | NPY_CONVERT(pyref, NPY_OBJECT) 57 | NPY_CONVERT(PyObject *, NPY_OBJECT) 58 | NPY_CONVERT(bool, NPY_BOOL) 59 | NPY_CONVERT(char, NPY_STRING) 60 | NPY_CONVERT(signed char, NPY_BYTE) 61 | NPY_CONVERT(unsigned char, NPY_UBYTE) 62 | NPY_CONVERT(std::byte, NPY_UBYTE) 63 | NPY_CONVERT(short, NPY_SHORT) 64 | NPY_CONVERT(unsigned short, NPY_USHORT) 65 | NPY_CONVERT(int, NPY_INT) 66 | NPY_CONVERT(unsigned int, NPY_UINT) 67 | NPY_CONVERT(long, NPY_LONG) 68 | NPY_CONVERT(unsigned long, NPY_ULONG) 69 | NPY_CONVERT(long long, NPY_LONGLONG) 70 | NPY_CONVERT(unsigned long long, NPY_ULONGLONG) 71 | NPY_CONVERT(float, NPY_FLOAT) 72 | NPY_CONVERT(double, NPY_DOUBLE) 73 | NPY_CONVERT(long double, NPY_LONGDOUBLE) 74 | NPY_CONVERT(std::complex, NPY_CFLOAT) 75 | NPY_CONVERT(std::complex, NPY_CDOUBLE) 76 | NPY_CONVERT(std::complex, NPY_CLONGDOUBLE) 77 | #undef NPY_CONVERT 78 | 79 | } // namespace cpp2py 80 | -------------------------------------------------------------------------------- /c++/cpp2py/py_stream.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017-2018 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2018-2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | #include 22 | #include "./pyref.hpp" 23 | 24 | namespace cpp2py { 25 | 26 | /** 27 | */ 28 | class py_stream { 29 | 30 | pyref sys, sys_out; 31 | void _write(const char *s) { pyref res = PyObject_CallMethod(sys_out, (char *)"write", (char *)"s", s); } 32 | 33 | public: 34 | py_stream() { 35 | if (!Py_IsInitialized()) CPP2PY_RUNTIME_ERROR << "Construction of a py_stream before the interpreter is initialized !!"; 36 | sys = pyref::module("sys"); 37 | sys_out = sys.attr("stdout"); 38 | } 39 | 40 | template py_stream &operator<<(T const &x) { 41 | std::stringstream fs; 42 | fs << x; 43 | _write(fs.str().c_str()); 44 | return *this; 45 | } 46 | 47 | // this is the type of std::cout 48 | typedef std::basic_ostream> CoutType; 49 | 50 | // this is the function signature of std::endl 51 | typedef CoutType &(*StandardEndLine)(CoutType &); 52 | 53 | // define an operator<< to take in std::endl 54 | py_stream &operator<<(StandardEndLine manip) { 55 | _write("\n"); 56 | return *this; 57 | } 58 | }; 59 | 60 | } // namespace cpp2py 61 | -------------------------------------------------------------------------------- /c++/cpp2py/pyref.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017-2018 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2018-2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | #include 21 | 22 | #include "./get_module.hpp" 23 | 24 | #include 25 | using namespace std::string_literals; 26 | 27 | namespace cpp2py { 28 | 29 | /** 30 | * A class to own a reference PyObject *, with proper reference counting. 31 | */ 32 | class pyref { 33 | PyObject *ob = nullptr; 34 | 35 | public: 36 | /// Null 37 | pyref() = default; 38 | 39 | /// Takes ownership of the reference 40 | pyref(PyObject *new_ref) : ob(new_ref) {} 41 | 42 | /// Release the ref 43 | ~pyref() { Py_XDECREF(ob); } 44 | 45 | /// Copy constructor 46 | pyref(pyref const &p) : ob(p.ob) { Py_XINCREF(ob); } 47 | 48 | /// Move constructor 49 | pyref(pyref &&p) : ob(p.ob) { p.ob = nullptr; } 50 | 51 | /// No copy assign. 52 | pyref &operator=(pyref const &p) { 53 | Py_XDECREF(ob); 54 | ob = p.ob; 55 | Py_XINCREF(ob); 56 | return *this; 57 | } 58 | 59 | /// Move assign 60 | pyref &operator=(pyref &&p) { 61 | Py_XDECREF(ob); 62 | ob = p.ob; 63 | p.ob = nullptr; 64 | return *this; 65 | } 66 | 67 | /// Returns a borrowed reference 68 | operator PyObject *() const { return ob; } 69 | 70 | /// Returns a new reference to the object 71 | [[nodiscard]] PyObject *new_ref() const { 72 | Py_XINCREF(ob); 73 | return ob; 74 | } 75 | 76 | /// ref counting 77 | [[nodiscard]] int refcnt() const { return (ob != nullptr ? Py_REFCNT(ob) : -100); } 78 | 79 | /// True iif the object is not NULL 80 | explicit operator bool() const { return (ob != nullptr); } 81 | 82 | /// Is object NULL 83 | [[nodiscard]] bool is_null() const { return ob == nullptr; } 84 | 85 | /// Is it Py_None 86 | [[nodiscard]] bool is_None() const { return ob == Py_None; } 87 | 88 | /// Returns the attribute of this. Null if error, or if is_null. 89 | pyref attr(const char *s) { return (ob ? PyObject_GetAttrString(ob, s) : nullptr); } // NULL : pass the error in chain call x.attr().attr().... 90 | 91 | /// Call 92 | pyref operator()(PyObject *a1) { 93 | return (ob ? PyObject_CallFunctionObjArgs(ob, a1, NULL) : nullptr); 94 | } // NULL : pass the error in chain call x.attr().attr().... 95 | 96 | /// Call 97 | pyref operator()(PyObject *a1, PyObject *a2) { 98 | return (ob ? PyObject_CallFunctionObjArgs(ob, a1, a2, NULL) : nullptr); 99 | } // NULL : pass the error in chain call x.attr().attr().... 100 | 101 | /// Import the module and returns a pyref to it 102 | static pyref module(std::string const &module_name) { 103 | // Maybe the module was already imported? 104 | PyObject *mod = PyImport_GetModule(pyref::string(module_name)); 105 | 106 | // If not, import normally 107 | if (mod == nullptr) mod = PyImport_ImportModule(module_name.c_str()); 108 | 109 | // Did we succeed? 110 | if (mod == nullptr) throw std::runtime_error(std::string{"Failed to import module "} + module_name); 111 | 112 | return mod; 113 | } 114 | 115 | /// Make a Python string from the C++ string 116 | static pyref string(std::string const &s) { return PyUnicode_FromString(s.c_str()); } 117 | 118 | /// Make a Python Tuple from the C++ objects 119 | template static pyref make_tuple(T const &...x) { return PyTuple_Pack(sizeof...(T), static_cast(x)...); } 120 | 121 | /// gets a reference to the class cls_name in module_name 122 | static pyref get_class(const char *module_name, const char *cls_name, bool raise_exception) { 123 | pyref cls = pyref::module(module_name).attr(cls_name); 124 | if (cls.is_null() && raise_exception) { 125 | std::string s = std::string{"Cannot find the class "} + module_name + "." + cls_name; 126 | PyErr_SetString(PyExc_TypeError, s.c_str()); 127 | } 128 | return cls; 129 | } 130 | 131 | /// checks that ob is of type module_name.cls_name 132 | static bool check_is_instance(PyObject *ob, PyObject *cls, bool raise_exception) { 133 | int i = PyObject_IsInstance(ob, cls); 134 | if (i == -1) { // an error has occurred 135 | i = 0; 136 | if (!raise_exception) PyErr_Clear(); 137 | } 138 | if ((i == 0) && (raise_exception)) { 139 | pyref cls_name_obj = PyObject_GetAttrString(cls, "__name__"); 140 | std::string err = "Type error: Python object does not match expected type "; 141 | err.append(PyUnicode_AsUTF8(cls_name_obj)); 142 | PyErr_SetString(PyExc_TypeError, err.c_str()); 143 | } 144 | return i; 145 | } 146 | }; 147 | static_assert(sizeof(pyref) == sizeof(PyObject *), "pyref must contain only a PyObject *"); 148 | 149 | // FIXME : put static or the other functions inline ? 150 | 151 | /// Returns a pyref from a borrowed ref 152 | inline pyref borrowed(PyObject *ob) { 153 | Py_XINCREF(ob); 154 | return {ob}; 155 | } 156 | 157 | inline std::string to_string(PyObject *ob) { 158 | pyref py_str = PyObject_Str(ob); 159 | if (py_str.is_null()) return "[unprintable]"; 160 | return PyUnicode_AsUTF8(py_str); 161 | } 162 | 163 | } // namespace cpp2py 164 | -------------------------------------------------------------------------------- /c++/cpp2py/signal_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2019-2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #include "cpp2py/signal_handler.hpp" 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace cpp2py::signal_handler { 26 | 27 | namespace { 28 | 29 | std::vector signals_list; 30 | bool initialized = false; 31 | 32 | void slot(int signal) { 33 | std::cerr << "TRIQS : Received signal " << signal << std::endl; 34 | signals_list.push_back(signal); 35 | } 36 | } // namespace 37 | 38 | void start() { 39 | if (initialized) return; 40 | static struct sigaction action; 41 | memset(&action, 0, sizeof(action)); 42 | action.sa_handler = slot; 43 | sigaction(SIGINT, &action, NULL); 44 | sigaction(SIGTERM, &action, NULL); 45 | sigaction(SIGXCPU, &action, NULL); 46 | sigaction(SIGQUIT, &action, NULL); 47 | sigaction(SIGUSR1, &action, NULL); 48 | sigaction(SIGUSR2, &action, NULL); 49 | sigaction(SIGSTOP, &action, NULL); 50 | initialized = true; 51 | } 52 | 53 | void stop() { 54 | signals_list.clear(); 55 | initialized = false; 56 | } 57 | 58 | bool received(bool pop_) { 59 | //if (!initialized) start(); 60 | bool r = signals_list.size() != 0; 61 | if (r && pop_) pop(); 62 | return r; 63 | } 64 | 65 | int last() { return signals_list.back(); } 66 | void pop() { return signals_list.pop_back(); } 67 | 68 | } // namespace cpp2py::signal_handler 69 | -------------------------------------------------------------------------------- /c++/cpp2py/signal_handler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2 | // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS) 3 | // Copyright (c) 2020 Simons Foundation 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0.txt 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // Authors: Olivier Parcollet, Nils Wentzell 18 | 19 | #pragma once 20 | namespace cpp2py { 21 | namespace signal_handler { 22 | 23 | /// Start the signal handler 24 | void start(); 25 | 26 | /// Stop it. ? 27 | void stop(); 28 | 29 | /// A signal has been received. If pop, and there is a signal, pop it. 30 | bool received(bool pop = false); 31 | 32 | /// Last received. 33 | int last(); 34 | 35 | /// pop the last signal 36 | void pop(); 37 | } // namespace signal_handler 38 | } // namespace cpp2py 39 | -------------------------------------------------------------------------------- /c++/cpp2py/traits.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Simons Foundation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0.txt 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Authors: Nils Wentzell 16 | 17 | #pragma once 18 | #include 19 | 20 | namespace cpp2py { 21 | 22 | template