diff --git a/cpp/csp/engine/CspType.h b/cpp/csp/engine/CspType.h index 7a0017fd..f8b5621e 100644 --- a/cpp/csp/engine/CspType.h +++ b/cpp/csp/engine/CspType.h @@ -90,6 +90,10 @@ class CspType static Ptr & BYTES(); static Ptr & DIALECT_GENERIC() { static auto s_type = std::make_shared( Type::DIALECT_GENERIC ); return s_type; } + static constexpr bool isNative( Type t ) { return t <= Type::MAX_NATIVE_TYPE; } + + bool isNative() const { return isNative( m_type ); } + template struct fromCType; diff --git a/cpp/csp/engine/DialectGenericType.h b/cpp/csp/engine/DialectGenericType.h index 4f97cc1b..9a343250 100644 --- a/cpp/csp/engine/DialectGenericType.h +++ b/cpp/csp/engine/DialectGenericType.h @@ -18,7 +18,7 @@ struct DialectGenericType DialectGenericType & operator=( const DialectGenericType & rhs ); DialectGenericType & operator=( DialectGenericType && rhs ); - void deepcopy( const DialectGenericType & rhs ); + DialectGenericType deepcopy() const; bool operator==( const DialectGenericType & rhs ) const; bool operator!=( const DialectGenericType & rhs ) const { return !( (*this)==rhs); } diff --git a/cpp/csp/engine/Struct.cpp b/cpp/csp/engine/Struct.cpp index 927ebee8..ceb70136 100644 --- a/cpp/csp/engine/Struct.cpp +++ b/cpp/csp/engine/Struct.cpp @@ -145,7 +145,7 @@ Struct * StructMeta::createRaw() const initialize( s ); if( m_default ) - s -> copyFrom( m_default.get(), true ); //TODO should be copy or deepcopy? + s -> copyFrom( m_default.get() ); //TODO change to deepcopy after this fix is released return s; } @@ -226,7 +226,7 @@ void StructMeta::initialize( Struct * s ) const m_base -> initialize( s ); } -void StructMeta::copyFrom( const Struct * src, Struct * dest, bool isDeepcopy ) +void StructMeta::copyFrom( const Struct * src, Struct * dest ) { if( unlikely( src == dest ) ) return; @@ -236,11 +236,23 @@ void StructMeta::copyFrom( const Struct * src, Struct * dest, bool isDeepcopy ) CSP_THROW( TypeError, "Attempting to copy from struct type '" << src -> meta() -> name() << "' to struct type '" << dest -> meta() -> name() << "'. copy_from may only be used to copy from same type or derived types" ); + dest -> meta() -> copyFromImpl( src, dest, false ); +} + +void StructMeta::deepcopyFrom( const Struct * src, Struct * dest ) +{ + if( unlikely( src == dest ) ) + return; + + if( dest -> meta() != src -> meta() && + !StructMeta::isDerivedType( src -> meta(), dest -> meta() ) ) + CSP_THROW( TypeError, "Attempting to deepcopy from struct type '" << src -> meta() -> name() << "' to struct type '" << dest -> meta() -> name() + << "'. deepcopy_from may only be used to copy from same type or derived types" ); - dest -> meta() -> copyFromImpl( src, dest, isDeepcopy ); -} + dest -> meta() -> copyFromImpl( src, dest, true ); +} -void StructMeta::copyFromImpl( const Struct * src, Struct * dest, bool isDeepcopy ) const +void StructMeta::copyFromImpl( const Struct * src, Struct * dest, bool deepcopy ) const { //quick outs, if fully native we can memcpy the whole thing if( isNative() ) @@ -256,10 +268,10 @@ void StructMeta::copyFromImpl( const Struct * src, Struct * dest, bool isDeepcop auto * field = m_fields[ idx ].get(); if( field -> isSet( src ) ) - if( !isDeepcopy ) - static_cast( field ) -> copyFrom( src, dest ); - else + if( deepcopy ) static_cast( field ) -> deepcopyFrom( src, dest ); + else + static_cast( field ) -> copyFrom( src, dest ); else static_cast( field ) -> clearValue( dest ); } @@ -270,7 +282,7 @@ void StructMeta::copyFromImpl( const Struct * src, Struct * dest, bool isDeepcop partialNativeSize() ); if( m_base ) - m_base -> copyFromImpl( src, dest, isDeepcopy ); + m_base -> copyFromImpl( src, dest, deepcopy ); } } @@ -487,8 +499,4 @@ void Struct::operator delete( void * ptr ) ::operator delete( p ); } -/*void Struct::deepcopyFrom( StructMeta * meta, Struct * rhs ) -{ -}*/ - } diff --git a/cpp/csp/engine/Struct.h b/cpp/csp/engine/Struct.h index 681b86b5..9aa2101b 100644 --- a/cpp/csp/engine/Struct.h +++ b/cpp/csp/engine/Struct.h @@ -313,6 +313,28 @@ class ArrayStructField : public NonNativeStructField { using CType = typename csp::CspType::Type::toCType::type; + //template::type), bool> = true> + template + static std::enable_if_t::type), void> deepcopy( const std::vector & src, std::vector & dest ) + { + dest = src; + } + + static void deepcopy( const std::vector & src, std::vector & dest ) + { + dest = src; + } + + //Declared at end of file since StructPtr isnt defined yet + static void deepcopy( const std::vector & src, std::vector & dest ); + + static void deepcopy( const std::vector & src, std::vector & dest ) + { + dest.resize( src.size() ); + for( size_t i = 0; i < src.size(); ++i ) + dest[i] = src[i].deepcopy(); + } + public: ArrayStructField( CspTypePtr arrayType, const std::string & fieldname ) : NonNativeStructField( arrayType, fieldname, sizeof( CType ), alignof( CType ) ) @@ -344,11 +366,11 @@ class ArrayStructField : public NonNativeStructField value( dest ) = value( src ); } - void deepcopyFrom( const Struct * src, Struct * dest ) const override //TODO implement + void deepcopyFrom( const Struct * src, Struct * dest ) const override { - value( dest ) = value( src ); + deepcopy( value( src ), value( dest ) ); } - + bool isEqual( const Struct * x, const Struct * y ) const override { return value( x ) == value( y ); @@ -434,7 +456,7 @@ class DialectGenericStructField : public NonNativeStructField void deepcopyFrom( const Struct * src, Struct * dest ) const override { - ( ( DialectGenericType * ) valuePtr( dest ) ) -> deepcopy( * ( ( DialectGenericType * ) valuePtr( src ) ) ); + *( ( DialectGenericType * ) valuePtr( dest ) ) = ( ( DialectGenericType * ) valuePtr( src ) ) -> deepcopy(); } bool isEqual( const Struct * x, const Struct * y ) const override @@ -594,7 +616,8 @@ class StructMeta : public std::enable_shared_from_this void destroy( Struct * s ) const; bool isEqual( const Struct * x, const Struct * y ) const; size_t hash( const Struct * x ) const; - static void copyFrom( const Struct * src, Struct * dest, bool isDeepcopy ); + static void copyFrom( const Struct * src, Struct * dest ); + static void deepcopyFrom( const Struct * src, Struct * dest ); static void updateFrom( const Struct * src, Struct * dest ); void clear( Struct * s ) const; bool allFieldsSet( const Struct * s ) const; @@ -612,7 +635,7 @@ class StructMeta : public std::enable_shared_from_this using FieldMap = std::unordered_map; size_t partialNativeSize() const { return m_size - m_nativeStart; } - void copyFromImpl( const Struct * src, Struct * dest, bool isDeepcopy ) const; + void copyFromImpl( const Struct * src, Struct * dest, bool deepcopy ) const; void updateFromImpl( const Struct * src, Struct * dest ) const; std::string m_name; @@ -674,20 +697,19 @@ class Struct StructPtr copy() const { StructPtr copy = meta() -> create(); - copy -> copyFrom( this, false ); + copy -> copyFrom( this ); return copy; } StructPtr deepcopy() const { StructPtr copy = meta() -> create(); - copy -> copyFrom( this, true ); + copy -> deepcopyFrom( this ); return copy; } - void copyFrom( const Struct * rhs, bool isDeepcopy ) + void copyFrom( const Struct * rhs ) { - StructMeta::copyFrom( rhs, this ); } @@ -849,6 +871,15 @@ class StructStructField final : public NonNativeStructField StructMetaPtr m_meta; }; +//Defined here to break decl dep +template +void ArrayStructField::deepcopy( const std::vector & src, std::vector & dest ) +{ + dest.resize( src.size() ); + for( size_t i = 0; i < src.size(); ++i ) + dest[i] = src[i] -> deepcopy(); +} + template struct StructField::upcast { using type = NotImplementedStructField; }; template<> struct StructField::upcast { using type = BoolStructField; }; diff --git a/cpp/csp/python/PyCspType.cpp b/cpp/csp/python/PyCspType.cpp index 3bee4645..9ef9c507 100644 --- a/cpp/csp/python/PyCspType.cpp +++ b/cpp/csp/python/PyCspType.cpp @@ -28,12 +28,11 @@ DialectGenericType::DialectGenericType( DialectGenericType &&rhs ) new( this ) csp::python::PyObjectPtr( reinterpret_cast(rhs) ); } -void DialectGenericType::deepcopy( const DialectGenericType & rhs ) +DialectGenericType DialectGenericType::deepcopy() const { static PyObject *pyDeepcopy = PyObject_GetAttrString( PyImport_ImportModule( "copy" ), "deepcopy" ); - PyObject * deepcopy = PyObject_CallObject( pyDeepcopy, PyTuple_Pack(1, python::toPython( rhs ) ) ); - - new( this ) DialectGenericType( python::fromPython( deepcopy ) ); + PyObject * pyVal = PyObject_CallFunction( pyDeepcopy, "O", python::toPythonBorrowed( *this ) ); + return DialectGenericType( reinterpret_cast( std::move( csp::python::PyObjectPtr::own( pyVal ) ) ) ); } DialectGenericType &DialectGenericType::operator=( const DialectGenericType &rhs ) diff --git a/cpp/csp/python/PyStruct.cpp b/cpp/csp/python/PyStruct.cpp index 8d855076..8ddb33da 100644 --- a/cpp/csp/python/PyStruct.cpp +++ b/cpp/csp/python/PyStruct.cpp @@ -133,7 +133,7 @@ static PyObject * PyStructMeta_new( PyTypeObject *subtype, PyObject *args, PyObj case csp::CspType::Type::ARRAY: { const CspArrayType & arrayType = static_cast( *csptype ); - field = switchCspType( arrayType.elemType(), [csptype,keystr]( auto tag ) -> std::shared_ptr + field = ArraySubTypeSwitch::invoke( arrayType.elemType(), [csptype,keystr]( auto tag ) -> std::shared_ptr { using CElemType = typename decltype(tag)::type; return std::make_shared>( csptype, keystr ); @@ -871,7 +871,18 @@ PyObject * PyStruct_copy_from( PyStruct * self, PyObject * o ) if( !PyType_IsSubtype( Py_TYPE( o ), &PyStruct::PyType ) ) CSP_THROW( TypeError, "Attempting to copy from non-struct type " << Py_TYPE( o ) -> tp_name ); - self -> struct_ -> copyFrom( ( ( PyStruct * ) o ) -> struct_.get(), true ); + self -> struct_ -> copyFrom( ( ( PyStruct * ) o ) -> struct_.get() ); + CSP_RETURN_NONE; +} + +PyObject * PyStruct_deepcopy_from( PyStruct * self, PyObject * o ) +{ + CSP_BEGIN_METHOD; + + if( !PyType_IsSubtype( Py_TYPE( o ), &PyStruct::PyType ) ) + CSP_THROW( TypeError, "Attempting to deepcopy from non-struct type " << Py_TYPE( o ) -> tp_name ); + + self -> struct_ -> deepcopyFrom( ( ( PyStruct * ) o ) -> struct_.get() ); CSP_RETURN_NONE; } @@ -892,12 +903,13 @@ PyObject * PyStruct_all_fields_set( PyStruct * self ) } static PyMethodDef PyStruct_methods[] = { - { "copy", (PyCFunction) PyStruct_copy, METH_NOARGS, "make a shallow copy of the struct" }, - { "deepcopy", (PyCFunction) PyStruct_deepcopy, METH_NOARGS, "make a deep copy of the struct" }, - { "clear", (PyCFunction) PyStruct_clear, METH_NOARGS, "clear all fields" }, - { "copy_from", (PyCFunction) PyStruct_copy_from, METH_O, "copy from struct. struct must be same type or a derived type. unset fields will copy over" }, - { "update_from", (PyCFunction) PyStruct_update_from, METH_O, "update from struct. struct must be same type or a derived type. unset fields will be not be copied" }, - { "update", (PyCFunction) PyStruct_update, METH_VARARGS | METH_KEYWORDS, "update from key=val. given fields will be set on struct. other fields will remain as is in struct" }, + { "copy", (PyCFunction) PyStruct_copy, METH_NOARGS, "make a shallow copy of the struct" }, + { "deepcopy", (PyCFunction) PyStruct_deepcopy, METH_NOARGS, "make a deep copy of the struct" }, + { "clear", (PyCFunction) PyStruct_clear, METH_NOARGS, "clear all fields" }, + { "copy_from", (PyCFunction) PyStruct_copy_from, METH_O, "copy from struct. struct must be same type or a derived type. unset fields will copy over" }, + { "deepcopy_from", (PyCFunction) PyStruct_deepcopy_from, METH_O, "deepcopy from struct. struct must be same type or a derived type. unset fields will copy over" }, + { "update_from", (PyCFunction) PyStruct_update_from, METH_O, "update from struct. struct must be same type or a derived type. unset fields will be not be copied" }, + { "update", (PyCFunction) PyStruct_update, METH_VARARGS | METH_KEYWORDS, "update from key=val. given fields will be set on struct. other fields will remain as is in struct" }, { "all_fields_set", (PyCFunction) PyStruct_all_fields_set, METH_NOARGS, "return true if all fields on the struct are set" }, { NULL} }; diff --git a/csp/tests/impl/test_struct.py b/csp/tests/impl/test_struct.py index 27acc7f0..d4523af7 100644 --- a/csp/tests/impl/test_struct.py +++ b/csp/tests/impl/test_struct.py @@ -113,14 +113,14 @@ class StructWithStruct(csp.Struct): s: BaseNative -class StructWithStructWithMutable(csp.Struct): +class StructWithMutableStruct(csp.Struct): a: int s: BaseNonNative class StructWithLists(csp.Struct): # native_list: [int] - # struct_list: [BaseNative] + struct_list: [BaseNative] dialect_generic_list: [list] @@ -331,7 +331,7 @@ def test_deepcopy(self): delattr(o, list(typ.metadata().keys())[0]) self.assertEqual(o, o.deepcopy()) - o = StructWithStructWithMutable() + o = StructWithMutableStruct() o.s = BaseNonNative() o.s.l = [1, 2, 3] @@ -341,21 +341,17 @@ def test_deepcopy(self): o.s.l[0] = -1 self.assertNotEqual(o, o_deepcopy) - list_new_values = { - # 'native_list': -1, - # 'struct_list': BaseNative(i=-1), - 'dialect_generic_list': [-1] - } + o = StructWithLists( struct_list= [ BaseNative( i = 123 ) ], + dialect_generic_list = [ { 'a' : 1 } ] ) - for list_type in list_new_values.keys(): - o = StructWithLists() - for key in StructWithLists.metadata().keys(): - setattr(o, key, values[key]) - o_deepcopy = o.deepcopy() - self.assertEqual(o, o_deepcopy) - # getattr(o, list_type)[0] = list_new_values[list_type] - getattr(o, list_type)[0][0] = list_new_values[list_type] - self.assertNotEqual(o, o_deepcopy) + o_deepcopy = o.deepcopy() + o.struct_list[0].i = -1 + o.dialect_generic_list[0][ 'b' ] = 2 + + self.assertEqual(o.struct_list[0].i, -1 ) + self.assertEqual(o.dialect_generic_list[0], { 'a' : 1, 'b' : 2} ) + self.assertEqual(o_deepcopy.struct_list[0].i, 123) + self.assertEqual(o_deepcopy.dialect_generic_list[0], {'a': 1}) def test_derived_defaults(self): s1 = StructWithDefaults() @@ -511,7 +507,7 @@ def test_copy_from(self): blank.copy_from(source) for key in blank.metadata().keys(): - self.assertEqual(getattr(blank, key), getattr(source, key)) + self.assertEqual(getattr(blank, key), getattr(source, key), ( typ, typ2, key )) def test_copy_from_unsets(self): source = DerivedMixed(i=1, f=2.3, i2=4, s2="woodchuck")