├── .gitignore ├── .readthedocs.yaml ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── bench ├── Makefile ├── basic_micro.py ├── bench_functioncalls.py ├── bench_runvector.py ├── functioncalls.cxx ├── functioncalls.h ├── functioncalls.i ├── functioncalls.xml ├── functioncalls_main.cxx ├── py11_functioncalls.cxx ├── py_functioncalls.py ├── py_runvector.py ├── runvector.cxx ├── runvector.h ├── runvector.i ├── runvector.xml ├── runvector_main.cxx └── support.py ├── doc ├── .gitignore ├── Makefile ├── make.bat ├── requirements.txt ├── source │ ├── _static │ │ └── css │ │ │ └── custom.css │ ├── basic_types.rst │ ├── bugs.rst │ ├── changelog.rst │ ├── classes.rst │ ├── cmake_interface.rst │ ├── conf.py │ ├── cppyy_features_header.rst │ ├── cuda.rst │ ├── debugging.rst │ ├── examples.rst │ ├── exceptions.rst │ ├── functions.rst │ ├── history.rst │ ├── index.rst │ ├── installation.rst │ ├── license.rst │ ├── lowlevel.rst │ ├── misc.rst │ ├── numba.rst │ ├── packages.rst │ ├── philosophy.rst │ ├── python.rst │ ├── pythonizations.rst │ ├── repositories.rst │ ├── starting.rst │ ├── stl.rst │ ├── strings.rst │ ├── testing.rst │ ├── toplevel.rst │ ├── type_conversions.rst │ └── utilities.rst └── tutorial │ ├── CppyyTutorial.ipynb │ └── GSLPythonizationTutorial.ipynb ├── etc └── valgrind-cppyy-cling.supp ├── installer └── cppyy_monkey_patch.py ├── pyproject.toml ├── python ├── cppyy │ ├── __init__.py │ ├── __pyinstaller │ │ ├── __init__.py │ │ └── hook-cppyy.py │ ├── _cpython_cppyy.py │ ├── _pypy_cppyy.py │ ├── _pythonization.py │ ├── _stdcpp_fix.py │ ├── _typemap.py │ ├── _version.py │ ├── interactive.py │ ├── ll.py │ ├── numba_ext.py │ ├── reflex.py │ └── types.py └── cppyy_compat │ └── __init__.py ├── setup.cfg ├── setup.py └── test ├── Makefile ├── __init__.py ├── advancedcpp.cxx ├── advancedcpp.h ├── advancedcpp.xml ├── advancedcpp2.cxx ├── advancedcpp2.h ├── advancedcpp2.xml ├── assert_interactive.py ├── bindexplib.py ├── conftest.py ├── conversions.cxx ├── conversions.h ├── conversions.xml ├── cpp11features.cxx ├── cpp11features.h ├── cpp11features.xml ├── crossinheritance.cxx ├── crossinheritance.h ├── crossinheritance.xml ├── datatypes.cxx ├── datatypes.h ├── datatypes.xml ├── doc_args_funcs.py ├── doc_helper.cxx ├── doc_helper.h ├── doc_helper.xml ├── example01.cxx ├── example01.h ├── example01.xml ├── fragile.cxx ├── fragile.h ├── fragile.xml ├── make_dict_win32.py ├── operators.cxx ├── operators.h ├── operators.xml ├── overloads.cxx ├── overloads.h ├── overloads.xml ├── pythonizables.cxx ├── pythonizables.h ├── pythonizables.xml ├── std_streams.cxx ├── std_streams.h ├── std_streams.xml ├── stltypes.cxx ├── stltypes.h ├── stltypes.xml ├── support.py ├── templ_args_funcs.py ├── templates.cxx ├── templates.h ├── templates.xml ├── test_aclassloader.py ├── test_advancedcpp.py ├── test_api.py ├── test_boost.py ├── test_concurrent.py ├── test_conversions.py ├── test_cpp11features.py ├── test_crossinheritance.py ├── test_datatypes.py ├── test_doc_features.py ├── test_eigen.py ├── test_fragile.py ├── test_leakcheck.py ├── test_lowlevel.py ├── test_numba.py ├── test_operators.py ├── test_overloads.py ├── test_pythonify.py ├── test_pythonization.py ├── test_regression.py ├── test_stltypes.py ├── test_streams.py └── test_templates.py /.gitignore: -------------------------------------------------------------------------------- 1 | # python compiled files 2 | *.pyc 3 | *.pyo 4 | 5 | # dictionary products 6 | *.so 7 | *.dll 8 | *.lib 9 | *.def 10 | *.exp 11 | *.pcm 12 | *.rootmap 13 | *_rflx.cpp 14 | 15 | .cache 16 | .pytest_cache 17 | 18 | # distribution / packaging 19 | .Python 20 | env/ 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | downloads/ 25 | eggs/ 26 | .eggs/ 27 | lib/ 28 | lib64/ 29 | parts/ 30 | sdist/ 31 | var/ 32 | wheels/ 33 | *.egg-info/ 34 | .installed.cfg 35 | *.egg 36 | 37 | # notebook products 38 | .ipynb_checkpoints 39 | doc/tutorial/gsl_selection.xml 40 | 41 | # saved log files of benches 42 | bench/logs 43 | bench/*.log 44 | bench/swig_*.py 45 | bench/*_wrap.cxx 46 | bench/*_main 47 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.11" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: doc/source/conf.py 17 | 18 | # We recommend specifying your dependencies to enable reproducible builds: 19 | # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 20 | python: 21 | install: 22 | - requirements: doc/requirements.txt 23 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2002-2021, The Regents of the University of California, 2 | through Lawrence Berkeley National Laboratory (subject to receipt of 3 | any required approvals from the U.S. Dept. of Energy). All rights 4 | reserved. Redistribution and use in source and binary forms, with or 5 | without modification, are permitted provided that the following 6 | conditions are met: 7 | 8 | (1) Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | (2) Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | (3) Neither the name of the University of California, Lawrence Berkeley 14 | National Laboratory, U.S. Dept. of Energy nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 22 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 24 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | SUCH DAMAGE. 29 | 30 | You are under no obligation whatsoever to provide any bug fixes, 31 | patches, or upgrades to the features, functionality or performance of 32 | the source code ("Enhancements") to anyone; however, if you choose to 33 | make your Enhancements available either publicly, or directly to 34 | Lawrence Berkeley National Laboratory, without imposing a separate 35 | written license agreement for such Enhancements, then you hereby grant 36 | the following license: a non-exclusive, royalty-free perpetual license 37 | to install, use, modify, prepare derivative works, incorporate into 38 | other computer software, distribute, and sublicense such Enhancements 39 | or derivative works thereof, in binary and source code form. 40 | 41 | 42 | Additional copyright holders 43 | ---------------------------- 44 | 45 | In addition to LBNL/UC Berkeley, this package contains files copyrighted by 46 | one or more of the following people and organizations, and licensed under 47 | the same conditions (except for some compatible licenses as retained in the 48 | source code): 49 | 50 | Lucio Asnaghi 51 | Simone Bacchio 52 | Aditi Dutta 53 | Shaheed Haque 54 | Aaron Jomy 55 | Jonas Rembser 56 | Toby StClere-Smithe 57 | Stefan Wunsch 58 | 59 | Conda-forge recipes were provided by Julian Rueth and Isuru Fernando. 60 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Include the license file 2 | include LICENSE.txt 3 | 4 | # Add custom installer 5 | recursive-include installer * 6 | 7 | # Do not add the test or doc directories 8 | prune test 9 | prune doc 10 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst -*- 2 | 3 | cppyy: Python-C++ bindings interface based on Cling/LLVM 4 | ======================================================== 5 | 6 | cppyy provides fully automatic, dynamic Python-C++ bindings by leveraging 7 | the Cling C++ interpreter and LLVM. 8 | It supports both PyPy (natively), CPython, and C++ language standards 9 | through C++20 (and parts of C++13). 10 | 11 | Details and performance are described in 12 | `this paper `_, 13 | originally presented at PyHPC'16, but since updated with improved performance 14 | numbers. 15 | 16 | Full documentation: `cppyy.readthedocs.io `_. 17 | 18 | Notebook-based tutorial: `Cppyy Tutorial `_. 19 | 20 | For Anaconda/miniconda, install cppyy from `conda-forge `_. 21 | 22 | ---- 23 | 24 | Change log: 25 | https://cppyy.readthedocs.io/en/latest/changelog.html 26 | 27 | Bug reports/feedback: 28 | https://github.com/wlav/cppyy/issues 29 | -------------------------------------------------------------------------------- /bench/Makefile: -------------------------------------------------------------------------------- 1 | dicts := functioncallsDict.so runvectorDict.so 2 | libs := libfunctioncalls.so librunvector.so 3 | modules := 4 | execs := functioncalls_main runvector_main 5 | CHECK_PYBIND11 := $(shell python -m pybind11 1>/dev/null 2>/dev/null; echo $$?) 6 | ifeq ($(CHECK_PYBIND11),0) 7 | modules += py11_functioncalls.so 8 | endif 9 | CHECK_SWIG := $(shell swig -help 1>/dev/null 2>/dev/null; echo $$?) 10 | ifeq ($(CHECK_SWIG),0) 11 | modules += _swig_functioncalls.so swig_functioncalls.py 12 | modules += _swig_runvector.so swig_runvector.py 13 | endif 14 | all : $(dicts) $(libs) $(execs) $(modules) 15 | 16 | clingconfig := cling-config 17 | 18 | cppflags:=-O3 -fPIC 19 | cppyy_cppflags=$(shell $(clingconfig) --cflags) $(cppflags) 20 | 21 | PLATFORM := $(shell uname -s) 22 | ifeq ($(PLATFORM),Darwin) 23 | MACHINE := $(shell uname -m) 24 | cppflags+=-dynamiclib -arch $(MACHINE) -undefined dynamic_lookup 25 | endif 26 | 27 | py11_%.so: py11_%.cxx lib%.so 28 | $(CXX) $(cppflags) -shared -std=c++11 -fPIC `python -m pybind11 --includes` py11_$*.cxx -o $@ -L. -l$* 29 | 30 | swig_%.py: %.h %.i 31 | swig -python -c++ -builtin $*.i 32 | 33 | _swig_%.so: swig_%.py lib%.so 34 | $(CXX) $(cppflags) -shared -I$(shell python -c 'import distutils.sysconfig as ds; print(ds.get_config_var("INCLUDEPY"))') -std=c++11 -fPIC $*_wrap.cxx -o $@ -L. -l$* 35 | 36 | %Dict.so: %_rflx.cpp lib%.so 37 | $(CXX) $(cppyy_cppflags) -shared -o $@ $*_rflx.cpp -L. -l$* 38 | 39 | %_rflx.cpp: %.h %.xml 40 | genreflex $< --selection=$*.xml --rootmap=$*Dict.rootmap --rootmap-lib=$*Dict.so 41 | 42 | lib%.so: %.cxx 43 | $(CXX) $(cppflags) -shared -o lib$*.so $*.cxx 44 | 45 | # note -O2 for cxx to make sure code actually runs instead of being optimized out of existence 46 | %_main: %_main.cxx lib%.so 47 | $(CXX) -std=c++11 -O2 -fPIC -o $@ $*_main.cxx -L. -l$* 48 | 49 | .PHONY: bench clean 50 | 51 | bench: all 52 | pytest -s bench_runvector.py --benchmark-sort=mean 53 | 54 | clean: 55 | -rm -f $(dicts) $(libs) $(execs) $(modules) $(wildcard *.rootmap) $(wildcard *_rdict.pcm) $(wildcard *_wrap.cxx) 56 | -------------------------------------------------------------------------------- /bench/basic_micro.py: -------------------------------------------------------------------------------- 1 | import cppyy, gc, math, os, psutil, time 2 | 3 | NLARGE = 20000000 4 | 5 | process = psutil.Process(os.getpid()) 6 | 7 | def benchit(what, callf, N): 8 | print("running:", what) 9 | mpre = process.memory_info().rss/1024 10 | tpre = time.perf_counter() 11 | for i in range(N): 12 | callf() 13 | tpost = time.perf_counter() 14 | gc.collect() 15 | mpost = process.memory_info().rss/1024 16 | if tpost - tpre < 1.: 17 | print(" suggest increasing N by %dx" % math.ceil(1./(tpost-tpre))) 18 | print(" time:", (tpost - tpre)/N) 19 | if mpost == mpre: 20 | print(" memcheck passed") 21 | else: 22 | print(" memcheck FAILED:", mpre, mpost) 23 | 24 | cppyy.cppdef(""" 25 | void gfunc() {} 26 | 27 | class MyClass { 28 | public: 29 | void mfunc() {} 30 | }; 31 | """) 32 | 33 | 34 | f = cppyy.gbl.gfunc 35 | benchit("global function", f, NLARGE) 36 | 37 | inst = cppyy.gbl.MyClass() 38 | benchit("member function", inst.mfunc, NLARGE) 39 | -------------------------------------------------------------------------------- /bench/bench_functioncalls.py: -------------------------------------------------------------------------------- 1 | import py, pytest, os, sys, math, warnings 2 | from support import setup_make 3 | 4 | setup_make("functioncallsDict.so") 5 | 6 | 7 | import cppyy 8 | 9 | cppyy.load_library("functioncalls") 10 | cppyy.load_library("functioncallsDict") 11 | 12 | import py_functioncalls 13 | 14 | all_configs = [('py', 'py_functioncalls'), ('cppyy', 'cppyy.gbl')] 15 | 16 | N = 10000 17 | preamble = "@pytest.mark.benchmark(group=group, warmup=True)" 18 | looprange = range 19 | if sys.hexversion < 0x3000000: 20 | looprange = xrange 21 | 22 | try: 23 | import __pypy__ 24 | except ImportError: 25 | try: 26 | import py11_functioncalls 27 | all_configs.append(('py11', 'py11_functioncalls')) 28 | py11 = True 29 | except ImportError: 30 | warnings.warn('pybind11 tests disabled') 31 | py11 = False 32 | 33 | try: 34 | import swig_functioncalls 35 | all_configs.append(('swig', 'swig_functioncalls')) 36 | swig = True 37 | except ImportError: 38 | warnings.warn('swig tests disabled') 39 | swig = False 40 | 41 | all_benches = [] 42 | 43 | 44 | #- group: empty-free --------------------------------------------------------- 45 | all_benches.append(('empty-free', ( 46 | """ 47 | def test_{0}_free_empty_call(benchmark): 48 | benchmark({1}.empty_call) 49 | """, 50 | ))) 51 | 52 | #- group: empty-inst --------------------------------------------------------- 53 | def call_instance_empty(inst): 54 | for i in looprange(N): 55 | inst.empty_call() 56 | 57 | all_benches.append(('empty-inst', ( 58 | """ 59 | def test_{0}_inst_empty_call(benchmark): 60 | inst = {1}.EmptyCall() 61 | benchmark(call_instance_empty, inst) 62 | """, 63 | ))) 64 | 65 | 66 | #- group: builtin-args-free -------------------------------------------------- 67 | all_benches.append(('builtin-args-free', ( 68 | """ 69 | def test_{0}_free_take_an_int(benchmark): 70 | benchmark({1}.take_an_int, 1) 71 | """, 72 | """ 73 | def test_{0}_free_take_a_double(benchmark): 74 | benchmark({1}.take_a_double, 1.) 75 | """, 76 | """ 77 | def test_{0}_free_take_a_struct(benchmark): 78 | benchmark({1}.take_a_struct, {1}.Value()) 79 | """, 80 | ))) 81 | 82 | #- group: builtin-args-inst -------------------------------------------------- 83 | def call_instance_take_an_int(inst, val): 84 | for i in looprange(N): 85 | inst.take_an_int(1) 86 | 87 | def call_instance_take_a_double(inst, val): 88 | for i in looprange(N): 89 | inst.take_a_double(val) 90 | 91 | def call_instance_take_a_struct(inst, val): 92 | for i in looprange(N): 93 | inst.take_a_struct(val) 94 | 95 | 96 | all_benches.append(('builtin-args-inst', ( 97 | """ 98 | def test_{0}_inst_take_an_int(benchmark): 99 | inst = {1}.TakeAValue() 100 | benchmark(call_instance_take_an_int, inst, 1) 101 | """, 102 | """ 103 | def test_{0}_inst_take_a_double(benchmark): 104 | inst = {1}.TakeAValue() 105 | benchmark(call_instance_take_a_double, inst, 1.) 106 | """, 107 | """ 108 | def test_{0}_inst_take_a_struct(benchmark): 109 | inst = {1}.TakeAValue() 110 | benchmark(call_instance_take_a_struct, inst, {1}.Value()) 111 | """, 112 | ))) 113 | 114 | #- group: builtin-args-pass -------------------------------------------------- 115 | def call_instance_pass_int(inst, val): 116 | for i in looprange(N): 117 | inst.pass_int(val) 118 | 119 | all_benches.append(('builtin-args-pass', ( 120 | """ 121 | def test_{0}_inst_pass_int(benchmark): 122 | inst = {1}.TakeAValue() 123 | benchmark(call_instance_pass_int, inst, 1) 124 | """, 125 | ))) 126 | 127 | 128 | #- group: do_work-free ------------------------------------------------------- 129 | all_benches.append(('do_work-free', ( 130 | """ 131 | def test_{0}_free_do_work(benchmark): 132 | benchmark({1}.do_work, 1.) 133 | """, 134 | ))) 135 | 136 | #- group: do_work-inst ------------------------------------------------------- 137 | def call_instance_do_work(inst): 138 | for i in looprange(N): 139 | inst.do_work(1.) 140 | 141 | all_benches.append(('do_work-inst', ( 142 | """ 143 | def test_{0}_inst_do_work(benchmark): 144 | inst = {1}.DoWork() 145 | benchmark(call_instance_do_work, inst) 146 | """, 147 | ))) 148 | 149 | 150 | #- group: overload-inst ------------------------------------------------------ 151 | def call_instance_overload(inst): 152 | for i in looprange(N): 153 | inst.add_it(1.) 154 | 155 | all_benches.append(('overload-inst', ( 156 | """ 157 | def test_{0}_inst_overload(benchmark): 158 | inst = {1}.OverloadedCall() 159 | benchmark(call_instance_overload, inst) 160 | """, 161 | ))) 162 | 163 | 164 | #- actual creation of all benches -------------------------------------------- 165 | for group, benches in all_benches: 166 | for bench in benches: 167 | for label, modname in all_configs: 168 | exec(preamble+bench.format(label, modname)) 169 | -------------------------------------------------------------------------------- /bench/bench_runvector.py: -------------------------------------------------------------------------------- 1 | import py, pytest, os, sys, math, warnings 2 | from support import setup_make 3 | 4 | setup_make("runvectorDict.so") 5 | 6 | 7 | import cppyy 8 | 9 | cppyy.load_library("runvector") 10 | cppyy.load_library("runvectorDict") 11 | 12 | all_configs = [('cppyy', 'cppyy.gbl')] 13 | 14 | preamble = "@pytest.mark.benchmark(group=group, warmup=True)" 15 | 16 | 17 | try: 18 | import __pypy__ 19 | import py_runvector 20 | all_configs.append(('py', 'py_runvector')) # too slow to run on CPython 21 | except ImportError: 22 | try: 23 | import py11_runvector 24 | all_configs.append(('py11', 'py11_runvector')) 25 | py11 = True 26 | except ImportError: 27 | warnings.warn('pybind11 tests disabled') 28 | py11 = False 29 | 30 | try: 31 | import swig_runvector 32 | all_configs.append(('swig', 'swig_runvector.cvar')) 33 | swig = True 34 | except ImportError: 35 | warnings.warn('swig tests disabled') 36 | swig = False 37 | 38 | all_benches = [] 39 | 40 | 41 | #- group: stl-vector --------------------------------------------------------- 42 | all_benches.append(('stl-vector', ( 43 | """ 44 | def test_{0}_stl_vector(benchmark): 45 | benchmark(sum, {1}.global_vector) 46 | """, 47 | ))) 48 | 49 | 50 | #- actual creation of all benches -------------------------------------------- 51 | for group, benches in all_benches: 52 | for bench in benches: 53 | for label, modname in all_configs: 54 | exec(preamble+bench.format(label, modname)) 55 | -------------------------------------------------------------------------------- /bench/functioncalls.cxx: -------------------------------------------------------------------------------- 1 | #include "functioncalls.h" 2 | 3 | #include 4 | #include 5 | 6 | 7 | //- group: empty ------------------------------------------------------------- 8 | void empty_call() { 9 | /* empty, to measure pure call overhead */ 10 | } 11 | 12 | void EmptyCall::empty_call() { 13 | /* empty, to measure pure call overhead */ 14 | } 15 | 16 | 17 | //- group: builtin-args-free ------------------------------------------------- 18 | void take_an_int(int /* unused */) { 19 | /* empty, to measure pure call overhead */ 20 | } 21 | 22 | void take_a_double(double /* unused */) { 23 | /* empty, to measure pure call overhead */ 24 | } 25 | 26 | void take_a_struct(Value /* unused */) { 27 | /* empty, to measure pure call overhead */ 28 | } 29 | 30 | //- group: builtin-args-inst ------------------------------------------------- 31 | void TakeAValue::take_an_int(int /* unused */) { 32 | /* empty, to measure pure call overhead */ 33 | } 34 | 35 | void TakeAValue::take_a_double(double /* unused */) { 36 | /* empty, to measure pure call overhead */ 37 | } 38 | 39 | void TakeAValue::take_a_struct(Value /* unused */) { 40 | /* empty, to measure pure call overhead */ 41 | } 42 | 43 | //- group: builtin-args-pass ------------------------------------------------- 44 | int TakeAValue::pass_int(int a) { 45 | return a + 42; 46 | } 47 | 48 | 49 | //- group: do-work ----------------------------------------------------------- 50 | double do_work(double arg) { 51 | return atan(arg); 52 | } 53 | 54 | double DoWork::do_work(double arg) { 55 | return atan(arg); 56 | } 57 | 58 | 59 | //- group: overload-inst ----------------------------------------------------- 60 | double OverloadedCall::add_it(int a, int b) { std::terminate(); } 61 | double OverloadedCall::add_it(short a) { std::terminate(); } 62 | double OverloadedCall::add_it(long a) { std::terminate(); } 63 | double OverloadedCall::add_it(int a, int b, int c) { std::terminate(); } 64 | double OverloadedCall::add_it(double a) { return 3.1415 + a; } 65 | double OverloadedCall::add_it(float a) { return 3.1415 + a; } 66 | double OverloadedCall::add_it(int a) { std::terminate(); } 67 | -------------------------------------------------------------------------------- /bench/functioncalls.h: -------------------------------------------------------------------------------- 1 | #ifndef CPPYY_FUNCTIONCALLS_H 2 | #define CPPYY_FUNCTIONCALLS_H 3 | 4 | //- group: empty-free -------------------------------------------------------- 5 | void empty_call(); 6 | 7 | //- group: empty-inst -------------------------------------------------------- 8 | class EmptyCall { 9 | public: 10 | void empty_call(); 11 | }; 12 | 13 | 14 | //- group: builtin-args-free ------------------------------------------------- 15 | struct Value { int m_int; }; 16 | 17 | void take_an_int(int); 18 | void take_a_double(double); 19 | void take_a_struct(Value); 20 | 21 | //- group: builtin-args-free ------------------------------------------------- 22 | class TakeAValue { 23 | public: 24 | void take_an_int(int); 25 | void take_a_double(double); 26 | void take_a_struct(Value); 27 | 28 | int pass_int(int); 29 | }; 30 | 31 | 32 | //- group: do-work-free ------------------------------------------------------ 33 | double do_work(double); 34 | 35 | //- group: do-work-inst ------------------------------------------------------ 36 | class DoWork { 37 | public: 38 | double do_work(double); 39 | }; 40 | 41 | 42 | //- group: overload-inst ----------------------------------------------------- 43 | class OverloadedCall { 44 | public: 45 | double add_it(int a, int b); 46 | double add_it(short a); 47 | double add_it(long a); 48 | double add_it(int a, int b, int c); 49 | double add_it(double a); 50 | double add_it(float a); 51 | double add_it(int a); 52 | }; 53 | 54 | #endif // !CPPYY_FUNCTIONCALLS_H 55 | -------------------------------------------------------------------------------- /bench/functioncalls.i: -------------------------------------------------------------------------------- 1 | %module swig_functioncalls 2 | %{ 3 | #include "functioncalls.h" 4 | %} 5 | 6 | %include "functioncalls.h" 7 | -------------------------------------------------------------------------------- /bench/functioncalls.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /bench/functioncalls_main.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "functioncalls.h" 7 | 8 | 9 | static const int N = 1000000000; // 10^9, i.e. per call is in nanoseconds 10 | 11 | int cpp_loop_offset() { 12 | int i = 0; 13 | for ( ; i < N; ++i) 14 | ; 15 | return i; 16 | } 17 | 18 | //- group: empty-free -------------------------------------------------------- 19 | void cpp_free_empty_call() { 20 | for (int i=0; i < N; ++i) 21 | empty_call(); 22 | } 23 | 24 | //- group: empty-inst -------------------------------------------------------- 25 | void cpp_inst_empty_call() { 26 | EmptyCall e; 27 | for (int i=0; i < N; ++i) 28 | e.empty_call(); 29 | } 30 | 31 | 32 | //- group: builtin-args-inst ------------------------------------------------- 33 | void cpp_inst_pass_int() { 34 | TakeAValue t; 35 | for (int i=0; i < N; ++i) 36 | t.pass_int(i); 37 | } 38 | 39 | 40 | //- group: do-work ----------------------------------------------------------- 41 | void cpp_inst_do_work() { 42 | DoWork d; 43 | for (int i=0; i < N; ++i) 44 | d.do_work(i); 45 | } 46 | 47 | 48 | //- group: do-overload ------------------------------------------------------- 49 | void cpp_inst_overload() { 50 | OverloadedCall o; 51 | for (int i=0; i < N; ++i) 52 | o.add_it((float)i); 53 | } 54 | 55 | 56 | //---------------------------------------------------------------------------- 57 | void run_bench(void (*cpp_bench)(), const char* label) { 58 | clock_t t1 = clock(); 59 | cpp_loop_offset(); 60 | clock_t t2 = clock(); 61 | cpp_bench(); 62 | clock_t t3 = clock(); 63 | 64 | std::cout << label << ": " << std::setprecision(8) 65 | << ((t3-t2) - (t2-t1))/((double)CLOCKS_PER_SEC) << " nanoseconds" << std::endl; 66 | } 67 | 68 | int main() { 69 | run_bench(cpp_free_empty_call, "cpp_free_empty_call"); 70 | run_bench(cpp_inst_empty_call, "cpp_inst_empty_call"); 71 | run_bench(cpp_inst_pass_int, "cpp_inst_pass_int"); 72 | run_bench(cpp_inst_do_work, "cpp_do_work"); 73 | run_bench(cpp_inst_overload, "cpp_inst_overload"); 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /bench/py11_functioncalls.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include "functioncalls.h" 3 | 4 | 5 | namespace py = pybind11; 6 | 7 | PYBIND11_MODULE(py11_functioncalls, m) { 8 | //- group: empty-free -------------------------------------------------------- 9 | m.def("empty_call", &empty_call); 10 | 11 | //- group: empty-inst -------------------------------------------------------- 12 | py::class_(m, "EmptyCall") 13 | .def(py::init<>()) 14 | .def("empty_call", &EmptyCall::empty_call); 15 | 16 | 17 | //- group: builtin-args-free ------------------------------------------------- 18 | py::class_(m, "Value") 19 | .def(py::init<>()); 20 | 21 | m.def("take_an_int", &take_an_int); 22 | m.def("take_a_double", &take_a_double); 23 | m.def("take_a_struct", &take_a_struct); 24 | 25 | //- group: builtin-args-free ------------------------------------------------- 26 | py::class_(m, "TakeAValue") 27 | .def(py::init<>()) 28 | .def("take_an_int", &TakeAValue::take_an_int) 29 | .def("take_a_double", &TakeAValue::take_a_double) 30 | .def("take_a_struct", &TakeAValue::take_a_struct) 31 | 32 | //- group: builtin-args-pass ------------------------------------------------- 33 | .def("pass_int", &TakeAValue::pass_int); 34 | 35 | 36 | //- group: do-work-free ------------------------------------------------------ 37 | m.def("do_work", &do_work); 38 | 39 | //- group: do-work-inst ------------------------------------------------------ 40 | py::class_(m, "DoWork") 41 | .def(py::init<>()) 42 | .def("do_work", &DoWork::do_work); 43 | 44 | 45 | //- group: overload-inst ----------------------------------------------------- 46 | py::class_(m, "OverloadedCall") 47 | .def(py::init<>()) 48 | .def("add_it", (double (OverloadedCall::*)(int, int)) &OverloadedCall::add_it) 49 | .def("add_it", (double (OverloadedCall::*)(short)) &OverloadedCall::add_it) 50 | .def("add_it", (double (OverloadedCall::*)(long)) &OverloadedCall::add_it) 51 | .def("add_it", (double (OverloadedCall::*)(int, int, int))&OverloadedCall::add_it) 52 | .def("add_it", (double (OverloadedCall::*)(double)) &OverloadedCall::add_it) 53 | .def("add_it", (double (OverloadedCall::*)(float)) &OverloadedCall::add_it) 54 | .def("add_it", (double (OverloadedCall::*)(int)) &OverloadedCall::add_it); 55 | } 56 | -------------------------------------------------------------------------------- /bench/py_functioncalls.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | 4 | #- group: empty-free --------------------------------------------------------- 5 | def empty_call(): 6 | pass 7 | 8 | #- group: empty-inst --------------------------------------------------------- 9 | class EmptyCall(object): 10 | def empty_call(self): 11 | pass 12 | 13 | 14 | #- group: builtin-args-free -------------------------------------------------- 15 | class Value(object): 16 | def __init__(self): 17 | self.m_int = 42 18 | 19 | def take_an_int(val): 20 | pass 21 | 22 | def take_a_double(val): 23 | pass 24 | 25 | def take_a_struct(val): 26 | pass 27 | 28 | #- group: builtin-args-inst -------------------------------------------------- 29 | class TakeAValue(object): 30 | def take_an_int(self, val): 31 | pass 32 | 33 | def take_a_double(self, val): 34 | pass 35 | 36 | def take_a_struct(self, val): 37 | pass 38 | 39 | def pass_int(self, val): 40 | return val + 42 41 | 42 | 43 | #- group: do_work-free ------------------------------------------------------- 44 | def do_work(val): 45 | return math.atan(val) 46 | 47 | #- group: do_work-inst ------------------------------------------------------- 48 | class DoWork(object): 49 | def do_work(self, val): 50 | return math.atan(val) 51 | 52 | 53 | #- group: overload-inst ------------------------------------------------------ 54 | class OverloadedCall(object): 55 | def add_it(self, *args): 56 | return 3.1415 + sum(args) 57 | -------------------------------------------------------------------------------- /bench/py_runvector.py: -------------------------------------------------------------------------------- 1 | import array, sys 2 | 3 | N = 100000000 # 10^8 4 | 5 | 6 | #- group: stl-vector --------------------------------------------------------- 7 | looprange = range 8 | if sys.hexversion < 0x3000000: 9 | looprange = xrange 10 | global_vector = array.array('i', looprange(N)) 11 | -------------------------------------------------------------------------------- /bench/runvector.cxx: -------------------------------------------------------------------------------- 1 | #include "runvector.h" 2 | 3 | std::vector global_vector; 4 | double global_effect = 0.; 5 | 6 | static const int N = 100000000; // 10^8 7 | 8 | namespace { 9 | struct Initializer { 10 | Initializer() { 11 | global_vector.reserve(N); 12 | for (int i=0; i < N; ++i) global_vector.push_back(i); 13 | } 14 | } _init_global_vector; 15 | } 16 | -------------------------------------------------------------------------------- /bench/runvector.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern std::vector global_vector; 4 | extern double global_effect; 5 | 6 | -------------------------------------------------------------------------------- /bench/runvector.i: -------------------------------------------------------------------------------- 1 | %module swig_runvector 2 | %{ 3 | #include "runvector.h" 4 | %} 5 | 6 | %include "runvector.h" 7 | %include "std_vector.i" 8 | 9 | namespace std { 10 | %template(vectori) vector; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /bench/runvector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /bench/runvector_main.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "runvector.h" 8 | 9 | 10 | static const int N = 100000000; // 10^8, i.e. per call is in 10 nanoseconds 11 | 12 | int cpp_loop_offset() { 13 | int i = 0; 14 | for ( ; i < N; ++i) 15 | ; 16 | return i; 17 | } 18 | 19 | 20 | //- group: stl-vector -------------------------------------------------------- 21 | void cpp_runvector() { 22 | for (auto i: global_vector) 23 | global_effect += i; 24 | } 25 | 26 | 27 | //---------------------------------------------------------------------------- 28 | void run_bench(void (*cpp_bench)(), const char* label) { 29 | clock_t t1 = clock(); 30 | cpp_loop_offset(); 31 | clock_t t2 = clock(); 32 | cpp_bench(); 33 | clock_t t3 = clock(); 34 | 35 | std::cout << label << ": " << std::setprecision(8) 36 | << 10.*((t3-t2) - (t2-t1))/((double)CLOCKS_PER_SEC) << " nanoseconds" << std::endl; 37 | } 38 | 39 | int main() { 40 | run_bench(cpp_runvector, "cpp_runvector"); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /bench/support.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import py, sys, subprocess 3 | 4 | currpath = py.path.local(__file__).dirpath() 5 | 6 | 7 | def setup_make(targetname): 8 | if sys.platform == 'win32': 9 | raise OSError("win32 not supported yet") 10 | popen = subprocess.Popen(["make", targetname], cwd=str(currpath), 11 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 12 | stdout, _ = popen.communicate() 13 | if popen.returncode: 14 | raise OSError("'make' failed:\n%s" % (stdout,)) 15 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | # Defining the exact version will make sure things don't break 2 | sphinx==6.2.1 3 | sphinx_rtd_theme==1.2.2 4 | readthedocs-sphinx-search==0.3.2 5 | -------------------------------------------------------------------------------- /doc/source/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | .rst-content .toconly { 2 | display: none; 3 | } 4 | 5 | nav .toconly { 6 | display: unset; 7 | } 8 | -------------------------------------------------------------------------------- /doc/source/bugs.rst: -------------------------------------------------------------------------------- 1 | .. _bugs: 2 | 3 | Bugs and feedback 4 | ================= 5 | 6 | Please report bugs, ask questions, request improvements, and post general 7 | comments on the `issue tracker`_ or on `stack overflow`_ (marked with the 8 | "cppyy" tag). 9 | 10 | .. _`issue tracker`: https://github.com/wlav/cppyy/issues 11 | .. _`stack overflow`: https://stackoverflow.com/questions/tagged/cppyy 12 | -------------------------------------------------------------------------------- /doc/source/cppyy_features_header.rst: -------------------------------------------------------------------------------- 1 | File features.h 2 | =============== 3 | 4 | .. code-block:: c++ 5 | :linenos: 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | //----- 13 | unsigned int gUint = 0; 14 | 15 | //----- 16 | class Abstract { 17 | public: 18 | virtual ~Abstract() {} 19 | virtual std::string abstract_method() = 0; 20 | virtual void concrete_method() = 0; 21 | }; 22 | 23 | void Abstract::concrete_method() { 24 | std::cout << "called Abstract::concrete_method" << std::endl; 25 | } 26 | 27 | //----- 28 | class Concrete : Abstract { 29 | public: 30 | Concrete(int n=42) : m_int(n), m_const_int(17) {} 31 | ~Concrete() {} 32 | 33 | virtual std::string abstract_method() { 34 | return "called Concrete::abstract_method"; 35 | } 36 | 37 | virtual void concrete_method() { 38 | std::cout << "called Concrete::concrete_method" << std::endl; 39 | } 40 | 41 | void array_method(int* ad, int size) { 42 | for (int i=0; i < size; ++i) 43 | std::cout << ad[i] << ' '; 44 | std::cout << '\n'; 45 | } 46 | 47 | void array_method(double* ad, int size) { 48 | for (int i=0; i < size; ++i) 49 | std::cout << ad[i] << ' '; 50 | std::cout << '\n'; 51 | } 52 | 53 | void uint_ref_assign(unsigned int& target, unsigned int value) { 54 | target = value; 55 | } 56 | 57 | Abstract* show_autocast() { 58 | return this; 59 | } 60 | 61 | operator const char*() { 62 | return "Hello operator const char*!"; 63 | } 64 | 65 | public: 66 | double m_data[4]; 67 | int m_int; 68 | const int m_const_int; 69 | 70 | static int s_int; 71 | }; 72 | 73 | typedef Concrete Concrete_t; 74 | 75 | int Concrete::s_int = 321; 76 | 77 | void call_abstract_method(Abstract* a) { 78 | a->abstract_method(); 79 | } 80 | 81 | //----- 82 | class Abstract1 { 83 | public: 84 | virtual ~Abstract1() {} 85 | virtual std::string abstract_method1() = 0; 86 | }; 87 | 88 | class Abstract2 { 89 | public: 90 | virtual ~Abstract2() {} 91 | virtual std::string abstract_method2() = 0; 92 | }; 93 | 94 | std::string call_abstract_method1(Abstract1* a) { 95 | return a->abstract_method1(); 96 | } 97 | 98 | std::string call_abstract_method2(Abstract2* a) { 99 | return a->abstract_method2(); 100 | } 101 | 102 | //----- 103 | int global_function(int) { 104 | return 42; 105 | } 106 | 107 | double global_function(double) { 108 | return std::exp(1); 109 | } 110 | 111 | int call_int_int(int (*f)(int, int), int i1, int i2) { 112 | return f(i1, i2); 113 | } 114 | 115 | template 116 | C multiply(A a, B b) { 117 | return C{a*b}; 118 | } 119 | 120 | //----- 121 | namespace Namespace { 122 | 123 | class Concrete { 124 | public: 125 | class NestedClass { 126 | public: 127 | std::vector m_v; 128 | }; 129 | 130 | }; 131 | 132 | int global_function(int i) { 133 | return 2*::global_function(i); 134 | } 135 | 136 | double global_function(double d) { 137 | return 2*::global_function(d); 138 | } 139 | 140 | } // namespace Namespace 141 | 142 | //----- 143 | enum EFruit {kApple=78, kBanana=29, kCitrus=34}; 144 | enum class NamedClassEnum { E1 = 42 }; 145 | 146 | //----- 147 | void throw_an_error(int i); 148 | 149 | class SomeError : public std::exception { 150 | public: 151 | explicit SomeError(const std::string& msg) : fMsg(msg) {} 152 | const char* what() const throw() override { return fMsg.c_str(); } 153 | 154 | private: 155 | std::string fMsg; 156 | }; 157 | 158 | class SomeOtherError : public SomeError { 159 | public: 160 | explicit SomeOtherError(const std::string& msg) : SomeError(msg) {} 161 | SomeOtherError(const SomeOtherError& s) : SomeError(s) {} 162 | }; 163 | -------------------------------------------------------------------------------- /doc/source/cuda.rst: -------------------------------------------------------------------------------- 1 | .. _cuda: 2 | 3 | 4 | CUDA support 5 | ============ 6 | 7 | .. warning:: 8 | 9 | This is an **experimental** feature, available starting with release 10 | 2.3.0. 11 | It is still incomplete and has only been tested on Linux on x86_64. 12 | 13 | CUDA is supported by passing all JITed code through two pipelines: one for the 14 | CPU and one for the GPU. 15 | Use of the ``__CUDA__`` pre-processor macro enables more fine-grained control 16 | over which pipeline sees what, which is used e.g. in the pre-compiled header: 17 | the GPU pipeline has the CUDA headers included, the CPU pipeline does not. 18 | Building the pre-compiled header will also pick up common CUDA libraries such 19 | as cuBLAS, if installed. 20 | 21 | Each version of CUDA requires specific versions of Clang and the system 22 | compiler (e.g. gcc) for proper functioning; it's therefore best to build the 23 | backend (``cppyy-cling``) from source for the specific combination of 24 | interest. 25 | The 3.x series of cppyy uses Clang13, the 2.x series Clang9, and this may 26 | limit the CUDA versions supported (especially since CUDA has changed the APIs 27 | for launching kernels in v11). 28 | 29 | There are three environment variables to control Cling's handling of CUDA: 30 | 31 | * ``CLING_ENABLE_CUDA`` (required): set to ``1`` to enable the CUDA 32 | backend. 33 | 34 | * ``CLING_CUDA_PATH`` (optional): set to the local CUDA installation if not 35 | in a standard location. 36 | 37 | * ``CLING_CUDA_ARCH`` (optional): set the architecture to target; default is 38 | ``sm_35`` (Clang9 is limited to ``sm_75``). 39 | 40 | After enabling CUDA with ``CLING_ENABLE_CUDA=1`` CUDA code can be used and 41 | kernels can be launched from JITed code by in ``cppyy.cppdef()``. 42 | There is currently no syntax or helpers yet to launch kernels from Python. 43 | -------------------------------------------------------------------------------- /doc/source/debugging.rst: -------------------------------------------------------------------------------- 1 | .. _debugging: 2 | 3 | Debugging 4 | ========= 5 | 6 | By default, the ``clang`` JIT as used by cppyy does not generate debugging 7 | information. 8 | This is first of all because it has proven to be not reliable in all cases, 9 | but also because in a production setting this information, being internal to 10 | the wrapper generation, goes unused. 11 | However, that does mean that a debugger that starts from python will not be 12 | able to step through JITed code into the C++ function that needs debugging, 13 | even when such information is available for that C++ function. 14 | 15 | To enable debugging information in JITed code, set the ``EXTRA_CLING_ARGS`` 16 | envar to ``-g`` (and any further compiler options you need, e.g. add ``-O2`` 17 | to debug optimized code). 18 | 19 | On a crash in C++, the backend will attempt to provide a stack trace. 20 | This works quite well on Linux (through ``gdb``) and decently on MacOS 21 | (through ``unwind``), but is currently unreliable on MS Windows. 22 | To prevent printing of this trace, which can be slow to produce, set the 23 | envar ``CPPYY_CRASH_QUIET`` to '1'. 24 | 25 | It is even more useful to obtain a traceback through the Python code that led 26 | up to the problem in C++. 27 | Many modern debuggers allow mixed-mode C++/Python debugging (for example 28 | `gdb`_ and `MSVC`_), but cppyy can also turn abortive C++ signals (such as a 29 | segmentation violation) into Python exceptions, yielding a normal traceback. 30 | This is particularly useful when working with cross-inheritance and other 31 | cross-language callbacks. 32 | 33 | To enable the signals to exceptions conversion, import the lowlevel module 34 | ``cppyy.ll`` and use: 35 | 36 | .. code-block:: python 37 | 38 | import cppyy.ll 39 | cppyy.ll.set_signals_as_exception(True) 40 | 41 | Call ``set_signals_as_exception(False)`` to disable the conversion again. 42 | It is recommended to only have the conversion enabled around the problematic 43 | code, as it comes with a performance penalty. 44 | If the problem can be localized to a specific function, you can use its 45 | ``__sig2exc__`` flag to only have the conversion active in that function. 46 | Finally, for convenient scoping, you can also use: 47 | 48 | .. code-block:: python 49 | 50 | with cppyy.ll.signals_as_exception(): 51 | # crashing code goes here 52 | 53 | The translation of signals to exceptions is as follows (all of the exceptions 54 | are subclasses of ``cppyy.ll.FatalError``): 55 | 56 | ======================================== ======================================== 57 | C++ signal Python exception 58 | ======================================== ======================================== 59 | ``SIGSEGV`` ``cppyy.ll.SegmentationViolation`` 60 | ``SIGBUS`` ``cppyy.ll.BusError`` 61 | ``SIGABRT`` ``cppyy.ll.AbortSignal`` 62 | ``SIGILL`` ``cppyy.ll.IllegalInstruction`` 63 | ======================================== ======================================== 64 | 65 | As an example, consider the following cross-inheritance code that crashes 66 | with a segmentation violation in C++, because a ``nullptr`` is dereferenced: 67 | 68 | .. code-block:: python 69 | 70 | import cppyy 71 | import cppyy.ll 72 | 73 | cppyy.cppdef(""" 74 | class Base { 75 | public: 76 | virtual ~Base() {} 77 | virtual int runit() = 0; 78 | }; 79 | 80 | int callback(Base* b) { 81 | return b->runit(); 82 | } 83 | 84 | void segfault(int* i) { *i = 42; } 85 | """) 86 | 87 | class Derived(cppyy.gbl.Base): 88 | def runit(self): 89 | print("Hi, from Python!") 90 | cppyy.gbl.segfault(cppyy.nullptr) 91 | 92 | If now used with ``signals_as_exception``, e.g. like so: 93 | 94 | .. code-block:: python 95 | 96 | d = Derived() 97 | with cppyy.ll.signals_as_exception(): 98 | cppyy.gbl.callback(d) 99 | 100 | it produces the following, very informative, Python-side trace:: 101 | 102 | Traceback (most recent call last): 103 | File "crashit.py", line 25, in 104 | cppyy.gbl.callback(d) 105 | cppyy.ll.SegmentationViolation: int ::callback(Base* b) => 106 | SegmentationViolation: void ::segfault(int* i) => 107 | SegmentationViolation: segfault in C++; program state was reset 108 | 109 | whereas without, there would be no Python-side information at all. 110 | 111 | 112 | .. _`gdb`: https://wiki.python.org/moin/DebuggingWithGdb 113 | .. _`MSVC`: https://docs.microsoft.com/en-us/visualstudio/python/debugging-mixed-mode-c-cpp-python-in-visual-studio 114 | -------------------------------------------------------------------------------- /doc/source/examples.rst: -------------------------------------------------------------------------------- 1 | .. _examples: 2 | 3 | Example repos 4 | ============= 5 | 6 | The detailed feature lists have examples that work using a header file, and 7 | there is the `tutorial`_ that shows mixing of C++ and Python interactively. 8 | The `cookie cutter`_ repo provides a good cmake based example. 9 | More complete examples that show packaging include these repos: 10 | 11 | * `flatsurf`_ 12 | * `iheartia`_ 13 | * `ns-3`_ 14 | * `popsicle`_ 15 | * `ogdf-python`_ 16 | * `python-vspline`_ 17 | * `mupdf`_ 18 | * `NWChemEx`_ 19 | * `EPICpy`_ 20 | * `bgfx-python`_ 21 | * `cppyy-bbhash`_ 22 | * `dnpy`_ 23 | * `PyEtaler`_ 24 | * `gco-cppyy`_ 25 | * `gmpxxyy`_ 26 | * `cppyy-knearestneighbors`_ 27 | * `lyncs`_ 28 | * `libsemigroups_cppyy`_ 29 | * `SopraClient`_ 30 | 31 | .. _tutorial: https://github.com/wlav/cppyy/blob/master/doc/tutorial/CppyyTutorial.ipynb 32 | .. _cookie cutter: https://github.com/camillescott/cookiecutter-cppyy-cmake 33 | .. _flatsurf: https://github.com/flatsurf 34 | .. _iheartia: https://github.com/iheartla/iheartla 35 | .. _ns-3: https://www.nsnam.org 36 | .. _popsicle: https://github.com/kunitoki/popsicle 37 | .. _ogdf-python: https://github.com/N-Coder/ogdf-python 38 | .. _python-vspline: https://bitbucket.org/kfj/python-vspline 39 | .. _mupdf: https://mupdf.com/ 40 | .. _NWChemEx: https://github.com/NWChemEx-Project 41 | .. _EPICpy: https://github.com/travisseymour/EPICpy 42 | .. _bgfx-python: https://github.com/fbertola/bgfx-python 43 | .. _cppyy-bbhash: https://github.com/camillescott/cppyy-bbhash 44 | .. _dnpy: https://github.com/txjmb/dnpy 45 | .. _PyEtaler: https://github.com/etaler/PyEtaler 46 | .. _gco-cppyy: https://github.com/agoose77/gco-cppyy 47 | .. _gmpxxyy: https://github.com/flatsurf/gmpxxyy 48 | .. _cppyy-knearestneighbors: https://github.com/jclay/cppyy-knearestneighbors-example 49 | .. _lyncs: https://github.com/Lyncs-API 50 | .. _libsemigroups_cppyy: https://github.com/libsemigroups/libsemigroups_cppyy 51 | .. _SopraClient: https://github.com/SoPra-Team-17/Client 52 | -------------------------------------------------------------------------------- /doc/source/exceptions.rst: -------------------------------------------------------------------------------- 1 | .. _exceptions: 2 | 3 | 4 | Exceptions 5 | ========== 6 | 7 | All C++ exceptions are converted to Python exceptions and all Python 8 | exceptions are converted to C++ exceptions, to allow exception propagation 9 | through multiple levels of callbacks, while retaining the option to handle 10 | the outstanding exception as needed in either language. 11 | To preserve an exception across the language boundaries, it must derive from 12 | ``std::exception``. 13 | If preserving the exception (or its type) is not possible, generic exceptions 14 | are used to propagate the exception: ``Exception`` in Python or 15 | ``CPyCppyy::PyException`` in C++. 16 | 17 | In the most common case of an instance of a C++ exception class derived from 18 | ``std::exception`` that is thrown from a compiled library and which is 19 | copyable, the exception can be caught and handled like any other bound C++ 20 | object (or with ``Exception`` on the Python and ``std::exception`` on the 21 | C++ side). 22 | If the exception is not copyable, but derived from ``std::exception``, the 23 | result of its ``what()`` reported with an instance of Python's ``Exception``. 24 | In all other cases, including exceptions thrown from interpreted code (due to 25 | limitations of the Clang JIT), the exception will turn into an instance of 26 | ``Exception`` with a generic message. 27 | 28 | The standard C++ exceptions are explicitly not mapped onto standard Python 29 | exceptions, since other than a few simple cases, the mapping is too crude to 30 | be useful as the typical usage in each standard library is too different. 31 | Thus, for example, a thrown ``std::runtime_error`` instance will become a 32 | ``cppyy.gbl.std.runtime_error`` instance on the Python side (with Python's 33 | ``Exception`` as its base class), not a ``RuntimeError`` instance. 34 | 35 | The C++ code used for the examples below can be found 36 | :doc:`here `, and it is assumed that that code is 37 | loaded at the start of any session. 38 | Download it, save it under the name ``features.h``, and load it: 39 | 40 | .. code-block:: python 41 | 42 | >>> import cppyy 43 | >>> cppyy.include('features.h') 44 | >>> 45 | 46 | In addition, the examples require the ``throw`` to be in compiled code. 47 | Save the following and build it into a shared library ``libfeatures.so`` (or 48 | ``libfeatures.dll`` on MS Windows): 49 | 50 | .. code-block:: C++ 51 | 52 | #include "features.h" 53 | 54 | void throw_an_error(int i) { 55 | if (i) throw SomeError{"this is an error"}; 56 | throw SomeOtherError{"this is another error"}; 57 | } 58 | 59 | And load the resulting library: 60 | 61 | .. code-block:: python 62 | 63 | >>> cppyy.load_library('libfeatures') 64 | >>> 65 | 66 | Then try it out: 67 | 68 | .. code-block:: python 69 | 70 | >>> cppyy.gbl.throw_an_error(1) 71 | Traceback (most recent call last): 72 | File "", line 1, in 73 | cppyy.gbl.SomeError: void ::throw_an_error(int i) => 74 | SomeError: this is an error 75 | >>> 76 | 77 | Note how the full type is preserved and how the result of ``what()`` is used 78 | for printing the exception. 79 | By preserving the full C++ type, it is possible to call any other member 80 | functions the exception may provide beyond ``what`` or access any additional 81 | data it carries. 82 | 83 | To catch the exception, you can either use the full type, or any of its base 84 | classes, including ``Exception`` and ``cppyy.gbl.std.exception``: 85 | 86 | .. code-block:: python 87 | 88 | >>> try: 89 | ... cppyy.gbl.throw_an_error(0) 90 | ... except cppyy.gbl.SomeOtherError as e: # catch by exact type 91 | ... print("received:", e) 92 | ... 93 | received: 94 | >>> try: 95 | ... cppyy.gbl.throw_an_error(0) 96 | ... except Exception as e: # catch through base class 97 | ... print("received:", e) 98 | ... 99 | received: 100 | >>> 101 | 102 | -------------------------------------------------------------------------------- /doc/source/history.rst: -------------------------------------------------------------------------------- 1 | .. _history: 2 | 3 | History 4 | ======= 5 | 6 | .. toctree:: 7 | :hidden: 8 | 9 | What is now called `cppyy` started life as `RootPython` from `CERN`_, but 10 | cppyy is not associated with CERN (it is still used there, however, 11 | underpinning `PyROOT`_). 12 | 13 | Back in late 2002, Pere Mato of CERN, had the idea of using the `CINT`_ C++ 14 | interpreter, which formed the interactive interface to `ROOT`_, to call from 15 | Python into C++: this became RootPython. 16 | This binder interfaced with Python through `boost.python`_ (v1), transpiling 17 | Python code into C++ and interpreting the result with CINT. 18 | In early 2003, I ported this code to boost.python v2, then recently released. 19 | In practice, however, re-interpreting the transpiled code was unusably slow, 20 | thus I modified the code to make direct use of CINT's internal reflection 21 | system, gaining about 25x in performance. 22 | I presented this work as `PyROOT` at the ROOT Users' Workshop in early 2004, 23 | and, after removing the boost.python dependency by using the C-API directly 24 | (gaining another factor 7 in speedup!), it was included in ROOT. 25 | PyROOT was presented at the SciPy'06 conference, but was otherwise not 26 | advocated outside of High Energy Physics (HEP). 27 | 28 | In 2010, the PyPy core developers and I held a `sprint at CERN`_ to use 29 | `Reflex`, a standalone alternative to CINT's reflection of C++, to add 30 | automatic C++ bindings, PyROOT-style, to `PyPy`_. 31 | This is where the name "cppyy" originated. 32 | Coined by Carl Friedrich Bolz, if you want to understand the meaning, just 33 | pronounce it slowly: cpp-y-y. 34 | 35 | After the ROOT team replaced CINT with `Cling`_, PyROOT soon followed. 36 | As part of Google's Summer of Code '16, Aditi Dutta moved PyPy/cppyy to Cling 37 | as well, and packaged the code for use through `PyPI`_. 38 | I continued this integration with the Python eco-system by forking PyROOT, 39 | reducing its dependencies, and repackaging it as CPython/cppyy. 40 | The combined result is the current cppyy project. 41 | Mid 2018, version 1.0 was released. 42 | 43 | 44 | .. _`CERN`: https://cern.ch/ 45 | .. _`PyROOT`: https://root.cern.ch/root/htmldoc/guides/users-guide/ROOTUsersGuide.html#python-interface 46 | .. _`CINT`: https://en.wikipedia.org/wiki/CINT 47 | .. _`ROOT`: https://root.cern.ch 48 | .. _`boost.python`: https://wiki.python.org/moin/boost.python/GettingStarted 49 | .. _`sprint at CERN`: https://morepypy.blogspot.com/2010/07/cern-sprint-report-wrapping-c-libraries.html 50 | .. _`PyPy`: https://www.pypy.org/ 51 | .. _`Cling`: https://github.com/vgvassilev/cling 52 | .. _`PyPI`: https://pypi.org/ 53 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | .. cppyy documentation master file, created by 2 | sphinx-quickstart on Wed Jul 12 14:35:45 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | .. meta:: 7 | :description: cppyy: Automatic Python-C++ bindings 8 | :keywords: Python, C++, llvm, cling, binding, bindings, automatic bindings, bindings generator, cross-language inheritance, calling C++ from Python, calling Python from C++, high performance, data science 9 | 10 | cppyy: Automatic Python-C++ bindings 11 | ==================================== 12 | 13 | cppyy is an automatic, run-time, Python-C++ bindings generator, for calling 14 | C++ from Python and Python from C++. 15 | Run-time generation enables detailed specialization for higher performance, 16 | lazy loading for reduced memory use in large scale projects, Python-side 17 | cross-inheritance and callbacks for working with C++ frameworks, run-time 18 | template instantiation, automatic object downcasting, exception mapping, and 19 | interactive exploration of C++ libraries. 20 | cppyy delivers this without any language extensions, intermediate languages, 21 | or the need for boiler-plate hand-written code. 22 | For design and performance, see this `PyHPC'16 paper`_, albeit that the 23 | CPython/cppyy performance has been vastly improved since, as well as this 24 | `CAAS presentation`_. 25 | For a quick teaser, see `Jason Turner's`_ introduction video. 26 | 27 | cppyy is based on `Cling`_, the C++ interpreter, to match Python's dynamism, 28 | interactivity, and run-time behavior. 29 | Consider this session, showing dynamic, interactive, mixing of C++ and Python 30 | features (there are more examples throughout the documentation and in the 31 | `tutorial`_): 32 | 33 | .. code-block:: python 34 | 35 | >>> import cppyy 36 | >>> cppyy.cppdef(""" 37 | ... class MyClass { 38 | ... public: 39 | ... MyClass(int i) : m_data(i) {} 40 | ... virtual ~MyClass() {} 41 | ... virtual int add_int(int i) { return m_data + i; } 42 | ... int m_data; 43 | ... };""") 44 | True 45 | >>> from cppyy.gbl import MyClass 46 | >>> m = MyClass(42) 47 | >>> cppyy.cppdef(""" 48 | ... void say_hello(MyClass* m) { 49 | ... std::cout << "Hello, the number is: " << m->m_data << std::endl; 50 | ... }""") 51 | True 52 | >>> MyClass.say_hello = cppyy.gbl.say_hello 53 | >>> m.say_hello() 54 | Hello, the number is: 42 55 | >>> m.m_data = 13 56 | >>> m.say_hello() 57 | Hello, the number is: 13 58 | >>> class PyMyClass(MyClass): 59 | ... def add_int(self, i): # python side override (CPython only) 60 | ... return self.m_data + 2*i 61 | ... 62 | >>> cppyy.cppdef("int callback(MyClass* m, int i) { return m->add_int(i); }") 63 | True 64 | >>> cppyy.gbl.callback(m, 2) # calls C++ add_int 65 | 15 66 | >>> cppyy.gbl.callback(PyMyClass(1), 2) # calls Python-side override 67 | 5 68 | >>> 69 | 70 | With a modern C++ compiler having its back, cppyy is future-proof. 71 | Consider the following session using ``boost::any``, a capsule-type that 72 | allows for heterogeneous containers in C++. 73 | The `Boost`_ library is well known for its no holds barred use of modern C++ 74 | and heavy use of templates: 75 | 76 | .. code-block:: python 77 | 78 | >>> import cppyy 79 | >>> cppyy.include('boost/any.hpp') # assumes you have boost installed 80 | >>> from cppyy.gbl import std, boost 81 | >>> val = boost.any() # the capsule 82 | >>> val.__assign__(std.vector[int]()) # assign it a std::vector 83 | 84 | >>> val.type() == cppyy.typeid(std.vector[int]) # verify type 85 | True 86 | >>> extract = boost.any_cast[int](std.move(val)) # wrong cast 87 | Traceback (most recent call last): 88 | File "", line 1, in 89 | cppyy.gbl.boost.bad_any_cast: Could not instantiate any_cast: 90 | int boost::any_cast(boost::any&& operand) => 91 | wrapexcept: boost::bad_any_cast: failed conversion using boost::any_cast 92 | >>> extract = boost.any_cast[std.vector[int]](val) # correct cast 93 | >>> type(extract) is std.vector[int] 94 | True 95 | >>> extract += xrange(100) 96 | >>> len(extract) 97 | 100 98 | >>> val.__assign__(std.move(extract)) # move forced 99 | 100 | >>> len(extract) # now empty (or invalid) 101 | 0 102 | >>> extract = boost.any_cast[std.vector[int]](val) 103 | >>> list(extract) 104 | [0, 1, 2, 3, 4, 5, 6, ..., 97, 98, 99] 105 | >>> 106 | 107 | Of course, there is no reason to use Boost from Python (in fact, this example 108 | calls out for :doc:`pythonizations `), but it shows that 109 | cppyy seamlessly supports many advanced C++ features. 110 | 111 | cppyy is available for both `CPython`_ (v2 and v3) and `PyPy`_, reaching 112 | C++-like performance with the latter. 113 | It makes judicious use of precompiled headers, dynamic loading, and lazy 114 | instantiation, to support C++ programs consisting of millions of lines of 115 | code and many thousands of classes. 116 | cppyy minimizes dependencies to allow its use in distributed, heterogeneous, 117 | development environments. 118 | 119 | .. _Cling: https://github.com/vgvassilev/cling 120 | .. _tutorial: https://github.com/wlav/cppyy/blob/master/doc/tutorial/CppyyTutorial.ipynb 121 | .. _`PyHPC'16 paper`: http://wlav.web.cern.ch/wlav/Cppyy_LavrijsenDutta_PyHPC16.pdf 122 | .. _`CAAS presentation`: https://www.youtube.com/watch?v=stMD7VDWlVU 123 | .. _`Jason Turner's`: https://www.youtube.com/watch?v=TL83P77vZ1k 124 | .. _`Boost`: http://www.boost.org/ 125 | .. _`CPython`: http://python.org 126 | .. _`PyPy`: http://pypy.org 127 | 128 | 129 | .. only: not latex 130 | 131 | Contents: 132 | 133 | .. toctree:: 134 | :maxdepth: 1 135 | 136 | changelog 137 | license 138 | 139 | .. toctree:: 140 | :caption: Getting Started 141 | :maxdepth: 1 142 | 143 | installation 144 | starting 145 | examples 146 | bugs 147 | 148 | .. toctree:: 149 | :caption: Features 150 | :maxdepth: 1 151 | 152 | toplevel 153 | basic_types 154 | strings 155 | classes 156 | functions 157 | type_conversions 158 | stl 159 | exceptions 160 | python 161 | numba 162 | cuda 163 | lowlevel 164 | misc 165 | debugging 166 | 167 | .. toctree:: 168 | :caption: Redistribution 169 | :maxdepth: 1 170 | 171 | pythonizations 172 | utilities 173 | cmake_interface 174 | 175 | .. toctree:: 176 | :caption: Developers 177 | :maxdepth: 1 178 | 179 | packages 180 | repositories 181 | testing 182 | 183 | .. toctree:: 184 | :caption: Background 185 | :maxdepth: 1 186 | 187 | history 188 | philosophy 189 | 190 | 191 | Bugs and feedback 192 | ----------------- 193 | 194 | Please report bugs or requests for improvement on the `issue tracker`_. 195 | 196 | 197 | .. _`issue tracker`: https://github.com/wlav/cppyy/issues 198 | -------------------------------------------------------------------------------- /doc/source/license.rst: -------------------------------------------------------------------------------- 1 | License and copyright 2 | ===================== 3 | 4 | Copyright (c) 2017-2021, The Regents of the University of California, 5 | through Lawrence Berkeley National Laboratory (subject to receipt of 6 | any required approvals from the U.S. Dept. of Energy). All rights 7 | reserved. Redistribution and use in source and binary forms, with or 8 | without modification, are permitted provided that the following 9 | conditions are met: 10 | 11 | (1) Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | (2) Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | (3) Neither the name of the University of California, Lawrence Berkeley 17 | National Laboratory, U.S. Dept. of Energy nor the names of its contributors 18 | may be used to endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 25 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | SUCH DAMAGE. 32 | 33 | You are under no obligation whatsoever to provide any bug fixes, 34 | patches, or upgrades to the features, functionality or performance of 35 | the source code ("Enhancements") to anyone; however, if you choose to 36 | make your Enhancements available either publicly, or directly to 37 | Lawrence Berkeley National Laboratory, without imposing a separate 38 | written license agreement for such Enhancements, then you hereby grant 39 | the following license: a non-exclusive, royalty-free perpetual license 40 | to install, use, modify, prepare derivative works, incorporate into 41 | other computer software, distribute, and sublicense such Enhancements 42 | or derivative works thereof, in binary and source code form. 43 | 44 | 45 | Additional copyright holders 46 | ---------------------------- 47 | 48 | In addition to LBNL/UC Berkeley, this package contains files copyrighted by 49 | one or more of the following people and organizations, and licensed under 50 | the same conditions (except for some compatible licenses as retained in the 51 | source code): 52 | 53 | * CERN 54 | * Lucio Asnaghi 55 | * Simone Bacchio 56 | * Robert Bradshaw 57 | * Ellis Breen 58 | * Antonio Cuni 59 | * Aditi Dutta 60 | * Shaheed Haque 61 | * Jonsomi 62 | * Max Kolin 63 | * Alvaro Moran 64 | * Tarmo Pikaro 65 | * Matti Picus 66 | * Camille Scott 67 | * Toby StClere-Smithe 68 | * Stefan Wunsch 69 | 70 | Conda-forge recipes were provided by Julian Rueth and Isuru Fernando. 71 | 72 | 73 | External code 74 | ------------- 75 | 76 | The create_src_directory.py script will pull in ROOT and LLVM sources, which 77 | are licensed differently: 78 | 79 | LLVM: distributed under University of Illinois/NCSA Open Source License 80 | https://opensource.org/licenses/UoI-NCSA.php 81 | ROOT: distributed under LGPL 2.1 82 | https://root.cern.ch/license 83 | 84 | The ROOT and LLVM/Clang codes are modified/patched, as part of the build 85 | process. 86 | -------------------------------------------------------------------------------- /doc/source/packages.rst: -------------------------------------------------------------------------------- 1 | .. _packages: 2 | 3 | PyPI Packages 4 | ============= 5 | 6 | Cppyy 7 | ----- 8 | 9 | The ``cppyy`` module is a frontend (see :ref:`Package Structure 10 | `), and most of the code is elsewhere. However, it does 11 | contain the docs for all of the modules, which are built using 12 | Sphinx: http://www.sphinx-doc.org/en/stable/ and published to 13 | http://cppyy.readthedocs.io/en/latest/index.html using a webhook. To create 14 | the docs:: 15 | 16 | $ pip install sphinx_rtd_theme 17 | Collecting sphinx_rtd_theme 18 | ... 19 | Successfully installed sphinx-rtd-theme-0.2.4 20 | $ cd docs 21 | $ make html 22 | 23 | The Python code in this module supports: 24 | 25 | * Interfacing to the correct backend for CPython or PyPy. 26 | * Pythonizations (TBD) 27 | 28 | Cppyy-backend 29 | ------------- 30 | 31 | The ``cppyy-backend`` module contains two areas: 32 | 33 | * A patched copy of cling 34 | * Wrapper code 35 | 36 | 37 | Package structure 38 | ----------------- 39 | .. _package-structure: 40 | 41 | There are four PyPA packages involved in a full installation, with the 42 | following structure:: 43 | 44 | (A) _cppyy (PyPy) 45 | / \ 46 | (1) cppyy (3) cppyy-backend -- (4) cppyy-cling 47 | \ / 48 | (2) CPyCppyy (CPython) 49 | 50 | The user-facing package is always ``cppyy`` (1). 51 | It is used to select the other (versioned) required packages, based on the 52 | python interpreter for which it is being installed. 53 | 54 | Below (1) follows a bifurcation based on interpreter. 55 | This is needed for functionality and performance: for CPython, there is the 56 | CPyCppyy package (2). 57 | It is written in C++, makes use of the Python C-API, and installs as a Python 58 | extension module. 59 | For PyPy, there is the builtin module ``_cppyy`` (A). 60 | This is not a PyPA package. 61 | It is written in RPython as it needs access to low-level pointers, JIT hints, 62 | and the ``_cffi_backend`` backend module (itself builtin). 63 | 64 | Shared again across interpreters is the backend, which is split in a small 65 | wrapper (3) and a large package that contains Cling/LLVM (4). 66 | The former is still under development and expected to be updated frequently. 67 | It is small enough to download and build very quickly. 68 | The latter, however, takes a long time to build, but since it is very stable, 69 | splitting it off allows the creation of binary wheels that need updating 70 | only infrequently (expected about twice a year). 71 | 72 | All code is publicly available; see the 73 | :doc:`section on repositories `. 74 | -------------------------------------------------------------------------------- /doc/source/python.rst: -------------------------------------------------------------------------------- 1 | .. _python: 2 | 3 | 4 | Python 5 | ====== 6 | 7 | The C++ code used for the examples below can be found 8 | :doc:`here `, and it is assumed that that code is 9 | loaded at the start of any session. 10 | Download it, save it under the name ``features.h``, and load it: 11 | 12 | .. code-block:: python 13 | 14 | >>> import cppyy 15 | >>> cppyy.include('features.h') 16 | >>> 17 | 18 | 19 | `PyObject` 20 | ---------- 21 | 22 | Arguments and return types of ``PyObject*`` can be used, and passed on to 23 | CPython API calls (or through ``cpyext`` in PyPy). 24 | 25 | 26 | `Doc strings` 27 | ------------- 28 | 29 | The documentation string of a method or function contains the C++ 30 | arguments and return types of all overloads of that name, as applicable. 31 | Example: 32 | 33 | .. code-block:: python 34 | 35 | >>> from cppyy.gbl import Concrete 36 | >>> print Concrete.array_method.__doc__ 37 | void Concrete::array_method(int* ad, int size) 38 | void Concrete::array_method(double* ad, int size) 39 | >>> 40 | 41 | 42 | `Help` 43 | ------ 44 | 45 | Bound C++ class is first-class Python and can thus be inspected like any 46 | Python objects can. 47 | For example, we can ask for ``help()``: 48 | 49 | .. code-block:: python 50 | 51 | >>> help(Concrete) 52 | Help on class Concrete in module gbl: 53 | 54 | class Concrete(Abstract) 55 | | Method resolution order: 56 | | Concrete 57 | | Abstract 58 | | CPPInstance 59 | | __builtin__.object 60 | | 61 | | Methods defined here: 62 | | 63 | | __assign__(self, const Concrete&) 64 | | Concrete& Concrete::operator=(const Concrete&) 65 | | 66 | | __init__(self, *args) 67 | | Concrete::Concrete(int n = 42) 68 | | Concrete::Concrete(const Concrete&) 69 | | 70 | etc. .... 71 | 72 | -------------------------------------------------------------------------------- /doc/source/pythonizations.rst: -------------------------------------------------------------------------------- 1 | .. _pythonizations: 2 | 3 | Pythonizations 4 | ============== 5 | 6 | Automatic bindings generation mostly gets the job done, but unless a C++ 7 | library was designed with expressiveness and interactivity in mind, using it 8 | will feel stilted. 9 | Thus, if you are not the end-user of a set of bindings, it is beneficial to 10 | implement *pythonizations*. 11 | Some of these are already provided by default, e.g. for STL containers. 12 | Consider the following code, iterating over an STL map, using naked bindings 13 | (i.e. "the C++ way"): 14 | 15 | .. code-block:: python 16 | 17 | >>> from cppyy.gbl import std 18 | >>> m = std.map[int, int]() 19 | >>> for i in range(10): 20 | ... m[i] = i*2 21 | ... 22 | >>> b = m.begin() 23 | >>> while b != m.end(): 24 | ... print(b.__deref__().second, end=' ') 25 | ... b.__preinc__() 26 | ... 27 | 0 2 4 6 8 10 12 14 16 18 28 | >>> 29 | 30 | Yes, that is perfectly functional, but it is also very clunky. 31 | Contrast this to the (automatic) pythonization: 32 | 33 | .. code-block:: python 34 | 35 | >>> for key, value in m: 36 | ... print(value, end=' ') 37 | ... 38 | 0 2 4 6 8 10 12 14 16 18 39 | >>> 40 | 41 | Such a pythonization can be written completely in Python using the bound C++ 42 | methods, with no intermediate language necessary. 43 | Since it is written on abstract features, there is also only one such 44 | pythonization that works for all STL map instantiations. 45 | 46 | 47 | Python callbacks 48 | ---------------- 49 | 50 | Since bound C++ entities are fully functional Python ones, pythonization can 51 | be done explicitly in an end-user facing Python module. 52 | However, that would prevent lazy installation of pythonizations, so instead a 53 | callback mechanism is provided. 54 | 55 | A callback is a function or callable object taking two arguments: the Python 56 | proxy class to be pythonized and its C++ name. 57 | The latter is provided to allow easy filtering. 58 | This callback is then installed through ``cppyy.py.add_pythonization`` and 59 | ideally only for the relevant namespace (installing callbacks for classes in 60 | the global namespace is supported, but beware of name clashes). 61 | 62 | Pythonization is most effective of well-structured C++ libraries that have 63 | idiomatic behaviors. 64 | It is then straightforward to use Python reflection to write rules. 65 | For example, consider this callback that looks for the conventional C++ 66 | function ``GetLength`` and replaces it with Python's ``__len__``: 67 | 68 | .. code-block:: python 69 | 70 | >>> import cppyy 71 | >>> 72 | >>> def replace_getlength(klass, name): 73 | ... try: 74 | ... klass.__len__ = klass.__dict__['GetLength'] 75 | ... del klass.GetLength 76 | ... except KeyError: 77 | ... pass 78 | ... 79 | >>> cppyy.py.add_pythonization(replace_getlength, 'MyNamespace') 80 | >>> 81 | >>> cppyy.cppdef(""" 82 | ... namespace MyNamespace { 83 | ... class MyClass { 84 | ... public: 85 | ... MyClass(int i) : fInt(i) {} 86 | ... int GetLength() { return fInt; } 87 | ... 88 | ... private: 89 | ... int fInt; 90 | ... }; 91 | ... }""") 92 | True 93 | >>> m = cppyy.gbl.MyNamespace.MyClass(42) 94 | >>> len(m) 95 | 42 96 | >>> m.GetLength() 97 | Traceback (most recent call last): 98 | File "", line 1, in 99 | AttributeError: 'MyClass' object has no attribute 'GetLength' 100 | >>> 101 | 102 | The deletion of ``GetLength`` method with ``del`` can be omitted 103 | if both ``MyClass.GetLength`` and ``MyClass.__len__`` should be valid. 104 | 105 | C++ callbacks 106 | ------------- 107 | 108 | If you are familiar with the Python C-API, it may sometimes be beneficial to 109 | add unique optimizations to your C++ classes to be picked up by the 110 | pythonization layer. 111 | There are two conventional function that cppyy will look for (no registration 112 | of callbacks needed): 113 | 114 | .. code-block:: C++ 115 | 116 | static void __cppyy_explicit_pythonize__(PyObject* klass, const std::string&); 117 | 118 | which is called *only* for the class that declares it. 119 | And: 120 | 121 | .. code-block:: C++ 122 | 123 | static void __cppyy_pythonize__(PyObject* klass, const std::string&); 124 | 125 | which is also called for all derived classes. 126 | 127 | Just as with the Python callbacks, the first argument will be the Python 128 | class proxy, the second the C++ name, for easy filtering. 129 | When called, cppyy will be completely finished with the class proxy, so any 130 | and all changes are fair game, including the low-level ones such as the replacement of 131 | iteration or buffer protocols. 132 | 133 | An example pythonization replacing ``MyClass.GetLength`` method with Python's ``__len__`` 134 | done with the C++ callbacks: 135 | 136 | .. code-block:: python 137 | 138 | >>> import cppyy 139 | >>> 140 | >>> cppyy.cppdef(""" 141 | ... #include 142 | ... 143 | ... namespace MyNamespace { 144 | ... class MyClass { 145 | ... public: 146 | ... MyClass(int i) : fInt(i) {} 147 | ... int GetLength() { return fInt; } 148 | ... 149 | ... private: 150 | ... int fInt; 151 | ... 152 | ... // pythonizations 153 | ... public: 154 | ... static void __cppyy_pythonize__(PyObject* klass, const std::string&){ 155 | ... auto cppName = "GetLength"; 156 | ... auto pythonizationName = "__len__"; 157 | ... auto* methodObject = PyObject_GetAttrString(klass, cppName); 158 | ... PyObject_SetAttrString(klass, pythonizationName, methodObject); 159 | ... Py_DECREF(methodObject); 160 | ... PyObject_DelAttrString(klass, cppName); 161 | ... } 162 | ... }; 163 | ... }""") 164 | True 165 | >>> m = cppyy.gbl.MyNamespace.MyClass(42) 166 | >>> len(m) 167 | 42 168 | >>> m.GetLength() 169 | Traceback (most recent call last): 170 | File "", line 1, in 171 | AttributeError: 'MyClass' object has no attribute 'GetLength' 172 | >>> 173 | -------------------------------------------------------------------------------- /doc/source/starting.rst: -------------------------------------------------------------------------------- 1 | .. _starting: 2 | 3 | Trying it out 4 | ============= 5 | 6 | This is a basic guide to try cppyy and see whether it works for you. 7 | Large code bases will benefit from more advanced features such as 8 | :doc:`pythonizations ` for a cleaner interface to clients; 9 | precompiled modules for faster parsing and reduced memory usage; 10 | ":ref:`dictionaries `" to package locations and manage 11 | dependencies; and mapping files for automatic, lazy, loading. 12 | You can, however, get very far with just the basics and it may even be 13 | completely sufficient for small packages with fewer classes. 14 | 15 | cppyy works by parsing C++ definitions through ``cling``, generating tiny 16 | wrapper codes to honor compile-time features and create standardized 17 | interfaces, then compiling/linking those wrappers with the ``clang`` JIT. 18 | It thus requires only those two ingredients: *C++ definitions* and 19 | *linker symbols*. 20 | All cppyy uses, the basic and the more advanced, are variations on the 21 | theme of bringing these two together at the point of use. 22 | 23 | Definitions typically live in header files and symbols in libraries. 24 | Headers can be loaded with ``cppyy.include`` and libraries with the 25 | ``cppyy.load_library`` call. 26 | Loading the header is sufficient to start exploring, with ``cppyy.gbl`` the 27 | starting point of all things C++, while the linker symbols are only needed at 28 | the point of first use. 29 | 30 | Here is an example using the `zlib`_ library, which is likely available on 31 | your system: 32 | 33 | .. code-block:: python 34 | 35 | >>> import cppyy 36 | >>> cppyy.include('zlib.h') # bring in C++ definitions 37 | >>> cppyy.load_library('libz') # load linker symbols 38 | >>> cppyy.gbl.zlibVersion() # use a zlib API 39 | '1.2.11' 40 | >>> 41 | 42 | Since header files can include other header files, it is easy to aggregate 43 | all relevant ones into a single header to include. 44 | If there are project-specific include paths, you can add those paths through 45 | ``cppyy.add_include_path``. 46 | If a header is C-only and not set for use with C++, use ``cppyy.c_include``, 47 | which adds ``extern "C"`` around the header. 48 | 49 | Library files can be aggregated by linking all relevant ones to a single 50 | library to load. 51 | Using the linker for this purpose allows regular system features such as 52 | ``rpath`` and envars such as ``LD_LIBRARY_PATH`` to be applied as usual. 53 | Note that any mechanism that exposes the library symbols will work. 54 | For example, you could also use the standard module ``ctypes`` through 55 | ``ctypes.CDLL`` with the ``ctypes.RTLD_GLOBAL`` option. 56 | 57 | To explore, start from ``cppyy.gbl`` to access your namespaces, classes, 58 | functions, etc., etc. directly; or use python's ``dir`` (or tab-completion) 59 | to see what is available. 60 | Use python's ``help`` to see list the methods and data members of classes and 61 | see the interfaces of functions. 62 | 63 | Now try this out for some of your own headers, libraries, and APIs! 64 | 65 | .. _`zlib`: https://en.wikipedia.org/wiki/Zlib 66 | -------------------------------------------------------------------------------- /doc/source/strings.rst: -------------------------------------------------------------------------------- 1 | .. _strings: 2 | 3 | 4 | Strings/Unicode 5 | =============== 6 | 7 | Both Python and C++ have core types to represent text and these are expected 8 | to be freely interchangeable. 9 | ``cppyy`` makes it easy to do just that for the most common cases, while 10 | allowing customization where necessary to cover the full range of diverse use 11 | cases (such as different codecs). 12 | In addition to these core types, there is a range of other character types, 13 | from ``const char*`` and ``std::wstring`` to ``bytes``, that see much less 14 | use, but are also fully supported. 15 | 16 | 17 | `std::string` 18 | """"""""""""" 19 | 20 | The C++ core type ``std::string`` is considered the equivalent of Python's 21 | ``str``, even as purely implementation-wise, it is more akin to ``bytes``: 22 | as a practical matter, a C++ programmer would use ``std::string`` where a 23 | Python developer would use ``str`` (and vice versa), not ``bytes``. 24 | 25 | A Python ``str`` is unicode, however, whereas an ``std::string`` is character 26 | based, thus conversions require encoding or decoding. 27 | To allow for different encodings, ``cppyy`` defers implicit conversions 28 | between the two types until forced, at which point it will default to seeing 29 | ``std::string`` as ASCII based and ``str`` to use the UTF-8 codec. 30 | To support this, the bound ``std::string`` has been pythonized to allow it to 31 | be a drop-in for a range of uses as appropriate within the local context. 32 | 33 | In particular, it is sometimes necessary (e.g. for function arguments that 34 | take a non-const reference or a pointer to non-const ``std::string`` 35 | variables), to use an actual ``std::string`` instance to allow in-place 36 | modifications. 37 | The pythonizations then allow their use where ``str`` is expected. 38 | For example: 39 | 40 | .. code-block:: python 41 | 42 | >>> cppyy.cppexec("std::string gs;") 43 | True 44 | >>> cppyy.gbl.gs = "hello" 45 | >>> type(cppyy.gbl.gs) # C++ std::string type 46 | 47 | >>> d = {"hello": 42} # dict filled with str 48 | >>> d[cppyy.gbl.gs] # drop-in use of std::string -> str 49 | 42 50 | >>> 51 | 52 | To handle codecs other than UTF-8, the ``std::string`` pythonization adds a 53 | ``decode`` method, with the same signature as the equivalent method of 54 | ``bytes``. 55 | If it is known that a specific C++ function always returns an ``std::string`` 56 | representing unicode with a codec other than UTF-8, it can in turn be 57 | explicitly pythonized to do the conversion with that codec. 58 | 59 | 60 | `std::string_view` 61 | """""""""""""""""" 62 | 63 | It is possible to construct a (char-based) ``std::string_view`` from a Python 64 | ``str``, but it requires the unicode object to be encoded and by default, 65 | UTF-8 is chosen. 66 | This will give the expected result if all characters in the ``str`` are from 67 | the ASCII set, but otherwise it is recommend to encode on the Python side and 68 | pass the resulting ``bytes`` object instead. 69 | 70 | 71 | `std::wstring` 72 | """""""""""""" 73 | 74 | C++'s "wide" string, ``std::wstring``, is based on ``wchar_t``, a character 75 | type that is not particularly portable as it can be 2 or 4 bytes in size, 76 | depending on the platform. 77 | cppyy supports ``std::wstring`` directly, using the ``wchar_t`` array 78 | conversions provided by Python's C-API. 79 | 80 | 81 | `const char*` 82 | """"""""""""" 83 | 84 | The C representation of text, ``const char*``, is problematic for two 85 | reasons: it does not express ownership; and its length is implicit, namely up 86 | to the first occurrence of ``'\0'``. 87 | The first can, up to an extent, be ameliorated: there are a range of cases 88 | where ownership can be inferred. 89 | In particular, if the C string is set from a Python ``str``, it is the latter 90 | that owns the memory and the bound proxy of the former that in turn owns the 91 | (unconverted) ``str`` instance. 92 | However, if the ``const char*``'s memory is allocated in C/C++, memory 93 | management is by necessity fully manual. 94 | Length, on the other hand, can only be known in the case of a fixed array. 95 | However even then, the more common case is to use the fixed array as a 96 | buffer, with the actual string still only extending up to the ``'\0'`` char, 97 | so that is assumed. 98 | (C++'s ``std::string`` suffers from none of these issues and should always be 99 | preferred when you have a choice.) 100 | 101 | 102 | `char*` 103 | """"""" 104 | 105 | The C representation of a character array, ``char*``, has all the problems of 106 | ``const char*``, but in addition is often used as "data array of 8-bit int". 107 | 108 | 109 | `character types` 110 | """"""""""""""""" 111 | 112 | cppyy directly supports the following character types, both as single 113 | variables and in array form: ``char``, ``signed char``, ``unsigned char``, 114 | ``wchar_t``, ``char16_t``, and ``char32_t``. 115 | 116 | -------------------------------------------------------------------------------- /doc/source/testing.rst: -------------------------------------------------------------------------------- 1 | .. _testing: 2 | 3 | 4 | Test suite 5 | ========== 6 | 7 | The cppyy tests live in the top-level cppyy package, can be run for 8 | both CPython and PyPy, and exercises the full setup, including the backend. 9 | Most tests are standalone and can be run independently, with a few exceptions 10 | in the template tests (see file ``test_templates.py``). 11 | 12 | To run the tests, first install cppyy by any usual means, then clone the 13 | cppyy repo, and enter the ``test`` directory:: 14 | 15 | $ git clone https://github.com/wlav/cppyy.git 16 | $ cd cppyy/test 17 | 18 | Next, build the dictionaries, the manner of which depends on your platform. 19 | On Linux or MacOS-X, run ``make``:: 20 | 21 | $ make all 22 | 23 | On Windows, run the dictionary building script:: 24 | 25 | $ python make_dict_win32.py all 26 | 27 | Next, make sure you have `pytest`_ installed, for example with ``pip``:: 28 | 29 | $ python -m pip install pytest 30 | 31 | and finally run the tests:: 32 | 33 | $ python -m pytest -sv 34 | 35 | On Linux and MacOS-X, all tests should succeed. 36 | On MS Windows 32bit there are 4 failing tests, on 64bit there are 5 still 37 | failing. 38 | 39 | 40 | .. _`pytest`: https://docs.pytest.org/en/latest/ 41 | -------------------------------------------------------------------------------- /doc/source/toplevel.rst: -------------------------------------------------------------------------------- 1 | .. _toplevel: 2 | 3 | 4 | Top Level 5 | ========= 6 | 7 | cppyy provides a couple of helper functions at the module level that provide 8 | (direct) access to the Cling interpreter (any C++ code is always accessed 9 | through the global namespace ``cppyy.gbl``). 10 | The documentation makes use of these helpers throughout, so they are listed 11 | here first, but their documentation is more conveniently accessible through 12 | the Python interpreter itself, using the ``help()`` function:: 13 | 14 | $ python 15 | >>> import cppyy 16 | >>> help(cppyy) 17 | 18 | 19 | `Loading C++` 20 | ------------- 21 | 22 | C++ code can be loaded as text to be JITed, or be compiled ahead of time and 23 | supplied in the form of a shared library. 24 | In the latter case, C++ headers need to be loaded as well to declare 25 | classes, functions, and variables to Cling. 26 | Instead of headers, pre-compiled code can be used; in particular all of the 27 | standard C++ headers and several system headers are pre-compiled at startup. 28 | cppyy provides the following helpers to load C++ code: 29 | 30 | * ``cppdef``: direct access to the interpreter. 31 | This function accepts C++ declarations as a string and JITs them (bindings 32 | are not created until actual use). 33 | The code is loaded into the global scope, thus any previously loaded code 34 | is available from one ``cppdef`` call to the next, as are all standard 35 | C++ headers that have been loaded through pre-compiled headers. 36 | Example:: 37 | 38 | >>> cppyy.cppdef(r"""\ 39 | ... void hello() { 40 | ... std::cout << "Hello, World!" << std::endl; 41 | ... }""") 42 | True 43 | >>> cppyy.gbl.hello() 44 | Hello, World! 45 | >>> 46 | 47 | * ``cppexec``: direct access to the interpreter. 48 | This function accepts C++ statements as a string, JITs and executes them. 49 | Just like ``cppdef``, execution is in the global scope and all previously 50 | loaded code is available. 51 | If the statements are declarations, the effect is the same as ``cppdef``, 52 | but ``cppexec`` also accepts executable lines. 53 | Example:: 54 | 55 | >>> cppyy.cppexec(r"""std::string hello = "Hello, World!";""") 56 | True 57 | >>> cppyy.cppexec("std::cout << hello << std::endl;") 58 | Hello, World! 59 | True 60 | >>> 61 | 62 | * ``include``: load declarations into the interpreter. 63 | This function accepts C++ declarations from a file, typically a header. 64 | Files are located through include paths given to the Cling. 65 | Example:: 66 | 67 | >>> cppyy.include("vector") # equivalent to "#include " 68 | True 69 | >>> 70 | 71 | * ``c_include``: load declarations into the interpreter. 72 | This function accepts C++ declarations from a file, typically a header. 73 | Name mangling is an important difference between C and C++ code. 74 | The use of ``c_include`` instead of ``include`` prevents mangling. 75 | 76 | * ``load_library``: load compiled C++ into the interpreter. 77 | This function takes the name of a shared library and loads it into current 78 | process, exposing all external symbols to Cling. 79 | Libraries are located through load paths given to Cling, either through the 80 | "-L" compiler flag or the dynamic search path environment variable (system 81 | dependent). 82 | Any method that brings symbols into the process (including normal linking, 83 | e.g. when embedding Python in a C++ application) is suitable to expose 84 | symbols. 85 | An alternative for ``load_library`` is for example ``ctypes.CDLL``, but 86 | that function does not respect dynamic load paths on all platforms. 87 | 88 | If a compilation error occurs during JITing of C++ code in any of the above 89 | helpers, a Python ``SyntaxError`` exception is raised. 90 | If a compilation warning occurs, a Python warning is issued. 91 | 92 | 93 | `Configuring Cling` 94 | ------------------- 95 | 96 | It is often convenient to add additional search paths for Cling to find 97 | headers and libraries when loading a module (Python does not have standard 98 | locations to place headers and libraries, but their locations can usually 99 | be inferred from the location of the module, i.e. it's ``__file__`` 100 | attribute). 101 | cppyy provides the following two helpers: 102 | 103 | * ``add_include_path``: add additional paths for Cling to look for headers. 104 | 105 | * ``add_library_path``: add additional paths for Cling to look for libraries. 106 | 107 | Both functions accept either a string (a single path) or a list (for adding 108 | multiple paths). 109 | Paths are allowed to be relative, but absolute paths are recommended. 110 | 111 | 112 | `C++ language` 113 | -------------- 114 | 115 | Some C++ compilation-time features have no Python equivalent. 116 | Instead, convenience functions are provided: 117 | 118 | * ``sizeof``: takes a proxied C++ type or its name as a string and returns 119 | the storage size (in units of ``char``). 120 | 121 | * ``typeid``: takes a proxied C++ type or its name as a string and returns 122 | the the C++ runtime type information (RTTI). 123 | 124 | * ``nullptr``: C++ ``NULL``. 125 | 126 | 127 | `Preprocessor` 128 | -------------- 129 | 130 | Preprocessor macro's (``#define``) are not available on the Python side, 131 | because there is no type information available for them. 132 | They are, however, often used for constant data (e.g. flags or numbers; note 133 | that modern C++ recommends the use of ``const`` and ``constexpr`` instead). 134 | Within limits, macro's representing constant data are accessible through the 135 | ``macro`` helper function. 136 | Example:: 137 | 138 | >>> import cppyy 139 | >>> cppyy.cppdef('#define HELLO "Hello, World!"') 140 | True 141 | >>> cppyy.macro("HELLO") 142 | 'Hello, World!' 143 | >>> 144 | 145 | -------------------------------------------------------------------------------- /doc/source/type_conversions.rst: -------------------------------------------------------------------------------- 1 | .. _type_conversions: 2 | 3 | 4 | Type conversions 5 | ================ 6 | 7 | Most type conversions are done automatically, e.g. between Python ``str`` 8 | and C++ ``std::string`` and ``const char*``, but low-level APIs exist to 9 | perform explicit conversions. 10 | 11 | The C++ code used for the examples below can be found 12 | :doc:`here `, and it is assumed that that code is 13 | loaded at the start of any session. 14 | Download it, save it under the name ``features.h``, and load it: 15 | 16 | .. code-block:: python 17 | 18 | >>> import cppyy 19 | >>> cppyy.include('features.h') 20 | >>> 21 | 22 | 23 | .. _sec-auto-casting-label: 24 | 25 | `Auto-casting` 26 | -------------- 27 | 28 | Object pointer returns from functions provide the most derived class known 29 | (i.e. exposed in header files) in the hierarchy of the object being returned. 30 | This is important to preserve object identity as well as to make casting, 31 | a pure C++ feature after all, superfluous. 32 | Example: 33 | 34 | .. code-block:: python 35 | 36 | >>> from cppyy.gbl import Abstract, Concrete 37 | >>> c = Concrete() 38 | >>> Concrete.show_autocast.__doc__ 39 | 'Abstract* Concrete::show_autocast()' 40 | >>> d = c.show_autocast() 41 | >>> type(d) 42 | 43 | >>> 44 | 45 | As a consequence, if your C++ classes should only be used through their 46 | interfaces, then no bindings should be provided to the concrete classes 47 | (e.g. by excluding them using a :ref:`selection file `). 48 | Otherwise, more functionality will be available in Python than in C++. 49 | 50 | Sometimes, however, full control over a cast is needed. 51 | For example, if the instance is bound by another tool or even a 3rd party, 52 | hand-written, extension library. 53 | Assuming the object supports the ``PyCapsule`` or ``CObject`` abstraction, 54 | then a C++-style reinterpret_cast (i.e. without implicitly taking offsets 55 | into account), can be done by taking and rebinding the address of an 56 | object: 57 | 58 | .. code-block:: python 59 | 60 | >>> from cppyy import addressof, bind_object 61 | >>> e = bind_object(addressof(d), Abstract) 62 | >>> type(e) 63 | 64 | >>> 65 | 66 | 67 | `Operators` 68 | ----------- 69 | 70 | If conversion operators are defined in the C++ class and a Python equivalent 71 | exists (i.e. all builtin integer and floating point types, as well as 72 | ``bool``), then these will map onto those Python conversions. 73 | Note that ``char*`` is mapped onto ``__str__``. 74 | Example: 75 | 76 | .. code-block:: python 77 | 78 | >>> from cppyy.gbl import Concrete 79 | >>> print(Concrete()) 80 | Hello operator const char*! 81 | >>> 82 | 83 | C++ code can overload conversion operators by providing methods in a class or 84 | global functions. 85 | Special care needs to be taken for the latter: first, make sure that they are 86 | actually available in some header file. 87 | Second, make sure that headers are loaded in the desired order. 88 | I.e. that these global overloads are available before use. 89 | 90 | -------------------------------------------------------------------------------- /installer/cppyy_monkey_patch.py: -------------------------------------------------------------------------------- 1 | # monkey patch to be able to select a specific backend based on PyPy's version, 2 | # which is not possible in the pyproject.toml file as there is currently no 3 | # marker for it (this may change, after which this file can be removed) 4 | 5 | try: 6 | # _BACKEND is the primary, __legacy__ the backwards compatible backend 7 | from setuptools.build_meta import _BACKEND 8 | main = _BACKEND 9 | except (NameError, ImportError): 10 | # fallback as the name __legacy__ is actually documented (and part of __all__) 11 | main = __legacy__ 12 | 13 | # the following ensures proper build/installation order, after which the normal 14 | # install through setup.py picks up their wheels from the cache (TODO: note the 15 | # duplication here with setup.py; find a better way) 16 | _get_requires_for_build_wheel = main.get_requires_for_build_wheel 17 | def get_requires_for_build_wheel(*args, **kwds): 18 | try: 19 | import __pypy__, sys 20 | version = sys.pypy_version_info 21 | requirements = ['cppyy-cling==6.32.8'] 22 | if version[0] == 5: 23 | if version[1] <= 9: 24 | requirements = ['cppyy-cling<6.12'] 25 | elif version[1] <= 10: 26 | requirements = ['cppyy-cling<=6.15'] 27 | elif version[0] == 6: 28 | if version[1] <= 0: 29 | requirements = ['cppyy-cling<=6.15'] 30 | elif version[0] == 7: 31 | if version[1] <= 3 and version[2] <= 3: 32 | requirements = ['cppyy-cling<=6.18.2.3'] 33 | except ImportError: 34 | # CPython 35 | requirements = ['cppyy-backend==1.15.3', 'cppyy-cling==6.32.8'] 36 | 37 | return requirements + _get_requires_for_build_wheel(*args, **kwds) 38 | 39 | main.get_requires_for_build_wheel = get_requires_for_build_wheel 40 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | backend-path = ["installer"] 4 | build-backend = "cppyy_monkey_patch:main" 5 | -------------------------------------------------------------------------------- /python/cppyy/__pyinstaller/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | __all__ = ['get_hook_dirs'] 4 | 5 | def get_hook_dirs(): 6 | return [os.path.dirname(__file__)] 7 | -------------------------------------------------------------------------------- /python/cppyy/__pyinstaller/hook-cppyy.py: -------------------------------------------------------------------------------- 1 | # PyInstaller hooks to declare the "data files" (libraries, headers, etc.) of 2 | # cppyy-backend. Placed here rather then in cppyy-backend to guarantee that any 3 | # packaging of top-level "cppyy" picks up the backend as well. 4 | # 5 | # See also setup.cfg. 6 | 7 | __all__ = ['datas'] 8 | 9 | 10 | def _backend_files(): 11 | import cppyy_backend, glob, os 12 | 13 | all_files = glob.glob(os.path.join( 14 | os.path.dirname(cppyy_backend.__file__), '*')) 15 | 16 | def datafile(path): 17 | return path, os.path.join('cppyy_backend', os.path.basename(path)) 18 | 19 | return [datafile(filename) for filename in all_files if os.path.isdir(filename)] 20 | 21 | def _api_files(): 22 | import cppyy, os 23 | 24 | paths = str(cppyy.gbl.gInterpreter.GetIncludePath()).split('-I') 25 | for p in paths: 26 | if not p: continue 27 | 28 | apipath = os.path.join(p.strip()[1:-1], 'CPyCppyy') 29 | if os.path.exists(apipath): 30 | return [(apipath, os.path.join('include', 'CPyCppyy'))] 31 | 32 | return [] 33 | 34 | datas = _backend_files()+_api_files() 35 | -------------------------------------------------------------------------------- /python/cppyy/_pypy_cppyy.py: -------------------------------------------------------------------------------- 1 | """ PyPy-specific touch-ups 2 | """ 3 | 4 | from . import _stdcpp_fix 5 | 6 | import os 7 | import sys 8 | from cppyy_backend import loader 9 | 10 | __all__ = [ 11 | 'gbl', 12 | 'addressof', 13 | 'bind_object', 14 | 'nullptr', 15 | ] 16 | 17 | # first load the dependency libraries of the backend, then 18 | # pull in the built-in low-level cppyy 19 | try: 20 | c = loader.load_cpp_backend() 21 | except RuntimeError: 22 | import sysconfig 23 | os.environ['CPPYY_BACKEND_LIBRARY'] = "libcppyy_backend"+sysconfig.get_config_var("SO") 24 | c = loader.load_cpp_backend() 25 | os.environ['CPPYY_BACKEND_LIBRARY'] = c._name 26 | 27 | # some older versions can be fixed up through a compatibility 28 | # module on the python side; load it, if available 29 | try: 30 | import cppyy_compat 31 | except ImportError: 32 | pass 33 | 34 | import _cppyy as _backend # built-in module 35 | try: 36 | _backend._post_import_startup() 37 | except AttributeError: 38 | pass 39 | _backend._cpp_backend = c 40 | 41 | def fixup_legacy(): 42 | version = sys.pypy_version_info 43 | if version[0] < 7 or (version[0] == 7 and version[1] <= 3 and version[2] <= 3): 44 | _backend.gbl.CppyyLegacy = _backend.gbl 45 | fixup_legacy() 46 | del fixup_legacy 47 | 48 | 49 | #- exports ------------------------------------------------------------------- 50 | _thismodule = sys.modules[__name__] 51 | for name in __all__: 52 | try: 53 | setattr(_thismodule, name, getattr(_backend, name)) 54 | except AttributeError: 55 | pass 56 | del name 57 | nullptr = _backend.nullptr 58 | 59 | def load_reflection_info(name): 60 | sc = _backend.gbl.gSystem.Load(name) 61 | if sc == -1: 62 | raise RuntimeError("Unable to load reflection library "+name) 63 | 64 | def _begin_capture_stderr(): 65 | pass 66 | 67 | def _end_capture_stderr(): 68 | return "" 69 | 70 | # add other exports to all 71 | __all__.append('load_reflection_info') 72 | __all__.append('_backend') 73 | __all__.append('_begin_capture_stderr') 74 | __all__.append('_end_capture_stderr') 75 | -------------------------------------------------------------------------------- /python/cppyy/_stdcpp_fix.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | # It may be that the interpreter (whether python or pypy-c) was not linked 4 | # with C++; force its loading before doing anything else (note that not 5 | # linking with C++ spells trouble anyway for any C++ libraries ...) 6 | if 'linux' in sys.platform and 'GCC' in sys.version: 7 | # TODO: check executable to see whether linking indeed didn't happen 8 | import ctypes 9 | try: 10 | stdcpp = ctypes.CDLL('libstdc++.so', ctypes.RTLD_GLOBAL) 11 | except Exception: 12 | pass 13 | # TODO: what if Linux/clang and what if Mac? 14 | -------------------------------------------------------------------------------- /python/cppyy/_typemap.py: -------------------------------------------------------------------------------- 1 | """ Externally provided types: get looked up if all else fails, e.g. 2 | for typedef-ed C++ builtin types. 3 | """ 4 | 5 | import ctypes 6 | import sys 7 | import types 8 | 9 | def _create_mapper(cls, extra_dct=None): 10 | def mapper(name, scope): 11 | if scope: 12 | cppname = scope+'::'+name 13 | modname = 'cppyy.gbl.'+scope 14 | else: 15 | cppname = name 16 | modname = 'cppyy.gbl' 17 | dct = {'__cpp_name__' : cppname, '__module__' : modname} 18 | if extra_dct: 19 | dct.update(extra_dct) 20 | return type(name, (cls,), dct) 21 | return mapper 22 | 23 | # from six.py --- 24 | # Copyright (c) 2010-2017 Benjamin Peterson 25 | # 26 | # Permission is hereby granted, free of charge, to any person obtaining a copy 27 | # of this software and associated documentation files (the "Software"), to deal 28 | # in the Software without restriction, including without limitation the rights 29 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 30 | # copies of the Software, and to permit persons to whom the Software is 31 | # furnished to do so, subject to the following conditions: 32 | # 33 | # The above copyright notice and this permission notice shall be included in all 34 | # copies or substantial portions of the Software. 35 | # 36 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 37 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 38 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 39 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 40 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 41 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 42 | # SOFTWARE. 43 | 44 | def with_metaclass(meta, *bases): 45 | """Create a base class with a metaclass.""" 46 | # This requires a bit of explanation: the basic idea is to make a dummy 47 | # metaclass for one level of class instantiation that replaces itself with 48 | # the actual metaclass. 49 | class metaclass(type): 50 | 51 | def __new__(cls, name, this_bases, d): 52 | if sys.version_info[:2] >= (3, 7): 53 | # This version introduced PEP 560 that requires a bit 54 | # of extra care (we mimic what is done by __build_class__). 55 | resolved_bases = types.resolve_bases(bases) 56 | if resolved_bases is not bases: 57 | d['__orig_bases__'] = bases 58 | else: 59 | resolved_bases = bases 60 | return meta(name, resolved_bases, d) 61 | 62 | @classmethod 63 | def __prepare__(cls, name, this_bases): 64 | return meta.__prepare__(name, bases) 65 | return type.__new__(metaclass, 'temporary_class', (), {}) 66 | # --- end from six.py 67 | 68 | class _BoolMeta(type): 69 | def __call__(cls, val = bool()): 70 | if val: 71 | return True 72 | return False 73 | 74 | class _Bool(with_metaclass(_BoolMeta, object)): 75 | pass 76 | 77 | 78 | def initialize(backend): 79 | if not hasattr(backend, 'type_map'): 80 | return 81 | 82 | tm = backend.type_map 83 | 84 | # boolean type (builtin type bool can nog be subclassed) 85 | tm['bool'] = _create_mapper(_Bool) 86 | 87 | # char types 88 | str_tm = _create_mapper(str) 89 | for tp in ['char', 'unsigned char', 'signed char']: 90 | tm[tp] = str_tm 91 | if sys.hexversion < 0x3000000: 92 | tm['wchar_t'] = _create_mapper(unicode) 93 | else: 94 | tm['wchar_t'] = str_tm 95 | 96 | # integer types 97 | int_tm = _create_mapper(int) 98 | for tp in ['int8_t', 'uint8_t', 'short', 'unsigned short', 'int']: 99 | tm[tp] = int_tm 100 | 101 | if sys.hexversion < 0x3000000: 102 | long_tm = _create_mapper(long) 103 | else: 104 | long_tm = tm['int'] 105 | for tp in ['unsigned int', 'long', 'unsigned long', 'long long', 'unsigned long long']: 106 | tm[tp] = long_tm 107 | 108 | # floating point types 109 | float_tm = _create_mapper(float) 110 | for tp in ['float', 'double', 'long double']: 111 | tm[tp] = float_tm 112 | 113 | # void* 114 | def voidp_init(self, arg=0): 115 | import cppyy, ctypes 116 | if arg == cppyy.nullptr: arg = 0 117 | ctypes.c_void_p.__init__(self, arg) 118 | tm['void*'] = _create_mapper(ctypes.c_void_p, {'__init__' : voidp_init}) 119 | -------------------------------------------------------------------------------- /python/cppyy/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '3.5.0' 2 | -------------------------------------------------------------------------------- /python/cppyy/interactive.py: -------------------------------------------------------------------------------- 1 | import sys 2 | try: 3 | import __pypy__ 4 | del __pypy__ 5 | ispypy = True 6 | except ImportError: 7 | ispypy = False 8 | 9 | 10 | #- fake namespace for interactive lazy lookups ------------------------------- 11 | class InteractiveLazy(object): 12 | def __init__(self, hook_okay): 13 | self._hook_okay = hook_okay 14 | 15 | def __getattr__(self, attr): 16 | import cppyy 17 | 18 | if attr == '__all__': 19 | # copy all exported items from cppyy itself 20 | for v in cppyy.__all__: 21 | self.__dict__[v] = getattr(cppyy, v) 22 | 23 | # add the lookup hook into cppyy.gbl if legal, or put it under 'g' 24 | # if not (PyPy and IPython for now) 25 | if self._hook_okay: 26 | caller = sys.modules[sys._getframe(1).f_globals['__name__']] 27 | cppyy._backend._set_cpp_lazy_lookup(caller.__dict__) 28 | return cppyy.__all__ 29 | self.__dict__['g'] = cppyy.gbl 30 | self.__dict__['std'] = cppyy.gbl.std 31 | return ['g', 'std']+cppyy.__all__ 32 | return getattr(cppyy, attr) 33 | 34 | sys.modules['cppyy.interactive'] = InteractiveLazy(\ 35 | not ispypy and not (hasattr(__builtins__, '__IPYTHON__') or 'IPython' in sys.modules)) 36 | del InteractiveLazy, ispypy 37 | -------------------------------------------------------------------------------- /python/cppyy/ll.py: -------------------------------------------------------------------------------- 1 | """ Low-level utilities, to be used for "emergencies only". 2 | """ 3 | 4 | import cppyy 5 | import ctypes 6 | import sys 7 | import warnings 8 | 9 | try: 10 | import __pypy__ 11 | del __pypy__ 12 | ispypy = True 13 | except ImportError: 14 | ispypy = False 15 | 16 | __all__ = [ 17 | 'argv', 18 | 'argc', 19 | 'cast', 20 | 'static_cast', 21 | 'reinterpret_cast', 22 | 'dynamic_cast', 23 | 'malloc', 24 | 'free', 25 | 'array_new', 26 | 'array_delete', 27 | 'signals_as_exception', 28 | 'set_signals_as_exception', 29 | 'FatalError', 30 | 'BusError', 31 | 'SegmentationViolation', 32 | 'IllegalInstruction', 33 | 'AbortSignal', 34 | ] 35 | 36 | 37 | # convenience functions to create C-style argv/argc 38 | def argv(): 39 | """Return C's argv for use with cppyy/ctypes.""" 40 | cargsv = (ctypes.c_char_p * len(sys.argv))(*(x.encode() for x in sys.argv)) 41 | return ctypes.POINTER(ctypes.c_char_p)(cargsv) 42 | 43 | def argc(): 44 | """Return C's argc for use with cppyy/ctypes.""" 45 | return len(sys.argv) 46 | 47 | # import low-level python converters 48 | for _name in ['addressof', 'as_cobject', 'as_capsule', 'as_ctypes', 'as_memoryview']: 49 | try: 50 | exec('%s = cppyy._backend.%s' % (_name, _name)) 51 | __all__.append(_name) 52 | except AttributeError: 53 | pass 54 | del _name 55 | 56 | 57 | # create low-level helpers 58 | cppyy.cppdef("""namespace __cppyy_internal { 59 | // type casting 60 | template 61 | T cppyy_cast(U val) { return (T)val; } 62 | 63 | template 64 | T cppyy_static_cast(U val) { return static_cast(val); } 65 | 66 | template 67 | T cppyy_reinterpret_cast(U val) { return reinterpret_cast(val); } 68 | 69 | template 70 | T* cppyy_dynamic_cast(S* obj) { return dynamic_cast(obj); } 71 | 72 | // memory allocation/free-ing 73 | template 74 | T* cppyy_malloc(size_t count=1) { return (T*)malloc(sizeof(T*)*count); } 75 | 76 | template 77 | T* cppyy_array_new(size_t count) { return new T[count]; } 78 | 79 | template 80 | void cppyy_array_delete(T* ptr) { delete[] ptr; } 81 | }""") 82 | 83 | 84 | # helper for sizing arrays 85 | class ArraySizer(object): 86 | def __init__(self, func): 87 | self.func = func 88 | def __getitem__(self, t): 89 | self.array_type = t 90 | return self 91 | def __call__(self, size, managed=False): 92 | res = self.func[self.array_type](size) 93 | try: 94 | res.reshape((size,)+res.shape[1:]) 95 | if managed: 96 | res.__python_owns__ = True 97 | except AttributeError: 98 | res.__reshape__((size,)) 99 | if managed: 100 | warnings.warn("managed low-level arrays of instances not supported") 101 | return res 102 | 103 | class CArraySizer(ArraySizer): 104 | def __call__(self, size, managed=False): 105 | res = ArraySizer.__call__(self, size, managed) 106 | res.__cpp_array__ = False 107 | return res 108 | 109 | 110 | # import casting helpers 111 | cast = cppyy.gbl.__cppyy_internal.cppyy_cast 112 | static_cast = cppyy.gbl.__cppyy_internal.cppyy_static_cast 113 | reinterpret_cast = cppyy.gbl.__cppyy_internal.cppyy_reinterpret_cast 114 | dynamic_cast = cppyy.gbl.__cppyy_internal.cppyy_dynamic_cast 115 | 116 | # import memory allocation/free-ing helpers 117 | malloc = CArraySizer(cppyy.gbl.__cppyy_internal.cppyy_malloc) 118 | free = cppyy.gbl.free # for symmetry 119 | array_new = ArraySizer(cppyy.gbl.__cppyy_internal.cppyy_array_new) 120 | array_delete = cppyy.gbl.__cppyy_internal.cppyy_array_delete 121 | 122 | # signals as exceptions 123 | if not ispypy: 124 | FatalError = cppyy._backend.FatalError 125 | BusError = cppyy._backend.BusError 126 | SegmentationViolation = cppyy._backend.SegmentationViolation 127 | IllegalInstruction = cppyy._backend.IllegalInstruction 128 | AbortSignal = cppyy._backend.AbortSignal 129 | 130 | class signals_as_exception: 131 | def __enter__(self): 132 | cppyy._backend.SetGlobalSignalPolicy(1) 133 | 134 | def __exit__(self, type, value, traceback): 135 | cppyy._backend.SetGlobalSignalPolicy(0) 136 | 137 | set_signals_as_exception = cppyy._backend.SetGlobalSignalPolicy 138 | 139 | else: 140 | class FatalError(Exception): 141 | pass 142 | class BusError(FatalError): 143 | pass 144 | class SegmentationViolation(FatalError): 145 | pass 146 | class IllegalInstruction(FatalError): 147 | pass 148 | class AbortSignal(FatalError): 149 | pass 150 | 151 | class signals_as_exception: 152 | def __enter__(self): 153 | pass # not yet implemented 154 | 155 | def __exit__(self, type, value, traceback): 156 | pass # not yet implemented 157 | 158 | def set_signals_as_exception(seton): 159 | return False 160 | 161 | del ispypy 162 | 163 | -------------------------------------------------------------------------------- /python/cppyy/reflex.py: -------------------------------------------------------------------------------- 1 | """ cppyy reflection 2 | """ 3 | 4 | import cppyy 5 | 6 | try: 7 | import __pypy__ 8 | __all__ = [ 9 | 10 | ] 11 | 12 | except ImportError: 13 | __all__ = [ 14 | 'RETURN_TYPE', 15 | ] 16 | 17 | cppyy.include("CPyCppyy/Reflex.h") 18 | 19 | IS_NAMESPACE = cppyy.gbl.Cppyy.Reflex.IS_NAMESPACE 20 | IS_AGGREGATE = cppyy.gbl.Cppyy.Reflex.IS_AGGREGATE 21 | 22 | OFFSET = cppyy.gbl.Cppyy.Reflex.OFFSET 23 | RETURN_TYPE = cppyy.gbl.Cppyy.Reflex.RETURN_TYPE 24 | TYPE = cppyy.gbl.Cppyy.Reflex.TYPE 25 | 26 | OPTIMAL = cppyy.gbl.Cppyy.Reflex.OPTIMAL 27 | AS_TYPE = cppyy.gbl.Cppyy.Reflex.AS_TYPE 28 | AS_STRING = cppyy.gbl.Cppyy.Reflex.AS_STRING 29 | -------------------------------------------------------------------------------- /python/cppyy/types.py: -------------------------------------------------------------------------------- 1 | """ C++ proxy types. 2 | """ 3 | 4 | import cppyy 5 | 6 | bck = cppyy._backend 7 | Instance = bck.CPPInstance 8 | 9 | try: 10 | import __pypy__ 11 | __all__ = [ 12 | 'Instance' 13 | ] 14 | 15 | except ImportError: 16 | __all__ = [ 17 | 'DataMember', 18 | 'Instance', 19 | 'Function', 20 | 'Method', 21 | 'Scope', 22 | 'InstanceArray', 23 | 'LowLevelView', 24 | 'Template' 25 | ] 26 | 27 | DataMember = bck.CPPDataMember 28 | Function = bck.CPPOverload 29 | Method = bck.CPPOverload 30 | Scope = bck.CPPScope 31 | InstanceArray = bck.InstanceArray 32 | LowLevelView = bck.LowLevelView 33 | Template = bck.TemplateProxy 34 | 35 | del bck 36 | -------------------------------------------------------------------------------- /python/cppyy_compat/__init__.py: -------------------------------------------------------------------------------- 1 | """Compatibility layer for PyPy 5.7-9 2 | """ 3 | 4 | def pypy58_57_compat(): 5 | import imp, os 6 | 7 | # first load and move the builtin cppyy module 8 | if not 'cppyy' in sys.modules: 9 | try: 10 | olddir = os.getcwd() 11 | from cppyy_backend import loader 12 | c = loader.load_cpp_backend() 13 | # move to the location of the backend, just in case '.' is 14 | # in the dynloader's path 15 | os.chdir(os.path.dirname(c._name)) 16 | imp.init_builtin('cppyy') 17 | except ImportError: 18 | raise EnvironmentError('"%s" missing in LD_LIBRARY_PATH' %\ 19 | os.path.dirname(c._name)) 20 | finally: 21 | os.chdir(olddir) 22 | 23 | sys.modules['_cppyy'] = sys.modules['cppyy'] 24 | del sys.modules['cppyy'] 25 | 26 | # now locate and load the pip cppyy module 27 | decdir = os.path.join(os.path.dirname(__file__), os.path.pardir) 28 | for path in sys.path: # walk over sys.path skips builtins 29 | try: 30 | fp, pathname, description = imp.find_module('cppyy', [path]) 31 | sys.modules['cppyy'] = imp.load_module('cppyy_', fp, pathname, description) 32 | break 33 | except ImportError: 34 | pass 35 | 36 | # copy over the _cppyy functions into cppyy 37 | old = sys.modules['_cppyy'] 38 | new = sys.modules['cppyy'] 39 | for name in dir(old): 40 | if not hasattr(new, name): 41 | setattr(new, name, getattr(old, name)) 42 | 43 | # for pypy5.9 we may need to move to the location of the backend, if '.' happens 44 | # to be in LD_LIBRARY_PATH, but not the full directory 45 | def py59_compat(): 46 | import os, cppyy_backend 47 | olddir = os.getcwd() 48 | c = cppyy_backend.loader.load_cpp_backend() 49 | os.chdir(os.path.dirname(c._name)) 50 | try: 51 | global __name__ 52 | actual_name = __name__; __name__ = '' 53 | import _cppyy as _backend 54 | except ImportError: 55 | raise EnvironmentError('"%s" missing in LD_LIBRARY_PATH' % os.path.dirname(c._name)) 56 | finally: 57 | __name__ = actual_name 58 | os.chdir(olddir) 59 | _backend.nullptr = _backend.gbl.nullptr 60 | 61 | 62 | import sys 63 | version = sys.pypy_version_info 64 | if version[0] == 5: 65 | if 6 < version[1] <= 8: 66 | pypy58_57_compat() 67 | elif version[1] == 9: 68 | py59_compat() 69 | del version, sys 70 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal=0 3 | 4 | [metadata] 5 | license_files = LICENSE.txt 6 | 7 | [options.entry_points] 8 | pyinstaller40 = 9 | hook-dirs = cppyy.__pyinstaller:get_hook_dirs 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import codecs, glob, os, sys, re 2 | from setuptools import setup, find_packages, Extension 3 | from distutils import log 4 | 5 | from setuptools.command.install import install as _install 6 | 7 | add_pkg = ['cppyy', 'cppyy.__pyinstaller'] 8 | try: 9 | import __pypy__, sys 10 | version = sys.pypy_version_info 11 | requirements = ['cppyy-backend==1.15.3', 'cppyy-cling==6.32.8'] 12 | if version[0] == 5: 13 | if version[1] <= 9: 14 | requirements = ['cppyy-backend<0.3', 'cppyy-cling<6.12'] 15 | add_pkg += ['cppyy_compat'] 16 | elif version[1] <= 10: 17 | requirements = ['cppyy-backend<0.4', 'cppyy-cling<=6.15'] 18 | elif version[0] == 6: 19 | if version[1] <= 0: 20 | requirements = ['cppyy-backend<1.1', 'cppyy-cling<=6.15'] 21 | elif version[0] == 7: 22 | if version[1] <= 3 and version[2] <= 3: 23 | requirements = ['cppyy-backend<=1.10', 'cppyy-cling<=6.18.2.3'] 24 | except ImportError: 25 | # CPython 26 | requirements = ['CPyCppyy==1.13.0', 'cppyy-backend==1.15.3', 'cppyy-cling==6.32.8'] 27 | 28 | setup_requirements = ['wheel'] 29 | if 'build' in sys.argv or 'install' in sys.argv: 30 | setup_requirements += requirements 31 | 32 | here = os.path.abspath(os.path.dirname(__file__)) 33 | with codecs.open(os.path.join(here, 'README.rst'), encoding='utf-8') as f: 34 | long_description = f.read() 35 | 36 | # https://packaging.python.org/guides/single-sourcing-package-version/ 37 | def read(*parts): 38 | with codecs.open(os.path.join(here, *parts), 'r') as fp: 39 | return fp.read() 40 | 41 | def find_version(*file_paths): 42 | version_file = read(*file_paths) 43 | version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", 44 | version_file, re.M) 45 | if version_match: 46 | return version_match.group(1) 47 | raise RuntimeError("Unable to find version string.") 48 | 49 | 50 | # 51 | # customized commands 52 | # 53 | class my_install(_install): 54 | def __init__(self, *args, **kwds): 55 | if 0x3000000 <= sys.hexversion: 56 | super(_install, self).__init__(*args, **kwds) 57 | else: 58 | # b/c _install is a classobj, not type 59 | _install.__init__(self, *args, **kwds) 60 | 61 | try: 62 | import cppyy_backend as cpb 63 | self._pchname = 'allDict.cxx.pch.' + str(cpb.__version__) 64 | except (ImportError, AttributeError): 65 | self._pchname = None 66 | 67 | def run(self): 68 | # base install 69 | _install.run(self) 70 | 71 | # force build of the .pch underneath the cppyy package if not available yet 72 | install_path = os.path.join(os.getcwd(), self.install_libbase, 'cppyy') 73 | 74 | if self._pchname: 75 | try: 76 | import cppyy_backend as cpb 77 | if not os.path.exists(os.path.join(cpb.__file__, 'etc', self._pchname)): 78 | log.info("installing pre-compiled header in %s", install_path) 79 | cpb.loader.set_cling_compile_options(True) 80 | cpb.loader.ensure_precompiled_header(install_path, self._pchname) 81 | except (ImportError, AttributeError): 82 | # ImportError may occur with wrong pip requirements resolution (unlikely) 83 | # AttributeError will occur with (older) PyPy as it relies on older backends 84 | self._pchname = None 85 | 86 | def get_outputs(self): 87 | outputs = _install.get_outputs(self) 88 | if self._pchname: 89 | # pre-emptively add allDict.cxx.pch, which may or may not be created; need full 90 | # path to make sure the final relative path is correct 91 | outputs.append(os.path.join(os.getcwd(), self.install_libbase, 'cppyy', self._pchname)) 92 | return outputs 93 | 94 | 95 | cmdclass = { 96 | 'install': my_install } 97 | 98 | 99 | setup( 100 | name='cppyy', 101 | version=find_version('python', 'cppyy', '_version.py'), 102 | description='Cling-based Python-C++ bindings', 103 | long_description=long_description, 104 | 105 | url='http://cppyy.readthedocs.org', 106 | 107 | # Author details 108 | author='Wim Lavrijsen', 109 | author_email='WLavrijsen@lbl.gov', 110 | 111 | license='LBNL BSD', 112 | 113 | classifiers=[ 114 | 'Development Status :: 5 - Production/Stable', 115 | 116 | 'Intended Audience :: Developers', 117 | 118 | 'Topic :: Software Development', 119 | 'Topic :: Software Development :: Interpreters', 120 | 121 | 'License :: OSI Approved :: BSD License', 122 | 123 | 'Programming Language :: Python :: Implementation :: PyPy', 124 | 'Programming Language :: Python :: 3', 125 | 'Programming Language :: Python :: 3.6', 126 | 'Programming Language :: Python :: 3.7', 127 | 'Programming Language :: Python :: 3.8', 128 | 'Programming Language :: Python :: 3.9', 129 | 'Programming Language :: Python :: 3.10', 130 | 'Programming Language :: C', 131 | 'Programming Language :: C++', 132 | 133 | 'Natural Language :: English' 134 | ], 135 | 136 | setup_requires=setup_requirements, 137 | install_requires=requirements, 138 | 139 | keywords='C++ bindings data science calling language integration', 140 | 141 | include_package_data=True, 142 | package_data={'': ['installer/cppyy_monkey_patch.py']}, 143 | 144 | package_dir={'': 'python'}, 145 | packages=find_packages('python', include=add_pkg), 146 | 147 | # TODO: numba_extensions will load all extensions even if the package 148 | # itself is not otherwise imported, just installed; in the case of cppyy, 149 | # that is currently too heavy (and breaks on conda) 150 | 151 | #entry_points={ 152 | # 'numba_extensions': [ 153 | # 'init = cppyy.numba_ext:_init_extension', 154 | # ], 155 | #}, 156 | 157 | cmdclass=cmdclass, 158 | 159 | zip_safe=False, 160 | ) 161 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | dicts = advancedcppDict.so \ 2 | advancedcpp2Dict.so \ 3 | conversionsDict.so \ 4 | cpp11featuresDict.so \ 5 | crossinheritanceDict.so \ 6 | datatypesDict.so \ 7 | doc_helperDict.so \ 8 | example01Dict.so \ 9 | fragileDict.so \ 10 | operatorsDict.so \ 11 | overloadsDict.so \ 12 | pythonizablesDict.so \ 13 | std_streamsDict.so \ 14 | stltypesDict.so \ 15 | templatesDict.so 16 | 17 | all : $(dicts) 18 | 19 | genreflex_flags := $(shell genreflex --cppflags) 20 | cppflags=$(shell cling-config --cppflags) $(genreflex_flags) -O3 -fPIC -I$(shell python -c 'import sysconfig as sc; print(sc.get_config_var("INCLUDEPY"))') -Wno-register 21 | 22 | PLATFORM := $(shell uname -s) 23 | ifeq ($(PLATFORM),Darwin) 24 | cppflags+=-dynamiclib -undefined dynamic_lookup -Wno-delete-non-virtual-dtor 25 | endif 26 | 27 | %Dict.so: %_rflx.cpp %.cxx 28 | $(CXX) $(cppflags) -shared -o $@ $^ 29 | 30 | %_rflx.cpp: %.h %.xml 31 | genreflex $< --selection=$*.xml --rootmap=$*Dict.rootmap --rootmap-lib=$*Dict.so 32 | 33 | .PHONY: test clean 34 | 35 | test: 36 | pytest test_*.py 37 | 38 | clean: 39 | -rm -f $(dicts) $(subst .so,.rootmap,$(dicts)) $(subst Dict.so,_rflx_rdict.pcm,$(dicts)) $(subst Dict.so,_rflx.cpp,$(dicts)) $(wildcard *.pyc) 40 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wlav/cppyy/48468034cae4f06a1d33c1c3f086c38a6e2e7c3a/test/__init__.py -------------------------------------------------------------------------------- /test/advancedcpp.cxx: -------------------------------------------------------------------------------- 1 | #include "advancedcpp.h" 2 | 3 | #include 4 | 5 | 6 | // for testing of default arguments 7 | #define IMPLEMENT_DEFAULTERS(type, tname) \ 8 | tname##_defaulter::tname##_defaulter(type a, type b, type c) { \ 9 | m_a = a; m_b = b; m_c = c; \ 10 | } \ 11 | type tname##_defaulter_func(int idx, type a, type b, type c) { \ 12 | if (idx == 0) return a; \ 13 | if (idx == 1) return b; \ 14 | if (idx == 2) return c; \ 15 | return (type)idx; \ 16 | } 17 | IMPLEMENT_DEFAULTERS(short, short) 18 | IMPLEMENT_DEFAULTERS(unsigned short, ushort) 19 | IMPLEMENT_DEFAULTERS(int, int) 20 | IMPLEMENT_DEFAULTERS(unsigned, uint) 21 | IMPLEMENT_DEFAULTERS(long, long) 22 | IMPLEMENT_DEFAULTERS(unsigned long, ulong) 23 | IMPLEMENT_DEFAULTERS(long long, llong) 24 | IMPLEMENT_DEFAULTERS(unsigned long long, ullong) 25 | IMPLEMENT_DEFAULTERS(float, float) 26 | IMPLEMENT_DEFAULTERS(double, double) 27 | 28 | std::string string_defaulter_func(int idx, const std::string& name1, std::string name2) { 29 | if (idx == 0) return name1; 30 | if (idx == 1) return name2; 31 | return "mies"; 32 | } 33 | 34 | 35 | // for esoteric inheritance testing 36 | a_class* create_c1() { return new c_class_1; } 37 | a_class* create_c2() { return new c_class_2; } 38 | 39 | int get_a( a_class& a ) { return a.m_a; } 40 | int get_b( b_class& b ) { return b.m_b; } 41 | int get_c( c_class& c ) { return c.m_c; } 42 | int get_d( d_class& d ) { return d.m_d; } 43 | 44 | 45 | // for namespace testing 46 | int a_ns::g_a = 11; 47 | int a_ns::b_class::s_b = 22; 48 | int a_ns::b_class::c_class::s_c = 33; 49 | int a_ns::d_ns::g_d = 44; 50 | int a_ns::d_ns::e_class::s_e = 55; 51 | int a_ns::d_ns::e_class::f_class::s_f = 66; 52 | 53 | int a_ns::get_g_a() { return g_a; } 54 | int a_ns::d_ns::get_g_d() { return g_d; } 55 | 56 | 57 | // for template testing 58 | template class T1; 59 | template class T2 >; 60 | template class T3; 61 | template class T3, T2 > >; 62 | template class a_ns::T4; 63 | template class a_ns::T4 > >; 64 | 65 | 66 | // helpers for checking pass-by-ref 67 | void set_int_through_ref(int& i, int val) { i = val; } 68 | int pass_int_through_const_ref(const int& i) { return i; } 69 | void set_long_through_ref(long& l, long val) { l = val; } 70 | long pass_long_through_const_ref(const long& l) { return l; } 71 | void set_double_through_ref(double& d, double val) { d = val; } 72 | double pass_double_through_const_ref(const double& d) { return d; } 73 | 74 | 75 | // for math conversions testing 76 | bool operator==(const some_comparable& c1, const some_comparable& c2) 77 | { 78 | return &c1 != &c2; // the opposite of a pointer comparison 79 | } 80 | 81 | bool operator!=(const some_comparable& c1, const some_comparable& c2) 82 | { 83 | return &c1 == &c2; // the opposite of a pointer comparison 84 | } 85 | 86 | 87 | // a couple of globals for access testing 88 | double my_global_double = 12.; 89 | double my_global_array[500]; 90 | static double sd = 1234.; 91 | double* my_global_ptr = &sd; 92 | const char my_global_string2[] = "zus jet teun"; 93 | const char* my_global_string3[3] = {"aap", "noot", "mies"}; 94 | some_int_holder my_global_int_holders[5] = { 95 | some_int_holder(13), some_int_holder(42), some_int_holder(88), 96 | some_int_holder(-1), some_int_holder(17) }; 97 | 98 | some_abstract_class* g_abstract_ptr = nullptr; 99 | 100 | 101 | // for life-line and identity testing 102 | int some_class_with_data::some_data::s_num_data = 0; 103 | 104 | 105 | // for testing multiple inheritance 106 | multi1::~multi1() {} 107 | multi2::~multi2() {} 108 | multi::~multi() {} 109 | 110 | 111 | // for testing calls to overloaded new 112 | int new_overloader::s_instances = 0; 113 | 114 | void* new_overloader::operator new(std::size_t size) { 115 | ++s_instances; 116 | return ::operator new(size); 117 | } 118 | 119 | void* new_overloader::operator new(std::size_t, void* p) throw() { 120 | // no ++s_instances, as no memory is allocated 121 | return p; 122 | } 123 | 124 | void new_overloader::operator delete(void* p, std::size_t) { 125 | if (p == 0) return; 126 | --s_instances; 127 | ::operator delete(p); 128 | } 129 | 130 | 131 | // overload order testing 132 | int overload_one_way::gime() const { return 1; } 133 | std::string overload_one_way::gime() { return "aap"; } 134 | 135 | std::string overload_the_other_way::gime() { return "aap"; } 136 | int overload_the_other_way::gime() const { return 1; } 137 | 138 | 139 | // exception handling testing 140 | void Thrower::throw_anything() { 141 | throw 1; 142 | } 143 | 144 | void Thrower::throw_exception() { 145 | throw std::runtime_error("C++ function failed"); 146 | } 147 | 148 | 149 | // operator to __str__ mapping 150 | std::ostream& Cpp2PyPrinting::Printable1::operator<<(std::ostream& os) { 151 | os << "Printable1::operator<<"; 152 | return os; 153 | } 154 | 155 | std::ostream& Cpp2PyPrinting::operator<<(std::ostream& os, const Printable2&) { 156 | os << "Cpp2PyPrinting::operator<<"; 157 | return os; 158 | } 159 | 160 | std::ostream& operator<<(std::ostream& os, const Cpp2PyPrinting::Printable3&) { 161 | os << "::operator<<(3)"; 162 | return os; 163 | } 164 | 165 | std::ostream& operator<<(std::ostream& os, const Printable4&) { 166 | os << "::operator<<(4)"; 167 | return os; 168 | } 169 | 170 | Printable6& Printable6::operator<<(int) { return *this; } 171 | 172 | std::ostream& operator<<(std::ostream& os, const Printable6& y) { return os << "Printable6"; } 173 | 174 | 175 | // for using directives testing 176 | int UsedSpace1::foo1() { 177 | return 13; 178 | } 179 | 180 | int UsedSpace2::bar() { 181 | return 42; 182 | } 183 | 184 | int UsedSpace1::inner::foo2() { 185 | return 27; 186 | } 187 | -------------------------------------------------------------------------------- /test/advancedcpp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /test/advancedcpp2.cxx: -------------------------------------------------------------------------------- 1 | #include "advancedcpp2.h" 2 | 3 | 4 | // for namespace testing 5 | int a_ns::g_g = 77; 6 | int a_ns::g_class::s_g = 88; 7 | int a_ns::g_class::h_class::s_h = 99; 8 | int a_ns::d_ns::g_i = 111; 9 | int a_ns::d_ns::i_class::s_i = 222; 10 | int a_ns::d_ns::i_class::j_class::s_j = 333; 11 | 12 | int a_ns::get_g_g() { return g_g; } 13 | int a_ns::d_ns::get_g_i() { return g_i; } 14 | -------------------------------------------------------------------------------- /test/advancedcpp2.h: -------------------------------------------------------------------------------- 1 | //=========================================================================== 2 | namespace a_ns { // for namespace testing 3 | extern int g_g; 4 | int get_g_g(); 5 | 6 | struct g_class { 7 | g_class() { m_g = -7; } 8 | int m_g; 9 | static int s_g; 10 | 11 | struct h_class { 12 | h_class() { m_h = -8; } 13 | int m_h; 14 | static int s_h; 15 | }; 16 | }; 17 | 18 | namespace d_ns { 19 | extern int g_i; 20 | int get_g_i(); 21 | 22 | struct i_class { 23 | i_class() { m_i = -9; } 24 | int m_i; 25 | static int s_i; 26 | 27 | struct j_class { 28 | j_class() { m_j = -10; } 29 | int m_j; 30 | static int s_j; 31 | }; 32 | }; 33 | 34 | } // namespace d_ns 35 | 36 | } // namespace a_ns 37 | -------------------------------------------------------------------------------- /test/advancedcpp2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/assert_interactive.py: -------------------------------------------------------------------------------- 1 | from cppyy.interactive import * 2 | 3 | # namespace at the global level 4 | assert std 5 | 6 | # cppyy functions 7 | assert cppdef 8 | assert include 9 | 10 | try: 11 | import __pypy__ 12 | # 'cppyy.gbl' bound to 'g' 13 | assert g 14 | assert g.std 15 | except ImportError: 16 | # full lazy lookup available 17 | assert gInterpreter 18 | -------------------------------------------------------------------------------- /test/bindexplib.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import os, sys, subprocess 4 | 5 | target = sys.argv[1] 6 | output = sys.argv[2] 7 | 8 | def isokay(name): 9 | # filter standard symbols 10 | return name[0] != '_' and not name in {'memcpy', 'memmove', 'memset'} 11 | 12 | popen = subprocess.Popen(['dumpbin', '/SYMBOLS', target+'.obj'], 13 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 14 | 15 | stdout, _ = popen.communicate() 16 | stdout = stdout.decode('utf-8').strip() 17 | 18 | outf = open(output+'.def', 'w') 19 | outf.write('LIBRARY %s.dll\nEXPORTS\n' % output) 20 | for line in stdout.split('\r\n'): 21 | parts = line.split() 22 | if len(parts) < 8: 23 | continue 24 | if parts[7][0:4] in ['??_G', '??_E']: # do not export deleting destructors 25 | continue 26 | if parts[4] == 'External': 27 | if isokay(parts[6]): 28 | outf.write('\t%s\tDATA\n' % parts[6]) 29 | elif parts[4] == '()' and parts[5] == 'External': 30 | if isokay(parts[7]): 31 | outf.write('\t%s\n' % parts[7]) 32 | 33 | -------------------------------------------------------------------------------- /test/conftest.py: -------------------------------------------------------------------------------- 1 | disabled = None 2 | -------------------------------------------------------------------------------- /test/conversions.cxx: -------------------------------------------------------------------------------- 1 | #include "conversions.h" 2 | 3 | #include 4 | 5 | 6 | //=========================================================================== 7 | double CNS::sumit(const std::vector& v) { 8 | return std::accumulate(v.begin(), v.end(), 0.); 9 | } 10 | 11 | double CNS::sumit(const std::vector& v1, const std::vector& v2) { 12 | return std::accumulate(v1.begin(), v1.end(), std::accumulate(v2.begin(), v2.end(), 0.)); 13 | } 14 | 15 | int CNS::Counter::s_count; 16 | 17 | CNS::Counter::Counter() { 18 | ++s_count; 19 | } 20 | 21 | CNS::Counter::Counter(const Counter&) { 22 | ++s_count; 23 | } 24 | 25 | CNS::Counter& CNS::Counter::operator=(const Counter&) { 26 | return *this; 27 | } 28 | 29 | CNS::Counter::~Counter() { 30 | --s_count; 31 | } 32 | 33 | double CNS::myhowmany(const std::vector& v) { 34 | return v.size(); 35 | } 36 | 37 | double CNS::myhowmany(const std::vector& v1, const std::vector& v2) { 38 | return v1.size() + v2.size(); 39 | } 40 | 41 | int CNS::sumints(const std::vector& v) { 42 | return std::accumulate(v.begin(), v.end(), 0); 43 | } 44 | 45 | int CNS::sumints(const std::vector& v1, const std::vector& v2) { 46 | return std::accumulate(v1.begin(), v1.end(), std::accumulate(v2.begin(), v2.end(), 0)); 47 | } 48 | 49 | double notallowed(std::vector& v) { 50 | return std::accumulate(v.begin(), v.end(), 0.); 51 | } 52 | -------------------------------------------------------------------------------- /test/conversions.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace CNS { 5 | 6 | //=========================================================================== 7 | double sumit(const std::vector&); 8 | double sumit(const std::vector&, const std::vector&); 9 | 10 | class Counter { 11 | public: 12 | Counter(); 13 | Counter(const Counter&); 14 | Counter& operator=(const Counter&); 15 | ~Counter(); 16 | 17 | static int s_count; 18 | }; 19 | 20 | double myhowmany(const std::vector&); 21 | double myhowmany(const std::vector&, const std::vector&); 22 | 23 | int sumints(const std::vector&); 24 | int sumints(const std::vector&, const std::vector&); 25 | 26 | double notallowed(std::vector&); 27 | 28 | } // namespace CNS 29 | -------------------------------------------------------------------------------- /test/conversions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/cpp11features.cxx: -------------------------------------------------------------------------------- 1 | #if __cplusplus >= 201103L 2 | 3 | #include "cpp11features.h" 4 | 5 | 6 | // for std::shared/unique_ptr<> testing 7 | int TestSmartPtr::s_counter = 0; 8 | 9 | std::shared_ptr create_shared_ptr_instance() { 10 | return std::shared_ptr(new TestSmartPtr); 11 | } 12 | 13 | std::unique_ptr create_unique_ptr_instance() { 14 | return std::unique_ptr(new TestSmartPtr); 15 | } 16 | 17 | int TestSmartPtr::get_value() { 18 | return 17; 19 | } 20 | 21 | int DerivedTestSmartPtr::get_value() { 22 | return m_int + 76; 23 | } 24 | 25 | int pass_shared_ptr(std::shared_ptr p) { 26 | return p->get_value(); 27 | } 28 | 29 | int move_shared_ptr(std::shared_ptr&& p) { 30 | return p->get_value(); 31 | } 32 | 33 | int move_unique_ptr(std::unique_ptr&& p) { 34 | return p->get_value(); 35 | } 36 | 37 | int move_unique_ptr_derived(std::unique_ptr&& p) { 38 | return p->get_value(); 39 | } 40 | 41 | TestSmartPtr create_TestSmartPtr_by_value() { 42 | return TestSmartPtr{}; 43 | } 44 | 45 | 46 | // for move ctors etc. 47 | int TestMoving1::s_move_counter = 0; 48 | int TestMoving1::s_instance_counter = 0; 49 | int TestMoving2::s_move_counter = 0; 50 | int TestMoving2::s_instance_counter = 0; 51 | 52 | void implicit_converion_move(TestMoving2&&) { 53 | /* empty */ 54 | } 55 | 56 | 57 | // for std::function testing 58 | std::function FNCreateTestStructFunc() { return [](const FNTestStruct& t) { return t.t; }; } 59 | std::function FunctionNS::FNCreateTestStructFunc() { return [](const FNTestStruct& t) { return t.t; }; } 60 | 61 | #endif // c++11 and later 62 | -------------------------------------------------------------------------------- /test/cpp11features.h: -------------------------------------------------------------------------------- 1 | #if __cplusplus >= 201103L 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | //=========================================================================== 9 | class TestSmartPtr { // for std::shared/unique_ptr<> testing 10 | public: 11 | static int s_counter; 12 | 13 | public: 14 | TestSmartPtr() { ++s_counter; } 15 | TestSmartPtr(const TestSmartPtr&) { ++s_counter; } 16 | virtual ~TestSmartPtr() { --s_counter; } 17 | 18 | public: 19 | virtual int get_value(); 20 | }; 21 | 22 | std::shared_ptr create_shared_ptr_instance(); 23 | std::unique_ptr create_unique_ptr_instance(); 24 | 25 | class DerivedTestSmartPtr : TestSmartPtr { 26 | public: 27 | DerivedTestSmartPtr(int i) : m_int(i) {} 28 | virtual int get_value(); 29 | 30 | public: 31 | int m_int; 32 | }; 33 | 34 | int pass_shared_ptr(std::shared_ptr p); 35 | int move_shared_ptr(std::shared_ptr&& p); 36 | int move_unique_ptr(std::unique_ptr&& p); 37 | int move_unique_ptr_derived(std::unique_ptr&& p); 38 | 39 | TestSmartPtr create_TestSmartPtr_by_value(); 40 | 41 | 42 | //=========================================================================== 43 | class TestMoving1 { // for move ctors etc. 44 | public: 45 | static int s_move_counter; 46 | static int s_instance_counter; 47 | 48 | public: 49 | TestMoving1() { ++s_instance_counter; } 50 | TestMoving1(TestMoving1&&) { ++s_move_counter; ++s_instance_counter; } 51 | TestMoving1(const TestMoving1&) { ++s_instance_counter; } 52 | TestMoving1& operator=(TestMoving1&&) { ++s_move_counter; return *this; } 53 | TestMoving1& operator=(TestMoving1&) { return *this; } 54 | ~TestMoving1() { --s_instance_counter; } 55 | }; 56 | 57 | class TestMoving2 { // note opposite method order from TestMoving1 58 | public: 59 | static int s_move_counter; 60 | static int s_instance_counter; 61 | 62 | public: 63 | TestMoving2() { ++s_instance_counter; } 64 | TestMoving2(const TestMoving1&) { ++s_instance_counter; } 65 | TestMoving2(const TestMoving2&) { ++s_instance_counter; } 66 | TestMoving2(TestMoving2&& other) { ++s_move_counter; ++s_instance_counter; } 67 | TestMoving2& operator=(TestMoving2&) { return *this; } 68 | TestMoving2& operator=(TestMoving2&&) { ++s_move_counter; return *this; } 69 | ~TestMoving2() { --s_instance_counter; } 70 | }; 71 | 72 | void implicit_converion_move(TestMoving2&&); 73 | 74 | 75 | //=========================================================================== 76 | struct TestData { // for initializer list construction 77 | TestData(int i=0) : m_int(i) {} 78 | int m_int; 79 | }; 80 | 81 | struct TestData2 { 82 | TestData2(int i=0) : m_int(i) {} 83 | virtual ~TestData2() {} 84 | int m_int; 85 | }; 86 | 87 | template 88 | class WithInitList { 89 | public: 90 | WithInitList(std::initializer_list ll) : m_data(ll) {} 91 | const T& operator[](int i) { return m_data[i]; } 92 | 93 | int size() { return m_data.size(); } 94 | 95 | private: 96 | std::vector m_data; 97 | }; 98 | 99 | 100 | //=========================================================================== 101 | struct FNTestStruct { // for std::function<> testing 102 | FNTestStruct(int i) : t(i) {} 103 | int t; 104 | }; 105 | std::function FNCreateTestStructFunc(); 106 | 107 | namespace FunctionNS { 108 | struct FNTestStruct { FNTestStruct(int i) : t(i) {} int t; }; 109 | std::function FNCreateTestStructFunc(); 110 | } 111 | 112 | 113 | //=========================================================================== 114 | struct StructWithHash {}; // for std::hash<> testing 115 | struct StructWithoutHash {}; 116 | 117 | namespace std { 118 | template<> 119 | struct hash { 120 | size_t operator()(const StructWithHash&) const { return 17; } 121 | }; 122 | } // namespace std 123 | 124 | #endif // c++11 and later 125 | -------------------------------------------------------------------------------- /test/cpp11features.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/crossinheritance.cxx: -------------------------------------------------------------------------------- 1 | #include "crossinheritance.h" 2 | 3 | 4 | // for overridden method checking 5 | CrossInheritance::Base1::~Base1() {} 6 | 7 | int CrossInheritance::Base1::call_get_value(Base1* b) { 8 | return b->get_value(); 9 | } 10 | 11 | int CrossInheritance::Base1::call_sum_value(Base1* b, int i) { 12 | return b->sum_value(i); 13 | } 14 | 15 | int CrossInheritance::Base1::call_sum_all(Base1* b, int i) { 16 | return b->sum_all(i); 17 | } 18 | 19 | int CrossInheritance::Base1::call_sum_all(Base1* b, int i, int j) { 20 | return b->sum_all(i, j); 21 | } 22 | 23 | int CrossInheritance::Base1::sum_pass_value(Base1* b) { 24 | int a = 0; 25 | a += b->pass_value1(1); 26 | int i = 2; 27 | a += b->pass_value2(i); 28 | a += b->pass_value3(3); 29 | a += b->pass_value4(*b); 30 | a += b->pass_value5(*b); 31 | return a; 32 | } 33 | 34 | int CrossInheritance::IBase2::call_get_value(IBase2* b) { 35 | return b->get_value(); 36 | } 37 | 38 | CrossInheritance::IBase3::IBase3(int i) { 39 | m_int = i; 40 | } 41 | 42 | int CrossInheritance::CBase2::get_value() { 43 | return 42; 44 | } 45 | 46 | int CrossInheritance::IBase4::call_get_value(IBase4* b) { 47 | return b->get_value(); 48 | } 49 | 50 | int CrossInheritance::CBase4::get_value() const { 51 | return 27; 52 | } 53 | 54 | int CrossInheritance::TDerived1::get_value() { 55 | return 27; 56 | } 57 | 58 | int CrossInheritance::CountableBase::s_count = 0; 59 | 60 | CrossInheritance::CountableBase::CountableBase() { 61 | ++s_count; 62 | } 63 | 64 | CrossInheritance::CountableBase::CountableBase(const CountableBase&) { 65 | ++s_count; 66 | } 67 | 68 | CrossInheritance::CountableBase& CrossInheritance::CountableBase::operator=(const CountableBase&) { 69 | return *this; 70 | } 71 | 72 | CrossInheritance::CountableBase::~CountableBase() { 73 | --s_count; 74 | } 75 | 76 | int CrossInheritance::CountableBase::call() { 77 | return -1; 78 | } 79 | 80 | CrossInheritance::Component::Component() { 81 | ++s_count; 82 | } 83 | 84 | CrossInheritance::Component::~Component() { 85 | --s_count; 86 | } 87 | 88 | int CrossInheritance::Component::get_count() { 89 | return s_count; 90 | } 91 | 92 | int CrossInheritance::Component::s_count = 0; 93 | 94 | namespace { 95 | 96 | class ComponentWithValue : public CrossInheritance::Component { 97 | public: 98 | ComponentWithValue(int value) : m_value(value) {} 99 | int getValue() { return m_value; } 100 | 101 | protected: 102 | int m_value; 103 | }; 104 | 105 | } // unnamed namespace 106 | 107 | CrossInheritance::Component* CrossInheritance::build_component(int value) { 108 | return new ComponentWithValue(value); 109 | } 110 | 111 | CrossInheritance::Component* CrossInheritance::cycle_component(Component* c) { 112 | return c; 113 | } 114 | 115 | // for protected member testing 116 | AccessProtected::MyBase::MyBase() : my_data(101) { 117 | /* empty */ 118 | } 119 | 120 | AccessProtected::MyBase::~MyBase() { 121 | /* empty */ 122 | } 123 | 124 | int AccessProtected::MyBase::get_data_v() { 125 | return my_data; 126 | } 127 | 128 | int AccessProtected::MyBase::get_data() { 129 | return my_data; 130 | } 131 | -------------------------------------------------------------------------------- /test/crossinheritance.h: -------------------------------------------------------------------------------- 1 | #ifndef CPPYY_TEST_CROSSINHERITANCE_H 2 | #define CPPYY_TEST_CROSSINHERITANCE_H 3 | 4 | #include 5 | 6 | 7 | //=========================================================================== 8 | namespace CrossInheritance { 9 | 10 | class Base1 { // for overridden method checking 11 | public: 12 | Base1() : m_int(42) {} 13 | Base1(int i) : m_int(i) {} 14 | virtual ~Base1(); 15 | 16 | virtual int get_value() { return m_int; } 17 | static int call_get_value(Base1* b); 18 | 19 | virtual int sum_value(int i) { return m_int + i; } 20 | static int call_sum_value(Base1* b, int); 21 | 22 | virtual int sum_all(int i) { return m_int + i; } 23 | virtual int sum_all(int i, int j) { return m_int + i + j; } 24 | static int call_sum_all(Base1* b, int); 25 | static int call_sum_all(Base1* b, int, int); 26 | 27 | virtual int pass_value1(int a) { return a; } 28 | virtual int pass_value2(int& a) { return a; } 29 | virtual int pass_value3(const int& a) { return a; } 30 | virtual int pass_value4(const Base1& b) { return b.m_int; } 31 | virtual int pass_value5(Base1& b) { return b.m_int; } 32 | static int sum_pass_value(Base1* b); 33 | 34 | public: 35 | int m_int; 36 | }; 37 | 38 | class IBase2 { 39 | public: 40 | IBase2() {} 41 | virtual ~IBase2() {} 42 | virtual int get_value() = 0; 43 | static int call_get_value(IBase2* b); 44 | }; 45 | 46 | class IBase3 : IBase2 { 47 | public: 48 | IBase3(int); 49 | int m_int; 50 | }; 51 | 52 | class CBase2 : public IBase2 { 53 | public: 54 | int get_value(); 55 | }; 56 | 57 | class IBase4 { 58 | public: 59 | IBase4() {} 60 | virtual ~IBase4() {} 61 | virtual int get_value() const = 0; // <- const, as opposed to IBase2 62 | static int call_get_value(IBase4* b); 63 | }; 64 | 65 | class CBase4 : public IBase4 { 66 | public: 67 | int get_value() const; 68 | }; 69 | 70 | template 71 | class TBase1 { 72 | public: 73 | virtual ~TBase1() {} 74 | virtual int get_value() { 75 | return 42; 76 | } 77 | }; 78 | 79 | class TDerived1 : public TBase1 { 80 | public: 81 | int get_value(); 82 | }; 83 | 84 | using TBase1_I = TBase1; 85 | 86 | class CountableBase { 87 | public: 88 | CountableBase(); 89 | CountableBase(const CountableBase&); 90 | CountableBase& operator=(const CountableBase&); 91 | virtual ~CountableBase(); 92 | 93 | virtual int call(); 94 | 95 | static int s_count; 96 | }; 97 | 98 | class Component { 99 | public: 100 | Component(); 101 | Component(const Component&) = delete; 102 | Component& operator=(const Component&) = delete; 103 | virtual ~Component(); 104 | 105 | static int get_count(); 106 | 107 | private: 108 | static int s_count; 109 | }; 110 | 111 | Component* build_component(int value); 112 | Component* cycle_component(Component* c); 113 | 114 | } // namespace CrossInheritance 115 | 116 | 117 | //=========================================================================== 118 | namespace AccessProtected { // for protected member testing 119 | 120 | class MyBase { 121 | public: 122 | MyBase(); 123 | virtual ~MyBase(); 124 | 125 | protected: 126 | virtual int get_data_v(); 127 | int get_data(); 128 | 129 | protected: 130 | int my_data; 131 | }; 132 | 133 | } // AccessProtected 134 | 135 | #endif // !CPPYY_TEST_CROSSINHERITANCE_H 136 | -------------------------------------------------------------------------------- /test/crossinheritance.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/datatypes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /test/doc_args_funcs.py: -------------------------------------------------------------------------------- 1 | def ann_f1(arg: 'int') -> 'double': 2 | return 3.1415*arg 3 | 4 | def ann_f2(arg1: 'int', arg2: 'int') -> 'int': 5 | return 3*arg1*arg2 6 | -------------------------------------------------------------------------------- /test/doc_helper.cxx: -------------------------------------------------------------------------------- 1 | #include "doc_helper.h" 2 | 3 | void DocHelper::throw_an_error(int i) { 4 | if (i) throw SomeError{"this is an error"}; 5 | throw SomeOtherError{"this is another error"}; 6 | } 7 | -------------------------------------------------------------------------------- /test/doc_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef CPPYY_TEST_DOCHELPER_H 2 | #define CPPYY_TEST_DOCHELPER_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | class SomeError : public std::exception { 9 | public: 10 | explicit SomeError(const std::string& msg) : fMsg(msg) {} 11 | SomeError(const SomeError& s) : fMsg(s.fMsg) {} 12 | 13 | const char* what() const throw() override { return fMsg.c_str(); } 14 | 15 | private: 16 | std::string fMsg; 17 | }; 18 | 19 | class SomeOtherError : public SomeError { 20 | public: 21 | explicit SomeOtherError(const std::string& msg) : SomeError(msg) {} 22 | SomeOtherError(const SomeOtherError& s) : SomeError(s) {} 23 | }; 24 | 25 | namespace DocHelper { 26 | 27 | void throw_an_error(int i); 28 | 29 | } // namespace DocHelper 30 | 31 | #endif // !CPPYY_TEST_DOCHELPER_H 32 | -------------------------------------------------------------------------------- /test/doc_helper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/example01.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "example01.h" 7 | 8 | //=========================================================================== 9 | payload::payload(double d) : m_data(d) { 10 | count++; 11 | } 12 | payload::payload(const payload& p) : m_data(p.m_data) { 13 | count++; 14 | } 15 | payload& payload::operator=(const payload& p) { 16 | if (this != &p) { 17 | m_data = p.m_data; 18 | } 19 | return *this; 20 | } 21 | payload::~payload() { 22 | count--; 23 | } 24 | 25 | double payload::getData() { return m_data; } 26 | void payload::setData(double d) { m_data = d; } 27 | 28 | // class-level data 29 | int payload::count = 0; 30 | 31 | 32 | //=========================================================================== 33 | example01::example01() : m_somedata(-99) { 34 | count++; 35 | } 36 | example01::example01(int a) : m_somedata(a) { 37 | count++; 38 | } 39 | example01::example01(const example01& e) : m_somedata(e.m_somedata) { 40 | count++; 41 | } 42 | example01& example01::operator=(const example01& e) { 43 | if (this != &e) { 44 | m_somedata = e.m_somedata; 45 | } 46 | return *this; 47 | } 48 | example01::~example01() { 49 | count--; 50 | } 51 | 52 | // class-level methods 53 | int example01::staticAddOneToInt(int a) { 54 | return a + 1; 55 | } 56 | int example01::staticAddOneToInt(int a, int b) { 57 | return a + b + 1; 58 | } 59 | double example01::staticAddToDouble(double a) { 60 | return a + 0.01; 61 | } 62 | int example01::staticAtoi(const char* str) { 63 | return ::atoi(str); 64 | } 65 | char* example01::staticStrcpy(const char* strin) { 66 | char* strout = (char*)malloc(::strlen(strin)+1); 67 | ::strcpy(strout, strin); 68 | return strout; 69 | } 70 | void example01::staticSetPayload(payload* p, double d) { 71 | p->setData(d); 72 | } 73 | 74 | payload* example01::staticCyclePayload(payload* p, double d) { 75 | staticSetPayload(p, d); 76 | return p; 77 | } 78 | 79 | payload example01::staticCopyCyclePayload(payload* p, double d) { 80 | staticSetPayload(p, d); 81 | return *p; 82 | } 83 | 84 | int example01::getCount() { 85 | return count; 86 | } 87 | 88 | void example01::setCount(int value) { 89 | count = value; 90 | } 91 | 92 | // instance methods 93 | int example01::addDataToInt(int a) { 94 | return m_somedata + a; 95 | } 96 | 97 | int example01::addDataToIntConstRef(const int& a) { 98 | return m_somedata + a; 99 | } 100 | 101 | int example01::overloadedAddDataToInt(int a, int b) { 102 | return m_somedata + a + b; 103 | } 104 | 105 | int example01::overloadedAddDataToInt(int a) { 106 | return m_somedata + a; 107 | } 108 | 109 | int example01::overloadedAddDataToInt(int a, int b, int c) { 110 | return m_somedata + a + b + c; 111 | } 112 | 113 | double example01::addDataToDouble(double a) { 114 | return m_somedata + a; 115 | } 116 | 117 | int example01::addDataToAtoi(const char* str) { 118 | return ::atoi(str) + m_somedata; 119 | } 120 | 121 | char* example01::addToStringValue(const char* str) { 122 | int out = ::atoi(str) + m_somedata; 123 | std::ostringstream ss; 124 | ss << out << std::ends; 125 | std::string result = ss.str(); 126 | char* cresult = (char*)malloc(result.size()+1); 127 | ::strcpy(cresult, result.c_str()); 128 | return cresult; 129 | } 130 | 131 | void example01::setPayload(payload* p) { 132 | p->setData(m_somedata); 133 | } 134 | 135 | payload* example01::cyclePayload(payload* p) { 136 | setPayload(p); 137 | return p; 138 | } 139 | 140 | payload example01::copyCyclePayload(payload* p) { 141 | setPayload(p); 142 | return *p; 143 | } 144 | 145 | // class-level data 146 | int example01::count = 0; 147 | 148 | 149 | // global 150 | int globalAddOneToInt(int a) { 151 | return a + 1; 152 | } 153 | 154 | int ns_example01::globalAddOneToInt(int a) { 155 | return ::globalAddOneToInt(a); 156 | } 157 | 158 | int installableAddOneToInt(example01& e, int a) { 159 | return e.staticAddOneToInt(a); 160 | } 161 | 162 | int ns_example01::gMyGlobalInt = 99; 163 | 164 | 165 | // argument passing 166 | #define typeValueImp(itype, tname) \ 167 | itype ArgPasser::tname##Value(itype arg0, int argn, itype arg1, itype arg2) \ 168 | { \ 169 | switch (argn) { \ 170 | case 0: \ 171 | return arg0; \ 172 | case 1: \ 173 | return arg1; \ 174 | case 2: \ 175 | return arg2; \ 176 | default: \ 177 | break; \ 178 | } \ 179 | \ 180 | return (itype)-1; \ 181 | } 182 | 183 | typeValueImp(short, short) 184 | typeValueImp(unsigned short, ushort) 185 | typeValueImp(int, int) 186 | typeValueImp(unsigned int, uint) 187 | typeValueImp(long, long) 188 | typeValueImp(unsigned long, ulong) 189 | 190 | typeValueImp(float, float) 191 | typeValueImp(double, double) 192 | 193 | std::string ArgPasser::stringValue(std::string arg0, int argn, std::string arg1) 194 | { 195 | switch (argn) { 196 | case 0: 197 | return arg0; 198 | case 1: 199 | return arg1; 200 | default: 201 | break; 202 | } 203 | 204 | return "argn invalid"; 205 | } 206 | 207 | std::string ArgPasser::stringRef(const std::string& arg0, int argn, const std::string& arg1) 208 | { 209 | return stringValue(arg0, argn, arg1); 210 | } 211 | 212 | 213 | // special case naming 214 | z_& z_::gime_z_(z_& z) { return z; } 215 | -------------------------------------------------------------------------------- /test/example01.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class payload { 4 | public: 5 | payload(double d = 0.); 6 | payload(const payload& p); 7 | payload& operator=(const payload& e); 8 | ~payload(); 9 | 10 | double getData(); 11 | void setData(double d); 12 | 13 | public: // class-level data 14 | static int count; 15 | 16 | private: 17 | double m_data; 18 | }; 19 | 20 | 21 | class example01 { 22 | public: 23 | example01(); 24 | example01(int a); 25 | example01(const example01& e); 26 | example01& operator=(const example01& e); 27 | virtual ~example01(); 28 | 29 | public: // class-level methods 30 | static int staticAddOneToInt(int a); 31 | static int staticAddOneToInt(int a, int b); 32 | static double staticAddToDouble(double a); 33 | static int staticAtoi(const char* str); 34 | static char* staticStrcpy(const char* strin); 35 | static void staticSetPayload(payload* p, double d); 36 | static payload* staticCyclePayload(payload* p, double d); 37 | static payload staticCopyCyclePayload(payload* p, double d); 38 | static int getCount(); 39 | static void setCount(int); 40 | 41 | public: // instance methods 42 | int addDataToInt(int a); 43 | int addDataToIntConstRef(const int& a); 44 | int overloadedAddDataToInt(int a, int b); 45 | int overloadedAddDataToInt(int a); 46 | int overloadedAddDataToInt(int a, int b, int c); 47 | double addDataToDouble(double a); 48 | int addDataToAtoi(const char* str); 49 | char* addToStringValue(const char* str); 50 | 51 | void setPayload(payload* p); 52 | payload* cyclePayload(payload* p); 53 | payload copyCyclePayload(payload* p); 54 | 55 | public: // class-level data 56 | static int count; 57 | 58 | public: // instance data 59 | int m_somedata; 60 | }; 61 | 62 | 63 | // global functions and data 64 | int globalAddOneToInt(int a); 65 | namespace ns_example01 { 66 | int globalAddOneToInt(int a); 67 | extern int gMyGlobalInt; 68 | } 69 | 70 | int installableAddOneToInt(example01&, int a); 71 | 72 | #define itypeValue(itype, tname) \ 73 | itype tname##Value(itype arg0, int argn=0, itype arg1=1, itype arg2=2) 74 | 75 | #define ftypeValue(ftype) \ 76 | ftype ftype##Value(ftype arg0, int argn=0, ftype arg1=1., ftype arg2=2.) 77 | 78 | 79 | // argument passing 80 | class ArgPasser { // use a class for now as methptrgetter not 81 | public: // implemented for global functions 82 | itypeValue(short, short); 83 | itypeValue(unsigned short, ushort); 84 | itypeValue(int, int); 85 | itypeValue(unsigned int, uint); 86 | itypeValue(long, long); 87 | itypeValue(unsigned long, ulong); 88 | 89 | ftypeValue(float); 90 | ftypeValue(double); 91 | 92 | std::string stringValue( 93 | std::string arg0, int argn=0, std::string arg1 = "default"); 94 | 95 | std::string stringRef( 96 | const std::string& arg0, int argn=0, const std::string& arg1="default"); 97 | }; 98 | 99 | 100 | // typedefs 101 | typedef example01 example01_t; 102 | 103 | 104 | // special case naming 105 | class z_ { 106 | public: 107 | z_& gime_z_(z_& z); 108 | int myint; 109 | }; 110 | 111 | // for pythonization checking 112 | class example01a : public example01 { 113 | public: 114 | example01a(int a) : example01(a) {} 115 | }; 116 | -------------------------------------------------------------------------------- /test/example01.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/fragile.cxx: -------------------------------------------------------------------------------- 1 | #include "fragile.h" 2 | #include 3 | 4 | 5 | fragile::H::HH* fragile::H::HH::copy() { 6 | return (HH*)0; 7 | } 8 | 9 | fragile::I fragile::gI; 10 | 11 | void fragile::fglobal(int, double, char) { 12 | /* empty; only used for doc-string testing */ 13 | } 14 | 15 | namespace fragile { 16 | 17 | class Kderived : public K { 18 | public: 19 | virtual ~Kderived(); 20 | }; 21 | 22 | } // namespace fragile 23 | 24 | fragile::Kderived::~Kderived() {} 25 | 26 | fragile::K::~K() {} 27 | 28 | fragile::K* fragile::K::GimeK(bool derived) { 29 | if (!derived) return this; 30 | else { 31 | static Kderived kd; 32 | return &kd; 33 | } 34 | }; 35 | 36 | fragile::K* fragile::K::GimeL() { 37 | static L l; 38 | return &l; 39 | } 40 | 41 | fragile::L::~L() {} 42 | 43 | 44 | int fragile::create_handle(OpaqueHandle_t* handle) { 45 | *handle = (OpaqueHandle_t)0x01; 46 | return 0x01; 47 | } 48 | 49 | int fragile::destroy_handle(OpaqueHandle_t handle, intptr_t addr) { 50 | if ((intptr_t)handle == addr) 51 | return 1; 52 | return 0; 53 | } 54 | 55 | 56 | // for signal -> exception testing 57 | void fragile::segfault() { 58 | int* i = 0; *i = 42; 59 | } 60 | 61 | void fragile::sigabort() { 62 | assert(0); 63 | } 64 | 65 | 66 | // for duplicate testing 67 | int fragile::add42(int i) { 68 | return i + 42; 69 | } 70 | -------------------------------------------------------------------------------- /test/fragile.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace fragile { 5 | 6 | class no_such_class; 7 | 8 | class A { 9 | public: 10 | virtual int check() { return (int)'A'; } 11 | virtual A* gime_null() { return (A*)0; } 12 | }; 13 | 14 | class B { 15 | public: 16 | virtual int check() { return (int)'B'; } 17 | no_such_class* gime_no_such() { return 0; } 18 | }; 19 | 20 | class C { 21 | public: 22 | virtual int check() { return (int)'C'; } 23 | void use_no_such(no_such_class*) {} 24 | }; 25 | 26 | class D { 27 | public: 28 | virtual int check() { return (int)'D'; } 29 | virtual int check(int, int) { return (int)'D'; } 30 | void overload() {} 31 | void overload(no_such_class*) {} 32 | void overload(char, int i = 0) {} // Reflex requires a named arg 33 | void overload(int, no_such_class* p = 0) {} 34 | }; 35 | 36 | 37 | static const uintptr_t dummy_location = 0xdead; 38 | 39 | class E { 40 | public: 41 | E() : m_pp_no_such((no_such_class**)&dummy_location), m_pp_a(0) {} 42 | 43 | virtual int check() { return (int)'E'; } 44 | void overload(no_such_class**) {} 45 | 46 | no_such_class** m_pp_no_such; 47 | A** m_pp_a; 48 | }; 49 | 50 | class F { 51 | public: 52 | F() : m_int(0) {} 53 | virtual int check() { return (int)'F'; } 54 | int m_int; 55 | }; 56 | 57 | class G { 58 | public: 59 | enum { unnamed1=24, unnamed2=96 }; 60 | 61 | class GG {}; 62 | }; 63 | 64 | class H { 65 | public: 66 | class HH { 67 | public: 68 | HH* copy(); 69 | }; 70 | HH* m_h; 71 | }; 72 | 73 | class I { 74 | public: 75 | operator bool() { return 0; } 76 | }; 77 | 78 | extern I gI; 79 | 80 | class J { 81 | public: 82 | int method1(int, double) { return 0; } 83 | }; 84 | 85 | void fglobal(int, double, char); 86 | 87 | namespace nested1 { 88 | class A {}; 89 | namespace nested2 { 90 | class A {}; 91 | namespace nested3 { 92 | class A {}; 93 | } // namespace nested3 94 | } // namespace nested2 95 | } // namespace nested1 96 | 97 | class K { 98 | public: 99 | virtual ~K(); 100 | K* GimeK(bool derived); 101 | K* GimeL(); 102 | }; 103 | 104 | class L : public K { 105 | public: 106 | virtual ~L(); 107 | no_such_class* m_no_such; 108 | }; 109 | 110 | class M { 111 | public: 112 | virtual ~M() {} 113 | enum E1 { kOnce=42 }; 114 | enum E2 { kTwice=12 }; 115 | }; 116 | 117 | class N : public M { 118 | public: 119 | enum E2 { kTwice=12 }; 120 | }; 121 | 122 | class O { 123 | public: 124 | virtual int abstract() = 0; 125 | }; 126 | 127 | class OpaqueType; 128 | typedef OpaqueType* OpaqueHandle_t; 129 | 130 | int create_handle(OpaqueHandle_t* handle); 131 | int destroy_handle(OpaqueHandle_t handle, intptr_t addr); 132 | 133 | 134 | // for signal -> exception testing 135 | void segfault(); 136 | void sigabort(); 137 | 138 | 139 | // for duplicate testing 140 | int add42(int i); 141 | 142 | } // namespace fragile 143 | -------------------------------------------------------------------------------- /test/fragile.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /test/make_dict_win32.py: -------------------------------------------------------------------------------- 1 | import glob, os, sys, subprocess 2 | 3 | USES_PYTHON_CAPI = set(('pythonizables',)) 4 | 5 | fn = sys.argv[1] 6 | 7 | if fn == 'all': 8 | all_headers = glob.glob('*.h') 9 | for header in all_headers: 10 | res = os.system(" ".join(['python', sys.argv[0], header[:-2]]+sys.argv[2:])) 11 | if res != 0: 12 | sys.exit(res) 13 | sys.exit(0) 14 | else: 15 | if fn[-4:] == '.cxx': fn = fn[:-4] 16 | elif fn[-2:] == '.h': fn = fn[:-2] 17 | if not os.path.exists(fn+'.h'): 18 | print("file %s.h does not exist" % (fn,)) 19 | sys.exit(1) 20 | 21 | uses_python_capi = False 22 | if fn in USES_PYTHON_CAPI: 23 | uses_python_capi = True 24 | 25 | if os.path.exists(fn+'Dict.dll'): 26 | dct_time = os.stat(fn+'Dict.dll').st_mtime 27 | if not '-f' in sys.argv: 28 | mustbuild = False 29 | for ext in ['.h', '.cxx', '.xml']: 30 | if os.stat(fn+ext).st_mtime > dct_time: 31 | mustbuild = True 32 | break 33 | if not mustbuild: 34 | sys.exit(0) 35 | 36 | # cleanup 37 | for fg in set(glob.glob(fn+"_rflx*") + glob.glob(fn+"Dict*") + \ 38 | glob.glob("*.obj") + glob.glob(fn+"Linkdef.h")): 39 | os.remove(fg) 40 | 41 | def _get_config_exec(): 42 | return [sys.executable, '-m', 'cppyy_backend._cling_config'] 43 | 44 | def get_config(what): 45 | config_exec_args = _get_config_exec() 46 | config_exec_args.append('--'+what) 47 | cli_arg = subprocess.check_output(config_exec_args) 48 | return cli_arg.decode("utf-8").strip() 49 | 50 | def get_python_include_dir(): 51 | incdir = subprocess.check_output([sys.executable, '-c', "import sysconfig; print(sysconfig.get_path('include'))"]) 52 | return incdir.decode("utf-8").strip() 53 | 54 | def get_python_lib_dir(): 55 | libdir = subprocess.check_output([sys.executable, '-c', "import sysconfig; print(sysconfig.get_path('stdlib'))"]) 56 | return os.path.join(os.path.dirname(libdir.decode("utf-8").strip()), 'libs') 57 | 58 | # genreflex option 59 | #DICTIONARY_CMD = "genreflex {fn}.h --selection={fn}.xml --rootmap={fn}Dict.rootmap --rootmap-lib={fn}Dict.dll".format(fn=fn) 60 | 61 | with open(fn+'Linkdef.h', 'w') as linkdef: 62 | linkdef.write("#ifdef __CLING__\n\n") 63 | linkdef.write("#pragma link C++ defined_in %s.h;\n" % fn) 64 | linkdef.write("\n#endif") 65 | 66 | DICTIONARY_CMD = "python -m cppyy_backend._rootcling -f {fn}_rflx.cxx -rmf {fn}Dict.rootmap -rml {fn}Dict.dll {fn}.h {fn}Linkdef.h".format(fn=fn) 67 | if os.system(DICTIONARY_CMD): 68 | sys.exit(1) 69 | 70 | import platform 71 | if '64' in platform.architecture()[0]: 72 | PLATFORMFLAG = '-D_AMD64_' 73 | MACHINETYPE = 'X64' 74 | else: 75 | PLATFORMFLAG = '-D_X86_' 76 | MACHINETYPE = 'IX86' 77 | 78 | cppflags = get_config('cppflags') 79 | if uses_python_capi: 80 | cppflags += ' -I"' + get_python_include_dir() + '"' 81 | BUILDOBJ_CMD_PART = "cl -O2 -nologo -TP -c -nologo " + cppflags + " -FIsehmap.h -MD -GR -D_WINDOWS -DWIN32 " + PLATFORMFLAG + " -EHsc- -W3 -wd4141 -wd4291 -wd4244 -wd4049 -D_XKEYCHECK_H -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS {fn}.cxx -Fo{fn}.obj" 82 | BUILDOBJ_CMD = BUILDOBJ_CMD_PART.format(fn=fn) 83 | if os.system(BUILDOBJ_CMD): 84 | sys.exit(1) 85 | BUILDOBJ_CMD = BUILDOBJ_CMD_PART.format(fn=fn+'_rflx') 86 | if os.system(BUILDOBJ_CMD): 87 | sys.exit(1) 88 | 89 | import cppyy_backend 90 | CREATEDEF_CMD = "python bindexplib.py {fn} {fn}Dict".format(fn=fn) 91 | if os.system(CREATEDEF_CMD): 92 | sys.exit(1) 93 | 94 | ldflags = '' 95 | if uses_python_capi: 96 | ldflags = ' /LIBPATH:"' + get_python_lib_dir() + '" ' 97 | CREATELIB_CMD = ("lib -nologo -MACHINE:" + MACHINETYPE + " -out:{fn}Dict.lib {fn}.obj {fn}_rflx.obj -def:{fn}Dict.def " + ldflags).format(fn=fn) 98 | if os.system(CREATELIB_CMD): 99 | sys.exit(1) 100 | 101 | ldflags += get_config('ldflags') 102 | LINKDLL_CMD = ("link -nologo {fn}.obj {fn}_rflx.obj -DLL -out:{fn}Dict.dll {fn}Dict.exp " + ldflags).format(fn=fn) 103 | if os.system(LINKDLL_CMD): 104 | sys.exit(1) 105 | 106 | # cleanup 107 | for fg in set(glob.glob(fn+"_rflx.cxx*") + glob.glob("*.obj") + glob.glob(fn+"Linkdef.h")): 108 | os.remove(fg) 109 | -------------------------------------------------------------------------------- /test/operators.cxx: -------------------------------------------------------------------------------- 1 | #include "operators.h" 2 | 3 | // for testing the case of virtual operator== 4 | v_opeq_base::v_opeq_base(int val) : m_val(val) {} 5 | v_opeq_base::~v_opeq_base() {} 6 | 7 | bool v_opeq_base::operator==(const v_opeq_base& other) { 8 | return m_val == other.m_val; 9 | } 10 | 11 | v_opeq_derived::v_opeq_derived(int val) : v_opeq_base(val) {} 12 | v_opeq_derived::~v_opeq_derived() {} 13 | 14 | bool v_opeq_derived::operator==(const v_opeq_derived& other) { 15 | return m_val != other.m_val; 16 | } 17 | 18 | 19 | // for indexing tests 20 | int& YAMatrix1::operator() (int, int) { 21 | return m_val; 22 | } 23 | 24 | const int& YAMatrix1::operator() (int, int) const { 25 | return m_val; 26 | } 27 | 28 | //- 29 | int& YAMatrix2::operator[] (int) { 30 | return m_val; 31 | } 32 | 33 | const int& YAMatrix2::operator[] (int) const { 34 | return m_val; 35 | } 36 | 37 | //- 38 | int& YAMatrix3::operator() (int, int) { 39 | return m_val; 40 | } 41 | 42 | const int& YAMatrix3::operator() (int, int) const { 43 | return m_val; 44 | } 45 | 46 | int& YAMatrix3::operator[] (int) { 47 | return m_val; 48 | } 49 | 50 | const int& YAMatrix3::operator[] (int) const { 51 | return m_val; 52 | } 53 | 54 | //- 55 | int& YAMatrix4::operator[] (int) { 56 | return m_val; 57 | } 58 | 59 | const int& YAMatrix4::operator[] (int) const { 60 | return m_val; 61 | } 62 | 63 | int& YAMatrix4::operator() (int, int) { 64 | return m_val; 65 | } 66 | 67 | const int& YAMatrix4::operator() (int, int) const { 68 | return m_val; 69 | } 70 | 71 | //- 72 | int& YAMatrix5::operator[] (int) { 73 | return m_val; 74 | } 75 | 76 | int YAMatrix5::operator[] (int) const { 77 | return m_val; 78 | } 79 | 80 | int& YAMatrix5::operator() (int, int) { 81 | return m_val; 82 | } 83 | 84 | int YAMatrix5::operator() (int, int) const { 85 | return m_val; 86 | } 87 | 88 | //- 89 | int& YAMatrix6::operator[] (int) { 90 | return m_val; 91 | } 92 | 93 | int YAMatrix6::operator[] (int) const { 94 | return m_val; 95 | } 96 | 97 | int& YAMatrix6::operator() (int, int) { 98 | return m_val; 99 | } 100 | 101 | //- 102 | int& YAMatrix7::operator[] (int) { 103 | return m_val; 104 | } 105 | 106 | int& YAMatrix7::operator() (int, int) { 107 | return m_val; 108 | } 109 | 110 | 111 | //- for __radd__/__rmul__, non-associative 112 | AssocADD operator+(int n, AssocADD& a) { 113 | return AssocADD(n+a.fx); 114 | } 115 | 116 | AssocADD operator+(AssocADD& a, int n) { 117 | return AssocADD(a.fx+n); 118 | } 119 | 120 | NonAssocRADD operator+(int n, NonAssocRADD& a) { 121 | return NonAssocRADD(n+a.fx); 122 | } 123 | 124 | AssocMUL operator*(int n, AssocMUL& m) { 125 | return AssocMUL(n*m.fx); 126 | } 127 | 128 | AssocMUL operator*(AssocMUL& m, int n) { 129 | return AssocMUL(m.fx*n); 130 | } 131 | 132 | NonAssocRMUL operator*(int n, NonAssocRMUL& m) { 133 | return NonAssocRMUL(n*m.fx); 134 | } 135 | 136 | 137 | //- for multi-lookup 138 | double MultiLookup::operator*(const Vector2& v1, const Vector2& v2) { 139 | return v1.x*v2.x + v1.y*v2.y; 140 | } 141 | 142 | MultiLookup::Vector2 MultiLookup::operator*(const Vector2& v, double a) { 143 | return Vector2{v.x*a, v.y*a}; 144 | } 145 | 146 | double MultiLookup::operator/(const Vector2& v1, const Vector2& v2) { 147 | return v1.x/v2.x + v1.y/v2.y; 148 | } 149 | 150 | MultiLookup::Vector2 MultiLookup::operator/(const Vector2& v, double a) { 151 | return Vector2{v.x/a, v.y/a}; 152 | } 153 | 154 | double MultiLookup::operator+(const Vector2& v1, const Vector2& v2) { 155 | return v1.x+v2.x + v1.y+v2.y; 156 | } 157 | 158 | MultiLookup::Vector2 MultiLookup::operator+(const Vector2& v, double a) { 159 | return Vector2{v.x+a, v.y+a}; 160 | } 161 | 162 | double MultiLookup::operator-(const Vector2& v1, const Vector2& v2) { 163 | return v1.x-v2.x + v1.y-v2.y; 164 | } 165 | 166 | MultiLookup::Vector2 MultiLookup::operator-(const Vector2& v, double a) { 167 | return Vector2{v.x-a, v.y-a}; 168 | } 169 | 170 | 171 | //- for unary functions 172 | SomeGlobalNumber operator-(const SomeGlobalNumber& n) { 173 | return SomeGlobalNumber{-n.i}; 174 | } 175 | 176 | SomeGlobalNumber operator+(const SomeGlobalNumber& n) { 177 | return SomeGlobalNumber{+n.i}; 178 | } 179 | 180 | SomeGlobalNumber operator~(const SomeGlobalNumber& n) { 181 | return SomeGlobalNumber{~n.i}; 182 | } 183 | 184 | Unary::SomeNumber Unary::operator-(const SomeNumber& n) { 185 | return SomeNumber{-n.i}; 186 | } 187 | 188 | Unary::SomeNumber Unary::operator+(const SomeNumber& n) { 189 | return SomeNumber{+n.i}; 190 | } 191 | 192 | Unary::SomeNumber Unary::operator~(const SomeNumber& n) { 193 | return SomeNumber{~n.i}; 194 | } 195 | -------------------------------------------------------------------------------- /test/operators.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/overloads.cxx: -------------------------------------------------------------------------------- 1 | #include "overloads.h" 2 | 3 | 4 | a_overload::a_overload() { i1 = 42; i2 = -1; } 5 | 6 | ns_a_overload::a_overload::a_overload() { i1 = 88; i2 = -34; } 7 | int ns_a_overload::b_overload::f(const std::vector* v) { return (*v)[0]; } 8 | 9 | ns_b_overload::a_overload::a_overload() { i1 = -33; i2 = 89; } 10 | 11 | b_overload::b_overload() { i1 = -2; i2 = 13; } 12 | 13 | c_overload::c_overload() {} 14 | int c_overload::get_int(a_overload* a) { return a->i1; } 15 | int c_overload::get_int(ns_a_overload::a_overload* a) { return a->i1; } 16 | int c_overload::get_int(ns_b_overload::a_overload* a) { return a->i1; } 17 | int c_overload::get_int(short* p) { return *p; } 18 | int c_overload::get_int(b_overload* b) { return b->i2; } 19 | int c_overload::get_int(int* p) { return *p; } 20 | 21 | d_overload::d_overload() {} 22 | int d_overload::get_int(int* p) { return *p; } 23 | int d_overload::get_int(b_overload* b) { return b->i2; } 24 | int d_overload::get_int(short* p) { return *p; } 25 | int d_overload::get_int(ns_b_overload::a_overload* a) { return a->i1; } 26 | int d_overload::get_int(ns_a_overload::a_overload* a) { return a->i1; } 27 | int d_overload::get_int(a_overload* a) { return a->i1; } 28 | 29 | class bb_ol {}; 30 | bb_ol* get_bb_ol() { return new bb_ol{}; } 31 | class dd_ol {}; 32 | dd_ol* get_dd_ol() { return new dd_ol{}; } 33 | 34 | more_overloads::more_overloads() {} 35 | std::string more_overloads::call(const aa_ol&) { return "aa_ol"; } 36 | std::string more_overloads::call(const bb_ol&, void* n) { n = 0; return "bb_ol"; } 37 | std::string more_overloads::call(const cc_ol&) { return "cc_ol"; } 38 | std::string more_overloads::call(const dd_ol&) { return "dd_ol"; } 39 | 40 | std::string more_overloads::call_unknown(const dd_ol&) { return "dd_ol"; } 41 | 42 | std::string more_overloads::call(double) { return "double"; } 43 | std::string more_overloads::call(int) { return "int"; } 44 | std::string more_overloads::call1(int) { return "int"; } 45 | std::string more_overloads::call1(double) { return "double"; } 46 | 47 | 48 | more_overloads2::more_overloads2() {} 49 | std::string more_overloads2::call(const bb_ol&) { return "bb_olref"; } 50 | std::string more_overloads2::call(const bb_ol*) { return "bb_olptr"; } 51 | 52 | std::string more_overloads2::call(const dd_ol*, int) { return "dd_olptr"; } 53 | std::string more_overloads2::call(const dd_ol&, int) { return "dd_olref"; } 54 | 55 | 56 | double calc_mean(long n, const float* a) { return calc_mean(n, a); } 57 | double calc_mean(long n, const double* a) { return calc_mean(n, a); } 58 | double calc_mean(long n, const int* a) { return calc_mean(n, a); } 59 | double calc_mean(long n, const short* a) { return calc_mean(n, a); } 60 | double calc_mean(long n, const long* a) { return calc_mean(n, a); } 61 | 62 | 63 | std::string more_overloads3::slice(size_t) const { return "const"; } 64 | std::string more_overloads3::slice(size_t) { return "non-const"; } 65 | std::string more_overloads3::slice(size_t, size_t) const { return "const"; } 66 | std::string more_overloads3::slice(size_t, size_t) { return "non-const"; } 67 | 68 | -------------------------------------------------------------------------------- /test/overloads.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class a_overload { 5 | public: 6 | a_overload(); 7 | int i1, i2; 8 | }; 9 | 10 | namespace ns_a_overload { 11 | class a_overload { 12 | public: 13 | a_overload(); 14 | int i1, i2; 15 | }; 16 | 17 | class b_overload { 18 | public: 19 | int f(const std::vector* v); 20 | }; 21 | } 22 | 23 | namespace ns_b_overload { 24 | class a_overload { 25 | public: 26 | a_overload(); 27 | int i1, i2; 28 | }; 29 | } 30 | 31 | class b_overload { 32 | public: 33 | b_overload(); 34 | int i1, i2; 35 | }; 36 | 37 | class c_overload { 38 | public: 39 | c_overload(); 40 | int get_int(a_overload* a); 41 | int get_int(ns_a_overload::a_overload* a); 42 | int get_int(ns_b_overload::a_overload* a); 43 | int get_int(short* p); 44 | int get_int(b_overload* b); 45 | int get_int(int* p); 46 | }; 47 | 48 | class d_overload { 49 | public: 50 | d_overload(); 51 | // int get_int(void* p) { return *(int*)p; } 52 | int get_int(int* p); 53 | int get_int(b_overload* b); 54 | int get_int(short* p); 55 | int get_int(ns_b_overload::a_overload* a); 56 | int get_int(ns_a_overload::a_overload* a); 57 | int get_int(a_overload* a); 58 | }; 59 | 60 | 61 | class aa_ol {}; 62 | class bb_ol; 63 | bb_ol* get_bb_ol(); 64 | class cc_ol {}; 65 | class dd_ol; 66 | dd_ol* get_dd_ol(); 67 | 68 | 69 | class more_overloads { 70 | public: 71 | more_overloads(); 72 | std::string call(const aa_ol&); 73 | std::string call(const bb_ol&, void* n=0); 74 | std::string call(const cc_ol&); 75 | std::string call(const dd_ol&); 76 | 77 | std::string call_unknown(const dd_ol&); 78 | 79 | std::string call(double); 80 | std::string call(int); 81 | std::string call1(int); 82 | std::string call1(double); 83 | }; 84 | 85 | class more_overloads2 { 86 | public: 87 | more_overloads2(); 88 | std::string call(const bb_ol&); 89 | std::string call(const bb_ol*); 90 | 91 | std::string call(const dd_ol*, int); 92 | std::string call(const dd_ol&, int); 93 | }; 94 | 95 | template 96 | double calc_mean(long n, const T* a) { 97 | double sum = 0., sumw = 0.; 98 | const T* end = a+n; 99 | while (a != end) { 100 | sum += *a++; 101 | sumw += 1; 102 | } 103 | 104 | return sum/sumw; 105 | } 106 | 107 | double calc_mean(long n, const float* a); 108 | double calc_mean(long n, const double* a); 109 | double calc_mean(long n, const int* a); 110 | double calc_mean(long n, const short* a); 111 | double calc_mean(long n, const long* a); 112 | 113 | class more_overloads3 { 114 | public: 115 | std::string slice(size_t) const; 116 | std::string slice(size_t); 117 | std::string slice(size_t, size_t); 118 | std::string slice(size_t, size_t) const; 119 | }; 120 | -------------------------------------------------------------------------------- /test/overloads.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/pythonizables.cxx: -------------------------------------------------------------------------------- 1 | #include "pythonizables.h" 2 | 3 | #include "Python.h" 4 | 5 | 6 | //=========================================================================== 7 | pyzables::NakedBuffers::NakedBuffers(int size, double valx, double valy) : m_size(size) { 8 | m_Xbuf = new double[size]; 9 | m_Ybuf = new double[size]; 10 | 11 | for (int i=0; i(new Countable); } 43 | 44 | pyzables::SharedCountable_t pyzables::gime_mine() { return mine; } 45 | pyzables::SharedCountable_t* pyzables::gime_mine_ptr() { return &mine; } 46 | pyzables::SharedCountable_t& pyzables::gime_mine_ref() { return mine; } 47 | 48 | unsigned int pyzables::pass_mine_sp(std::shared_ptr ptr) { return ptr->m_check; } 49 | unsigned int pyzables::pass_mine_sp_ref(std::shared_ptr& ptr) { return ptr->m_check; } 50 | unsigned int pyzables::pass_mine_sp_ptr(std::shared_ptr* ptr) { return (*ptr)->m_check; } 51 | 52 | unsigned int pyzables::pass_mine_rp(Countable c) { return c.m_check; } 53 | unsigned int pyzables::pass_mine_rp_ref(const Countable& c) { return c.m_check; } 54 | unsigned int pyzables::pass_mine_rp_ptr(const Countable* c) { return c->m_check; } 55 | 56 | pyzables::Countable* pyzables::gime_naked_countable() { return new Countable{}; } 57 | 58 | 59 | //=========================================================================== 60 | pyzables::WithCallback1::WithCallback1(int i) : m_int(i) {} 61 | 62 | int pyzables::WithCallback1::get_int() { return m_int; } 63 | void pyzables::WithCallback1::set_int(int i) { m_int = i; } 64 | 65 | static inline void replace_method_name(PyObject* klass, const char* n1, const char* n2) { 66 | PyObject* meth = PyObject_GetAttrString(klass, n1); 67 | PyObject_SetAttrString(klass, n2, meth); 68 | Py_DECREF(meth); 69 | PyObject_DelAttrString(klass, n1); 70 | } 71 | 72 | void pyzables::WithCallback1::WithCallback1::__cppyy_explicit_pythonize__(PyObject* klass, const std::string& name) { 73 | // change methods to camel case 74 | replace_method_name(klass, "get_int", "GetInt"); 75 | replace_method_name(klass, "set_int", "SetInt"); 76 | 77 | // store the provided class name 78 | klass_name = name; 79 | } 80 | 81 | std::string pyzables::WithCallback1::klass_name{"not set"}; 82 | 83 | pyzables::WithCallback2::WithCallback2(int i) : m_int(i) {} 84 | 85 | int pyzables::WithCallback2::get_int() { return m_int; } 86 | void pyzables::WithCallback2::set_int(int i) { m_int = i; } 87 | 88 | void pyzables::WithCallback2::WithCallback2::__cppyy_pythonize__(PyObject* klass, const std::string& name) { 89 | // change methods to camel case 90 | replace_method_name(klass, "get_int", "GetInt"); 91 | replace_method_name(klass, "set_int", "SetInt"); 92 | 93 | // store the provided class name 94 | klass_name = name; 95 | } 96 | 97 | std::string pyzables::WithCallback2::klass_name{"not set"}; 98 | 99 | int pyzables::WithCallback3::get_int() { return 2*m_int; } 100 | void pyzables::WithCallback3::set_int(int i) { m_int = 2*i; } 101 | -------------------------------------------------------------------------------- /test/pythonizables.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Python 6 | struct _object; 7 | typedef _object PyObject; 8 | 9 | 10 | namespace pyzables { 11 | 12 | //=========================================================================== 13 | class SomeDummy1 {}; 14 | class SomeDummy2 {}; 15 | 16 | 17 | //=========================================================================== 18 | class NakedBuffers { 19 | public: 20 | NakedBuffers(int size, double valx, double valy); 21 | NakedBuffers(const NakedBuffers&) = delete; 22 | NakedBuffers& operator=(const NakedBuffers&) = delete; 23 | ~NakedBuffers(); 24 | 25 | public: 26 | int GetN(); 27 | double* GetX(); 28 | double* GetY(); 29 | 30 | private: 31 | double* m_Xbuf; 32 | double* m_Ybuf; 33 | int m_size; 34 | }; 35 | 36 | template 37 | class NakedBuffers2 { 38 | public: 39 | NakedBuffers2(int size, double valx, double valy) : m_Xbuf(size), m_Ybuf(size) { 40 | for (int i=0; i { 58 | public: 59 | Vector(int size) : std::vector(size) {} 60 | }; 61 | 62 | 63 | //=========================================================================== 64 | class MyBase { 65 | public: 66 | virtual ~MyBase(); 67 | }; 68 | class MyDerived : public MyBase { 69 | public: 70 | virtual ~MyDerived(); 71 | }; 72 | 73 | MyBase* GimeDerived(); 74 | 75 | 76 | //=========================================================================== 77 | class Countable { 78 | public: 79 | Countable() { ++sInstances; } 80 | Countable(const Countable&) { ++sInstances; } 81 | Countable& operator=(const Countable&) { return *this; } 82 | ~Countable() { --sInstances; } 83 | 84 | public: 85 | virtual const char* say_hi() { return "Hi!"; } 86 | 87 | public: 88 | unsigned int m_check = 0xcdcdcdcd; 89 | 90 | public: 91 | static int sInstances; 92 | }; 93 | 94 | typedef std::shared_ptr SharedCountable_t; 95 | extern SharedCountable_t mine; 96 | 97 | void renew_mine(); 98 | 99 | SharedCountable_t gime_mine(); 100 | SharedCountable_t* gime_mine_ptr(); 101 | SharedCountable_t& gime_mine_ref(); 102 | 103 | unsigned int pass_mine_sp(SharedCountable_t p); 104 | unsigned int pass_mine_sp_ref(SharedCountable_t& p); 105 | unsigned int pass_mine_sp_ptr(SharedCountable_t* p); 106 | 107 | unsigned int pass_mine_rp(Countable); 108 | unsigned int pass_mine_rp_ref(const Countable&); 109 | unsigned int pass_mine_rp_ptr(const Countable*); 110 | 111 | Countable* gime_naked_countable(); 112 | 113 | 114 | //=========================================================================== 115 | class unknown_iterator; 116 | class IndexableBase { 117 | public: 118 | unknown_iterator* begin() { return nullptr; } 119 | unknown_iterator* end() { return (unknown_iterator*)1; } 120 | int operator[](int) { return 42; } 121 | int size() { return 1; } 122 | }; 123 | 124 | class IndexableDerived : public IndexableBase {}; 125 | 126 | 127 | //=========================================================================== 128 | class WithCallback1 { 129 | public: 130 | WithCallback1(int i); 131 | 132 | public: 133 | int get_int(); 134 | void set_int(int i); 135 | 136 | private: 137 | int m_int; 138 | 139 | public: 140 | static void __cppyy_explicit_pythonize__(PyObject* klass, const std::string&); 141 | static std::string klass_name; 142 | }; 143 | 144 | class WithCallback2 { 145 | public: 146 | WithCallback2(int i); 147 | 148 | public: 149 | int get_int(); 150 | void set_int(int i); 151 | 152 | protected: 153 | int m_int; 154 | 155 | public: 156 | static void __cppyy_pythonize__(PyObject* klass, const std::string&); 157 | static std::string klass_name; 158 | }; 159 | 160 | class WithCallback3 : public WithCallback2 { 161 | public: 162 | using WithCallback2::WithCallback2; 163 | 164 | public: 165 | int get_int(); 166 | void set_int(int i); 167 | }; 168 | 169 | } // namespace pyzables 170 | -------------------------------------------------------------------------------- /test/pythonizables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/std_streams.cxx: -------------------------------------------------------------------------------- 1 | #include "std_streams.h" 2 | 3 | #ifndef _WIN32 4 | template class std::basic_ios >; 5 | #endif 6 | -------------------------------------------------------------------------------- /test/std_streams.h: -------------------------------------------------------------------------------- 1 | #ifndef STD_STREAMS_H 2 | #define STD_STREAMS_H 1 3 | 4 | #ifndef __CLING__ 5 | #include 6 | #endif 7 | #include 8 | 9 | #ifndef __CLING__ 10 | #ifndef _WIN32 11 | extern template class std::basic_ios >; 12 | #endif 13 | #endif 14 | 15 | #endif // STD_STREAMS_H 16 | -------------------------------------------------------------------------------- /test/std_streams.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/stltypes.cxx: -------------------------------------------------------------------------------- 1 | #include "stltypes.h" 2 | 3 | #include 4 | 5 | 6 | //- adverse effect of implicit conversion on vector 7 | int vectest_ol1(const std::vector&) { return 1; } 8 | int vectest_ol1(std::string) { return 2; } 9 | int vectest_ol2(std::string) { return 2; } 10 | int vectest_ol2(const std::vector&) { return 1; } 11 | 12 | 13 | //- helpers for testing array 14 | int ArrayTest::get_pp_px(Point** p, int idx) { 15 | return p[idx]->px; 16 | } 17 | 18 | int ArrayTest::get_pp_py(Point** p, int idx) { 19 | return p[idx]->py; 20 | } 21 | 22 | int ArrayTest::get_pa_px(Point* p[], int idx) { 23 | return p[idx]->px; 24 | } 25 | 26 | int ArrayTest::get_pa_py(Point* p[], int idx) { 27 | return p[idx]->py; 28 | } 29 | 30 | 31 | // helpers for string testing 32 | std::string str_array_1[3] = {"a", "b", "c"}; 33 | std::string str_array_2[] = {"d", "e", "f", "g"}; 34 | std::string str_array_3[3][2] = {{"a", "b"}, {"c", "d"}, {"e", "f"}}; 35 | std::string str_array_4[4][2][2] = { 36 | {{"a", "b"}, {"c", "d"}}, 37 | {{"e", "f"}, {"g", "h"}}, 38 | {{"i", "j"}, {"k", "l"}}, 39 | {{"m", "n"}, {"o", "p"}}, 40 | }; 41 | 42 | 43 | // helpers for mixing unicode and std::string 44 | size_t UnicodeAndSTL::get_size(std::string s) { return s.size(); } 45 | size_t UnicodeAndSTL::get_size_cr(const std::string& s ) { return s.size(); } 46 | size_t UnicodeAndSTL::get_size_cc(const char* s) { return strlen(s); } 47 | size_t UnicodeAndSTL::get_size_w(std::wstring s) { return s.size(); } 48 | size_t UnicodeAndSTL::get_size_wcr(const std::wstring& s) { return s.size(); } 49 | std::string UnicodeAndSTL::get_string(std::string s) { return s; } 50 | std::string UnicodeAndSTL::get_string_cr(const std::string& s) { return s; } 51 | std::string UnicodeAndSTL::get_string_cc(const char* s) { return s; } 52 | std::wstring UnicodeAndSTL::get_string_w(std::wstring s) { return s; } 53 | std::wstring UnicodeAndSTL::get_string_wcr(const std::wstring& s) { return s; } 54 | 55 | 56 | // helpers for string_view testing 57 | #if __cplusplus > 201402L 58 | std::string_view::size_type StringViewTest::count(const std::string_view arg) { 59 | return arg.size(); 60 | } 61 | 62 | std::string_view::size_type StringViewTest::count_cr(const std::string_view& arg) { 63 | return arg.size(); 64 | } 65 | #endif // __cplusplus > 201402L 66 | 67 | // helper for exception base class testing 68 | int MyError::s_count = 0; 69 | int MyError::get_count() { return s_count; } 70 | 71 | MyError::MyError(const std::string& msg) : fMsg(msg) { 72 | s_count += 1; 73 | } 74 | 75 | MyError::MyError(const MyError& other) : fMsg(other.fMsg) { 76 | s_count += 1; 77 | } 78 | 79 | MyError::~MyError() { 80 | s_count -= 1; 81 | } 82 | 83 | const char* MyError::what() const throw() { return fMsg.c_str(); } 84 | 85 | int GetMyErrorCount() { 86 | return MyError::s_count; 87 | } 88 | 89 | YourError::YourError(const std::string& msg) : MyError(msg) {} 90 | YourError::YourError(const YourError& s) : MyError(s) {} 91 | 92 | ErrorNamespace::MyError::MyError(const std::string& msg) : fMsg(msg) {} 93 | const char* ErrorNamespace::MyError::what() const throw() { return fMsg.c_str(); } 94 | 95 | void ErrorNamespace::throw_error(int i) { 96 | if (i == 0) throw ::MyError("first error"); 97 | else if (i == 1) throw ::YourError("second error"); 98 | else if (i == 2) throw MyError("third error"); 99 | throw YourError("fourth error"); 100 | } 101 | -------------------------------------------------------------------------------- /test/stltypes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /test/support.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import os, py, sys, subprocess 3 | 4 | currpath = py.path.local(__file__).dirpath() 5 | 6 | 7 | def setup_make(targetname): 8 | if sys.platform == 'win32': 9 | popen = subprocess.Popen([sys.executable, "make_dict_win32.py", targetname], cwd=str(currpath), 10 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 11 | else: 12 | popen = subprocess.Popen(["make", targetname+"Dict.so"], cwd=str(currpath), 13 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 14 | stdout, _ = popen.communicate() 15 | if popen.returncode: 16 | raise OSError("'make' failed:\n%s" % (stdout,)) 17 | 18 | if sys.hexversion >= 0x3000000: 19 | pylong = int 20 | pyunicode = str 21 | maxvalue = sys.maxsize 22 | else: 23 | pylong = long 24 | pyunicode = unicode 25 | maxvalue = sys.maxint 26 | 27 | IS_WINDOWS = 0 28 | if 'win32' in sys.platform: 29 | import platform 30 | if '64' in platform.architecture()[0]: 31 | IS_WINDOWS = 64 32 | maxvalue = 2**31-1 33 | else: 34 | IS_WINDOWS = 32 35 | 36 | IS_MAC_ARM = 0 37 | if 'darwin' in sys.platform: 38 | import platform 39 | if 'arm64' in platform.machine(): 40 | IS_MAC_ARM = 64 41 | os.environ["CPPYY_UNCAUGHT_QUIET"] = "1" 42 | 43 | try: 44 | import __pypy__ 45 | ispypy = True 46 | except ImportError: 47 | ispypy = False 48 | -------------------------------------------------------------------------------- /test/templ_args_funcs.py: -------------------------------------------------------------------------------- 1 | import cppyy 2 | 3 | def ann_adapt(node: 'FPTA::Node&') -> cppyy.gbl.FPTA.EventId: 4 | return cppyy.gbl.FPTA.EventId(node.fData) 5 | 6 | def ann_ref_mod(node: 'FPTA::Node&') -> cppyy.gbl.FPTA.EventId: 7 | ev_id = cppyy.gbl.FPTA.EventId(node.fData) 8 | node.fData = 81 9 | return ev_id 10 | -------------------------------------------------------------------------------- /test/templates.cxx: -------------------------------------------------------------------------------- 1 | #include "templates.h" 2 | 3 | 4 | // template methods 5 | long MyTemplatedMethodClass::get_size() { return -1; } 6 | 7 | long MyTemplatedMethodClass::get_char_size() { return (long)sizeof(char); } 8 | long MyTemplatedMethodClass::get_int_size() { return (long)sizeof(int); } 9 | long MyTemplatedMethodClass::get_long_size() { return (long)42; /* "lying" */ } 10 | long MyTemplatedMethodClass::get_float_size() { return (long)sizeof(float); } 11 | long MyTemplatedMethodClass::get_double_size() { return (long)sizeof(double); } 12 | long MyTemplatedMethodClass::get_self_size() { return (long)sizeof(MyTemplatedMethodClass); } 13 | 14 | 15 | // variadic templates 16 | #ifdef WIN32 17 | __declspec(dllexport) 18 | #endif 19 | std::string some_variadic::gTypeName = ""; 20 | 21 | 22 | // template with empty body 23 | namespace T_WithEmptyBody { 24 | 25 | #ifdef WIN32 26 | __declspec(dllexport) 27 | #endif 28 | std::string side_effect = "not set"; 29 | 30 | template 31 | void some_empty() { 32 | side_effect = "side effect"; 33 | } 34 | 35 | template void some_empty(); 36 | 37 | } // namespace T_WithRValue 38 | 39 | 40 | // The following is hidden from the Cling interpreter, but available to the 41 | // linker; it allows for testing whether a function return is picked up from 42 | // the compiled instantation or from the interpreter. 43 | 44 | namespace FailedTypeDeducer { 45 | 46 | template 47 | class A { 48 | public: 49 | T result() { return T{42}; } 50 | }; 51 | 52 | template class A; 53 | 54 | template class B; 55 | 56 | } // namespace FailedTypeDeducer 57 | -------------------------------------------------------------------------------- /test/templates.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /test/test_aclassloader.py: -------------------------------------------------------------------------------- 1 | import py 2 | from pytest import raises 3 | from .support import setup_make 4 | 5 | currpath = py.path.local(__file__).dirpath() 6 | test_dct = str(currpath.join("example01Dict")) 7 | 8 | def setup_module(mod): 9 | setup_make("example01") 10 | 11 | 12 | class TestACLASSLOADER: 13 | 14 | def setup_class(cls): 15 | import cppyy 16 | 17 | def test01_class_autoloading(self): 18 | """Test whether a class can be found through .rootmap.""" 19 | import cppyy 20 | example01_class = cppyy.gbl.example01 21 | assert example01_class 22 | cl2 = cppyy.gbl.example01 23 | assert cl2 24 | assert example01_class is cl2 25 | -------------------------------------------------------------------------------- /test/test_boost.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pytest import mark, raises, skip 3 | from .support import setup_make 4 | 5 | noboost = False 6 | if not (os.path.exists(os.path.join(os.path.sep, 'usr', 'include', 'boost')) or \ 7 | os.path.exists(os.path.join(os.path.sep, 'usr', 'local', 'include', 'boost'))): 8 | noboost = True 9 | 10 | 11 | @mark.skipif(noboost == True, reason="boost not found") 12 | class TestBOOSTANY: 13 | def setup_class(cls): 14 | import cppyy 15 | 16 | cppyy.include('boost/any.hpp') 17 | 18 | def test01_any_class(self): 19 | """Availability of boost::any""" 20 | 21 | import cppyy 22 | 23 | assert cppyy.gbl.boost.any 24 | 25 | from cppyy.gbl import std 26 | from cppyy.gbl.boost import any 27 | 28 | assert std.list[any] 29 | 30 | def test02_any_usage(self): 31 | """boost::any assignment and casting""" 32 | 33 | import cppyy 34 | 35 | assert cppyy.gbl.boost 36 | 37 | from cppyy.gbl import std, boost 38 | 39 | val = boost.any() 40 | # test both by-ref and by rvalue 41 | v = std.vector[int]() 42 | val.__assign__(v) 43 | val.__assign__(std.move(std.vector[int](range(100)))) 44 | assert val.type() == cppyy.typeid(std.vector[int]) 45 | 46 | extract = boost.any_cast[std.vector[int]](val) 47 | assert type(extract) is std.vector[int] 48 | assert len(extract) == 100 49 | extract += range(100) 50 | assert len(extract) == 200 51 | 52 | val.__assign__(std.move(extract)) # move forced 53 | #assert len(extract) == 0 # not guaranteed by the standard 54 | 55 | # TODO: we hit boost::any_cast(boost::any* operand) instead 56 | # of the reference version which raises 57 | boost.any_cast.__useffi__ = False 58 | try: 59 | # raises(Exception, boost.any_cast[int], val) 60 | assert not boost.any_cast[int](val) 61 | except Exception: 62 | # getting here is good, too ... 63 | pass 64 | 65 | extract = boost.any_cast[std.vector[int]](val) 66 | assert len(extract) == 200 67 | 68 | 69 | @mark.skipif(noboost == True, reason="boost not found") 70 | class TestBOOSTOPERATORS: 71 | def setup_class(cls): 72 | import cppyy 73 | 74 | cppyy.include('boost/operators.hpp') 75 | 76 | def test01_ordered(self): 77 | """ordered_field_operators as base used to crash""" 78 | 79 | import cppyy 80 | 81 | try: 82 | cppyy.include("gmpxx.h") 83 | except ImportError: 84 | skip("gmpxx not installed") 85 | cppyy.cppdef(""" 86 | namespace boost_test { 87 | class Derived : boost::ordered_field_operators, boost::ordered_field_operators {}; 88 | } 89 | """) 90 | 91 | assert cppyy.gbl.boost_test.Derived 92 | 93 | 94 | @mark.skipif(noboost == True, reason="boost not found") 95 | class TestBOOSTVARIANT: 96 | def setup_class(cls): 97 | import cppyy 98 | 99 | cppyy.include("boost/variant/variant.hpp") 100 | cppyy.include("boost/variant/get.hpp") 101 | 102 | def test01_variant_usage(self): 103 | """boost::variant usage""" 104 | 105 | # as posted on stackoverflow as example 106 | import cppyy 107 | 108 | cpp = cppyy.gbl 109 | std = cpp.std 110 | boost = cpp.boost 111 | 112 | cppyy.cppdef("""namespace BV { 113 | class A { }; 114 | class B { }; 115 | class C { }; } """) 116 | 117 | VariantType = boost.variant['BV::A, BV::B, BV::C'] 118 | VariantTypeList = std.vector[VariantType] 119 | 120 | v = VariantTypeList() 121 | 122 | v.push_back(VariantType(cpp.BV.A())) 123 | assert v.back().which() == 0 124 | v.push_back(VariantType(cpp.BV.B())) 125 | assert v.back().which() == 1 126 | v.push_back(VariantType(cpp.BV.C())) 127 | assert v.back().which() == 2 128 | 129 | assert type(boost.get['BV::A'](v[0])) == cpp.BV.A 130 | raises(Exception, boost.get['BV::B'], v[0]) 131 | assert type(boost.get['BV::B'](v[1])) == cpp.BV.B 132 | assert type(boost.get['BV::C'](v[2])) == cpp.BV.C 133 | 134 | 135 | @mark.skipif(noboost == True, reason="boost not found") 136 | class TestBOOSTERASURE: 137 | def setup_class(cls): 138 | import cppyy 139 | 140 | cppyy.include("boost/type_erasure/any.hpp") 141 | cppyy.include("boost/type_erasure/member.hpp") 142 | cppyy.include("boost/mpl/vector.hpp") 143 | 144 | def test01_erasure_usage(self): 145 | """boost::type_erasure usage""" 146 | 147 | import cppyy 148 | 149 | cppyy.cppdef(""" 150 | BOOST_TYPE_ERASURE_MEMBER((has_member_f), f, 0) 151 | 152 | using LengthsInterface = boost::mpl::vector< 153 | boost::type_erasure::copy_constructible<>, 154 | has_member_f() const>>; 155 | 156 | using Lengths = boost::type_erasure::any; 157 | 158 | struct Unerased { 159 | std::vector f() const { return std::vector{}; } 160 | }; 161 | 162 | Lengths lengths() { 163 | return Unerased{}; 164 | } 165 | """) 166 | 167 | assert cppyy.gbl.lengths() is not None 168 | -------------------------------------------------------------------------------- /test/test_conversions.py: -------------------------------------------------------------------------------- 1 | import py 2 | from pytest import raises 3 | from .support import setup_make 4 | 5 | currpath = py.path.local(__file__).dirpath() 6 | test_dct = str(currpath.join("conversionsDict")) 7 | 8 | def setup_module(mod): 9 | setup_make("conversions") 10 | 11 | 12 | class TestCONVERSIONS: 13 | def setup_class(cls): 14 | cls.test_dct = test_dct 15 | import cppyy 16 | cls.conversion = cppyy.load_reflection_info(cls.test_dct) 17 | 18 | def test01_implicit_vector_conversions(self): 19 | """Test implicit conversions of std::vector""" 20 | 21 | import cppyy 22 | CNS = cppyy.gbl.CNS 23 | 24 | N = 10 25 | total = float(sum(range(N))) 26 | 27 | v = cppyy.gbl.std.vector['double'](range(N)) 28 | assert CNS.sumit(v) == total 29 | assert sum(v) == total 30 | assert CNS.sumit(range(N)) == total 31 | 32 | M = 5 33 | total = float(sum(range(N)) + sum(range(M, N))) 34 | v1 = cppyy.gbl.std.vector['double'](range(N)) 35 | v2 = cppyy.gbl.std.vector['double'](range(M, N)) 36 | assert CNS.sumit(v1, v2) == total 37 | assert sum(v1)+sum(v2) == total 38 | assert CNS.sumit(v1, range(M, N)) == total 39 | assert CNS.sumit(range(N), v2) == total 40 | assert CNS.sumit(range(N), range(M, N)) == total 41 | 42 | def test02_memory_handling_of_temporaries(self): 43 | """Verify that memory of temporaries is properly cleaned up""" 44 | 45 | import cppyy, gc 46 | CNS, CC = cppyy.gbl.CNS, cppyy.gbl.CNS.Counter 47 | 48 | assert CC.s_count == 0 49 | c = CC() 50 | assert c.__python_owns__ 51 | assert CC.s_count == 1 52 | del c; gc.collect() 53 | assert CC.s_count == 0 54 | 55 | assert CNS.myhowmany((CC(), CC(), CC())) == 3 56 | gc.collect() 57 | assert CC.s_count == 0 58 | 59 | assert CNS.myhowmany((CC(), CC(), CC()), [CC(), CC()]) == 5 60 | gc.collect() 61 | assert CC.s_count == 0 62 | 63 | def test03_error_handling(self): 64 | """Verify error handling""" 65 | 66 | import cppyy, gc 67 | CNS, CC = cppyy.gbl.CNS, cppyy.gbl.CNS.Counter 68 | 69 | N = 13 70 | total = sum(range(N)) 71 | assert CNS.sumints(range(N)) == total 72 | assert CNS.sumit([float(x) for x in range(N)]) == float(total) 73 | raises(TypeError, CNS.sumints, [float(x) for x in range(N)]) 74 | raises(TypeError, CNS.sumints, list(range(N))+[0.]) 75 | 76 | assert CC.s_count == 0 77 | 78 | raises(TypeError, CNS.sumints, list(range(N))+[CC()]) 79 | gc.collect() 80 | assert CC.s_count == 0 81 | 82 | raises(TypeError, CNS.sumints, range(N), [CC()]) 83 | gc.collect() 84 | 85 | assert CC.s_count == 0 86 | raises(TypeError, CNS.sumints, [CC()], range(N)) 87 | gc.collect() 88 | assert CC.s_count == 0 89 | 90 | def test04_implicit_conversion_from_tuple(self): 91 | """Allow implicit conversions from tuples as arguments {}-like""" 92 | 93 | # Note: fails on windows b/c the assignment operator for strings is 94 | # template, which ("operator=(std::string)") doesn't instantiate 95 | import cppyy 96 | 97 | m = cppyy.gbl.std.map[str, str]() 98 | m.insert(('a', 'b')) # implicit conversion to std::pair 99 | 100 | assert m['a'] == 'b' 101 | 102 | def test05_bool_conversions(self): 103 | """Test operator bool() and null pointer behavior""" 104 | 105 | import cppyy 106 | 107 | cppyy.cppdef("""\ 108 | namespace BoolConversions { 109 | struct Test1 {}; 110 | struct Test2 { 111 | Test2(bool b) : m_b(b) {} 112 | explicit operator bool() const { return m_b; } 113 | bool m_b; 114 | }; 115 | 116 | Test1* CreateNullTest1() { return nullptr; } 117 | Test2* CreateNullTest2() { return nullptr; } 118 | }""") 119 | 120 | ns = cppyy.gbl.BoolConversions 121 | 122 | for t in [ns.CreateNullTest1(), ns.CreateNullTest2()]: 123 | assert not t 124 | 125 | assert ns.Test1() 126 | assert ns.Test2(True) 127 | assert not ns.Test2(False) 128 | -------------------------------------------------------------------------------- /test/test_eigen.py: -------------------------------------------------------------------------------- 1 | import py, os 2 | from pytest import mark, raises 3 | from .support import setup_make 4 | 5 | inc_paths = [os.path.join(os.path.sep, 'usr', 'include'), 6 | os.path.join(os.path.sep, 'usr', 'local', 'include')] 7 | 8 | eigen_path = None 9 | for p in inc_paths: 10 | p = os.path.join(p, 'eigen3') 11 | if os.path.exists(p): 12 | eigen_path = p 13 | 14 | 15 | @mark.skipif(eigen_path is None, reason="Eigen not found") 16 | class TestEIGEN: 17 | def setup_class(cls): 18 | import cppyy, warnings 19 | 20 | cppyy.add_include_path(eigen_path) 21 | with warnings.catch_warnings(): 22 | warnings.simplefilter('ignore') 23 | cppyy.include('Eigen/Dense') 24 | 25 | def test01_simple_matrix_and_vector(self): 26 | """Basic creation of an Eigen::Matrix and Eigen::Vector""" 27 | 28 | import cppyy 29 | 30 | a = cppyy.gbl.Eigen.Matrix['double', 2, 2]() 31 | assert a.rows() == 2 32 | assert a.cols() == 2 33 | 34 | b = cppyy.gbl.Eigen.MatrixXd(2, 2) 35 | b[0,0] = 3 36 | assert b(0,0) == 3. 37 | b[1,0] = 2.5 38 | assert b(1,0) == 2.5 39 | b[0,1] = -1 40 | assert b(0,1) == -1. 41 | b[1,1] = b(1,0) + b(0,1) 42 | assert b(1,1) == b[1,0] + b[0,1] 43 | 44 | v = cppyy.gbl.Eigen.VectorXd(2) 45 | v[0] = 4 46 | assert v[0] == 4 and v(0) == 4 47 | v[1] = v(0) - 1 48 | assert v(1) == 3 and v[1] == 3 49 | 50 | def test02_comma_insertion(self): 51 | """Comma insertion overload""" 52 | 53 | import cppyy 54 | 55 | m = cppyy.gbl.Eigen.MatrixXd(2, 5) 56 | assert m.rows() == 2 57 | assert m.cols() == 5 58 | 59 | # TODO: this calls a conversion to int ... 60 | #m.resize(cppyy.gbl.Eigen.NoChange_t(), 3) 61 | #assert m.rows() == 2 62 | #assert m.cols() == 3 63 | 64 | m.resize(4, 3) 65 | assert m.rows() == 4 66 | assert m.cols() == 3 67 | 68 | # equivalent of 'm << 12, 11, ..., 1' in C++ 69 | c = (m << 12) 70 | for i in range(11, 0, -1): 71 | c = c.__comma__(i) 72 | assert m[0, 0] == 12. 73 | assert m[0, 1] == 11. 74 | assert m[0, 2] == 10. 75 | assert m[1, 0] == 9. 76 | assert m[1, 1] == 8. 77 | assert m[1, 2] == 7. 78 | assert m[2, 0] == 6. 79 | assert m[2, 1] == 5. 80 | assert m[2, 2] == 4. 81 | assert m[3, 0] == 3. 82 | assert m[3, 1] == 2. 83 | assert m[3, 2] == 1. 84 | 85 | matA = cppyy.gbl.Eigen.MatrixXf(2, 2) 86 | (matA << 1).__comma__(2).__comma__(3).__comma__(4) 87 | matB = cppyy.gbl.Eigen.MatrixXf(4, 4) 88 | # TODO: the insertion operator is a template that expect only the base class 89 | #(matB << matA).__comma__(matA/10).__comma__(matA/10).__comma__(matA) 90 | 91 | v = cppyy.gbl.Eigen.VectorXd(2) 92 | v.resize(5) 93 | assert v.size() == 5 94 | assert v.rows() == 5 95 | assert v.cols() == 1 96 | # the following is equivalent to: 97 | # (v << 1).__comma__(2).__comma__(3).__comma__(4).__comma__(5) 98 | from functools import reduce 99 | reduce(lambda x, y: x.__comma__(y), range(2, 6), v << 1) 100 | for i in range(5): 101 | assert v(i) == i+1 102 | 103 | def test03_matrices_and_vectors(self): 104 | """Matrices and vectors""" 105 | 106 | import cppyy 107 | 108 | # 'dynamic' matrices/vectors 109 | MatrixXd = cppyy.gbl.Eigen.MatrixXd 110 | VectorXd = cppyy.gbl.Eigen.VectorXd 111 | 112 | m = MatrixXd.Random(3, 3) 113 | assert m.rows() == 3 114 | assert m.cols() == 3 115 | m = (m + MatrixXd.Constant(3, 3, 1.2)) * 50 116 | 117 | v = VectorXd(3) 118 | (v << 1).__comma__(2).__comma__(3); 119 | 120 | assert (m*v).size() == v.size() 121 | 122 | # 'static' matrices/vectors 123 | Matrix3d = cppyy.gbl.Eigen.Matrix3d 124 | Vector3d = cppyy.gbl.Eigen.Vector3d 125 | 126 | m = Matrix3d.Random() 127 | m = (m + Matrix3d.Constant(1.2)) * 50 128 | 129 | v = Vector3d(1, 2, 3) 130 | 131 | assert (m*v).size() == v.size() 132 | 133 | def test04_resizing_through_assignment(self): 134 | """Resize on assignment""" 135 | 136 | import cppyy 137 | 138 | a = cppyy.gbl.Eigen.MatrixXf(2, 2) 139 | assert a.size() == 4 140 | b = cppyy.gbl.Eigen.MatrixXf(3, 3) 141 | assert b.size() == 9 142 | 143 | a.__assign__(b) 144 | assert a.size() == 9 145 | 146 | 147 | @mark.skipif(eigen_path is None, reason="Eigen not found") 148 | class TestEIGEN_REGRESSIOn: 149 | def setup_class(cls): 150 | import cppyy, warnings 151 | 152 | cppyy.add_include_path(eigen_path) 153 | with warnings.catch_warnings(): 154 | warnings.simplefilter('ignore') 155 | cppyy.include('Eigen/Dense') 156 | 157 | def test01_use_of_Map(self): 158 | """Use of Map (used to crash)""" 159 | 160 | import cppyy 161 | from cppyy.gbl import Eigen 162 | 163 | assert Eigen.VectorXd 164 | assert Eigen.Map 165 | 166 | assert Eigen.Map[Eigen.VectorXd] # used to crash 167 | -------------------------------------------------------------------------------- /test/test_streams.py: -------------------------------------------------------------------------------- 1 | import py 2 | from pytest import raises 3 | from .support import setup_make 4 | 5 | currpath = py.path.local(__file__).dirpath() 6 | test_dct = str(currpath.join("std_streamsDict")) 7 | 8 | def setup_module(mod): 9 | setup_make("std_streams") 10 | 11 | 12 | class TestSTDStreams: 13 | def setup_class(cls): 14 | cls.test_dct = test_dct 15 | import cppyy 16 | cls.streams = cppyy.load_reflection_info(cls.test_dct) 17 | 18 | def test01_std_ostream(self): 19 | """Test availability of std::ostream""" 20 | 21 | import cppyy 22 | 23 | assert cppyy.gbl.std is cppyy.gbl.std 24 | assert cppyy.gbl.std.ostream is cppyy.gbl.std.ostream 25 | 26 | assert callable(cppyy.gbl.std.ostream) 27 | 28 | def test02_std_cout(self): 29 | """Test access to std::cout""" 30 | 31 | import cppyy 32 | 33 | assert not (cppyy.gbl.std.cout is None) 34 | 35 | def test03_consistent_naming_if_char_traits(self): 36 | """Naming consistency if char_traits""" 37 | 38 | import cppyy 39 | 40 | cppyy.cppdef("""\ 41 | namespace stringstream_base { 42 | void pass_through_base(std::ostream& o) { 43 | o << "TEST STRING"; 44 | } }""") 45 | 46 | s = cppyy.gbl.std.ostringstream(); 47 | # base class used to fail to match 48 | cppyy.gbl.stringstream_base.pass_through_base(s) 49 | assert s.str() == "TEST STRING" 50 | 51 | def test04_naming_of_ostringstream(self): 52 | """Naming consistency of ostringstream""" 53 | 54 | import cppyy 55 | 56 | short_type = cppyy.gbl.CppyyLegacy.TClassEdit.ShortType 57 | s0 = short_type("std::basic_ostringstream", 2) 58 | s1 = short_type("std::basic_ostringstream >", 2) 59 | s2 = short_type("std::basic_ostringstream >", 2) 60 | s3 = short_type("std::basic_ostringstream, std::allocator >", 2) 61 | s4 = short_type("std::basic_ostringstream, std::allocator >", 2) 62 | 63 | assert s1 == s0 64 | assert s2 == s0 65 | assert s3 == s0 66 | assert s4 == s0 67 | 68 | get_class = cppyy.gbl.CppyyLegacy.TClass.GetClass 69 | cl0 = get_class("std::ostringstream") 70 | cl1 = get_class("std::basic_ostringstream") 71 | cl2 = get_class("std::basic_ostringstream, std::allocator >") 72 | 73 | assert cl0 == cl1 74 | assert cl1 == cl2 75 | assert cl2 == cl0 76 | --------------------------------------------------------------------------------