Author: Wim Lavrijsen <wlavrij...@lbl.gov> Branch: cppyy-packaging Changeset: r94449:e7331182c14c Date: 2018-04-24 13:39 -0700 http://bitbucket.org/pypy/pypy/changeset/e7331182c14c/
Log: const correctness for data members and associated tests diff --git a/pypy/module/_cppyy/__init__.py b/pypy/module/_cppyy/__init__.py --- a/pypy/module/_cppyy/__init__.py +++ b/pypy/module/_cppyy/__init__.py @@ -7,6 +7,7 @@ interpleveldefs = { '_resolve_name' : 'interp_cppyy.resolve_name', '_scope_byname' : 'interp_cppyy.scope_byname', + '_is_static_data' : 'interp_cppyy.is_static_data', '_is_template' : 'interp_cppyy.is_template', '_std_string_name' : 'interp_cppyy.std_string_name', '_set_class_generator' : 'interp_cppyy.set_class_generator', diff --git a/pypy/module/_cppyy/interp_cppyy.py b/pypy/module/_cppyy/interp_cppyy.py --- a/pypy/module/_cppyy/interp_cppyy.py +++ b/pypy/module/_cppyy/interp_cppyy.py @@ -149,6 +149,24 @@ W_CPPLibrary.typedef.acceptable_as_base_class = True +#----- +# Classes involved with methods and functions: +# +# CPPMethod: base class wrapping a single function or method +# CPPConstructor: specialization for allocating a new object +# CPPFunction: specialization for free and static functions +# CPPSetItem: specialization for Python's __setitem__ +# CPPTemplatedCall: trampoline to instantiate and bind templated functions +# W_CPPOverload, W_CPPConstructorOverload, W_CPPTemplateOverload: +# user-facing, app-level, collection of overloads, with specializations +# for constructors and templates +# W_CPPBoundMethod: instantiated template method +# +# All methods/functions derive from CPPMethod and are collected as overload +# candidates in user-facing overload classes. Templated methods are a two-step +# process, where first the template is instantiated (or selected if already +# available), which returns a callable object that is the actual bound method. + class CPPMethod(object): """Dispatcher of methods. Checks the arguments, find the corresponding FFI function if available, makes the call, and returns the wrapped result. It @@ -688,6 +706,18 @@ ) +#----- +# Classes for data members: +# +# W_CPPDataMember: instance data members +# W_CPPConstDataMember: specialization for const data members +# W_CPPStaticData: class-level and global/static data +# W_CPPConstStaticData: specialization for const global/static data +# +# Data is represented by an offset which is either a global pointer (static data) +# or an offset from the start of an instance (data members). The "const" +# specializations raise when attempting to set their value. + class W_CPPDataMember(W_Root): _attrs_ = ['space', 'scope', 'converter', 'offset'] _immutable_fields = ['scope', 'converter', 'offset'] @@ -698,9 +728,6 @@ self.converter = converter.get_converter(self.space, type_name, '') self.offset = offset - def is_static(self): - return self.space.w_False - def _get_offset(self, cppinstance): if cppinstance: assert lltype.typeOf(cppinstance.clsdecl.handle) == lltype.typeOf(self.scope.handle) @@ -728,16 +755,25 @@ W_CPPDataMember.typedef = TypeDef( 'CPPDataMember', - is_static = interp2app(W_CPPDataMember.is_static), __get__ = interp2app(W_CPPDataMember.get), __set__ = interp2app(W_CPPDataMember.set), ) W_CPPDataMember.typedef.acceptable_as_base_class = False + +class W_CPPConstDataMember(W_CPPDataMember): + def set(self, w_cppinstance, w_value): + raise oefmt(self.space.w_TypeError, "assignment to const data not allowed") + +W_CPPConstDataMember.typedef = TypeDef( + 'CPPConstDataMember', + __get__ = interp2app(W_CPPDataMember.get), + __set__ = interp2app(W_CPPConstDataMember.set), +) +W_CPPConstDataMember.typedef.acceptable_as_base_class = False + + class W_CPPStaticData(W_CPPDataMember): - def is_static(self): - return self.space.w_True - @jit.elidable_promote() def _get_offset(self, cppinstance): return self.offset @@ -751,19 +787,34 @@ W_CPPStaticData.typedef = TypeDef( 'CPPStaticData', - is_static = interp2app(W_CPPStaticData.is_static), __get__ = interp2app(W_CPPStaticData.get), __set__ = interp2app(W_CPPStaticData.set), ) W_CPPStaticData.typedef.acceptable_as_base_class = False -def is_static(space, w_obj): + +class W_CPPConstStaticData(W_CPPStaticData): + def set(self, w_cppinstance, w_value): + raise oefmt(self.space.w_TypeError, "assignment to const data not allowed") + +W_CPPConstStaticData.typedef = TypeDef( + 'CPPConstStaticData', + __get__ = interp2app(W_CPPConstStaticData.get), + __set__ = interp2app(W_CPPConstStaticData.set), +) +W_CPPConstStaticData.typedef.acceptable_as_base_class = False + + +def is_static_data(space, w_obj): try: space.interp_w(W_CPPStaticData, w_obj, can_be_None=False) return space.w_True except Exception: return space.w_False +#----- + + class W_CPPScopeDecl(W_Root): _attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers'] _immutable_fields_ = ['handle', 'name'] @@ -847,7 +898,10 @@ offset = capi.c_datamember_offset(self.space, self, dm_idx) if offset == -1: raise self.missing_attribute_error(dm_name) - datamember = W_CPPStaticData(self.space, self, type_name, offset) + if capi.c_is_const_data(self.space, self, dm_idx): + datamember = W_CPPConstStaticData(self.space, self, type_name, offset) + else: + datamember = W_CPPStaticData(self.space, self, type_name, offset) self.datamembers[dm_name] = datamember return datamember @@ -967,8 +1021,13 @@ if offset == -1: continue # dictionary problem; raises AttributeError on use is_static = bool(capi.c_is_staticdata(self.space, self, i)) - if is_static: + is_const = bool(capi.c_is_const_data(self.space, self, i)) + if is_static and is_const: + datamember = W_CPPConstStaticData(self.space, self, type_name, offset) + elif is_static: datamember = W_CPPStaticData(self.space, self, type_name, offset) + elif is_const: + datamember = W_CPPConstDataMember(self.space, self, type_name, offset) else: datamember = W_CPPDataMember(self.space, self, type_name, offset) self.datamembers[datamember_name] = datamember diff --git a/pypy/module/_cppyy/pythonify.py b/pypy/module/_cppyy/pythonify.py --- a/pypy/module/_cppyy/pythonify.py +++ b/pypy/module/_cppyy/pythonify.py @@ -169,6 +169,7 @@ return method def make_cppclass(scope, cl_name, decl): + import _cppyy # get a list of base classes for class creation bases = [get_pycppclass(base) for base in decl.get_base_names()] @@ -209,7 +210,7 @@ for d_name in decl.get_datamember_names(): cppdm = decl.get_datamember(d_name) d_class[d_name] = cppdm - if cppdm.is_static(): + if _cppyy._is_static_data(cppdm): d_meta[d_name] = cppdm # create a metaclass to allow properties (for static data write access) @@ -278,7 +279,7 @@ try: cppdm = scope.__cppdecl__.get_datamember(name) setattr(scope, name, cppdm) - if cppdm.is_static(): + if _cppyy._is_static_data(cppdm): setattr(scope.__class__, name, cppdm) pycppitem = getattr(scope, name) # gets actual property value except AttributeError: diff --git a/pypy/module/_cppyy/test/datatypes.cxx b/pypy/module/_cppyy/test/datatypes.cxx --- a/pypy/module/_cppyy/test/datatypes.cxx +++ b/pypy/module/_cppyy/test/datatypes.cxx @@ -6,7 +6,7 @@ //=========================================================================== -CppyyTestData::CppyyTestData() : m_owns_arrays(false) +CppyyTestData::CppyyTestData() : m_const_int(17), m_owns_arrays(false) { m_bool = false; m_char = 'a'; @@ -333,3 +333,17 @@ CppyyTestPod* get_null_pod() { return (CppyyTestPod*)0; } + + +//= function pointer passing ================================================ +int sum_of_int(int i1, int i2) { + return i1+i2; +} + +double sum_of_double(double d1, double d2) { + return d1+d2; +} + +double call_double_double(double (*d)(double, double), double d1, double d2) { + return d(d1, d2); +} diff --git a/pypy/module/_cppyy/test/datatypes.h b/pypy/module/_cppyy/test/datatypes.h --- a/pypy/module/_cppyy/test/datatypes.h +++ b/pypy/module/_cppyy/test/datatypes.h @@ -1,5 +1,5 @@ // copied from RtypesCore.h ... -#if defined(R__WIN32) +#if defined(R__WIN32) && !defined(__CINT__) typedef __int64 Long64_t; //Portable signed long integer 8 bytes typedef unsigned __int64 ULong64_t; //Portable unsigned long integer 8 bytes #else @@ -26,8 +26,13 @@ //=========================================================================== namespace EnumSpace { - enum E {E1 = 1, E2}; -}; + enum E {E1 = 1, E2}; + class EnumClass { + public: + enum {E1 = -1}; + enum EE {E2 = -1}; + }; +} //=========================================================================== @@ -243,6 +248,7 @@ short m_short; unsigned short m_ushort; int m_int; + const int m_const_int; // special case: const testing unsigned int m_uint; long m_long; unsigned long m_ulong; @@ -364,3 +370,9 @@ void set_global_pod(CppyyTestPod* t); CppyyTestPod* get_global_pod(); CppyyTestPod* get_null_pod(); + + +//= function pointer passing ================================================ +int sum_of_int(int i1, int i2); +double sum_of_double(double d1, double d2); +double call_double_double(double (*d)(double, double), double d1, double d2); diff --git a/pypy/module/_cppyy/test/datatypes.xml b/pypy/module/_cppyy/test/datatypes.xml --- a/pypy/module/_cppyy/test/datatypes.xml +++ b/pypy/module/_cppyy/test/datatypes.xml @@ -4,6 +4,8 @@ <class name="FourVector" /> <enum name="EFruit" /> + <enum name="EnumSpace::E" /> + <class name="EnumSpace::EnumClass" /> <function pattern="get_*" /> <function pattern="set_*" /> @@ -14,4 +16,8 @@ <variable name="g_int" /> <variable name="g_pod" /> + <function name="sum_of_int" /> + <function name="sum_of_double" /> + <function name="call_double_double" /> + </lcgdict> diff --git a/pypy/module/_cppyy/test/test_datatypes.py b/pypy/module/_cppyy/test/test_datatypes.py --- a/pypy/module/_cppyy/test/test_datatypes.py +++ b/pypy/module/_cppyy/test/test_datatypes.py @@ -191,6 +191,10 @@ for i in range(self.N): assert eval('c.m_%s_array2[i]' % names[j]) == b[i] + # can not write to constant data + assert c.m_const_int == 17 + raises(TypeError, setattr, c, 'm_const_int', 71) + c.__destruct__() def test03_array_passing(self): @@ -464,6 +468,10 @@ assert gbl.kBanana == 29 assert gbl.kCitrus == 34 + assert gbl.EnumSpace.E + assert gbl.EnumSpace.EnumClass.E1 == -1 # anonymous + assert gbl.EnumSpace.EnumClass.E2 == -1 # named type + def test11_string_passing(self): """Test passing/returning of a const char*""" @@ -741,3 +749,19 @@ c.s_voidp = c2 address_equality_test(c.s_voidp, c2) + + def test21_function_pointers(self): + """Function pointer passing""" + + import _cppyy as cppyy + + f1 = cppyy.gbl.sum_of_int + f2 = cppyy.gbl.sum_of_double + f3 = cppyy.gbl.call_double_double + + assert 5 == f1(2, 3) + assert 5. == f2(5., 0.) + + raises(TypeError, f3, f1, 2, 3) + + assert 5. == f3(f2, 5., 0.) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit