Skip to content

Commit

Permalink
struct deepcopy - implemented array deepcopy, minor cleanup.
Browse files Browse the repository at this point in the history
Signed-off-by: Artur Gajowniczek <artur.gajowniczek@gmail.com>
  • Loading branch information
robambalu authored and argaj committed Mar 5, 2024
1 parent 70fe707 commit 0fe40ce
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 54 deletions.
4 changes: 4 additions & 0 deletions cpp/csp/engine/CspType.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ class CspType
static Ptr & BYTES();
static Ptr & DIALECT_GENERIC() { static auto s_type = std::make_shared<const CspType>( 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<typename T>
struct fromCType;

Expand Down
2 changes: 1 addition & 1 deletion cpp/csp/engine/DialectGenericType.h
Original file line number Diff line number Diff line change
Expand Up @@ -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); }
Expand Down
34 changes: 21 additions & 13 deletions cpp/csp/engine/Struct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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() )
Expand All @@ -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<NonNativeStructField*>( field ) -> copyFrom( src, dest );
else
if( deepcopy )
static_cast<NonNativeStructField*>( field ) -> deepcopyFrom( src, dest );
else
static_cast<NonNativeStructField*>( field ) -> copyFrom( src, dest );
else
static_cast<NonNativeStructField*>( field ) -> clearValue( dest );
}
Expand All @@ -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 );
}
}

Expand Down Expand Up @@ -487,8 +499,4 @@ void Struct::operator delete( void * ptr )
::operator delete( p );
}

/*void Struct::deepcopyFrom( StructMeta * meta, Struct * rhs )
{
}*/

}
51 changes: 41 additions & 10 deletions cpp/csp/engine/Struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,28 @@ class ArrayStructField : public NonNativeStructField
{
using CType = typename csp::CspType::Type::toCType<CspType::Type::ARRAY,ElemT>::type;

//template<typename T, std::enable_if_t<CspType::isNative(CspType::fromCType<T>::type), bool> = true>
template<typename T>
static std::enable_if_t<CspType::isNative(CspType::Type::fromCType<T>::type), void> deepcopy( const std::vector<T> & src, std::vector<T> & dest )
{
dest = src;
}

static void deepcopy( const std::vector<std::string> & src, std::vector<std::string> & dest )
{
dest = src;
}

//Declared at end of file since StructPtr isnt defined yet
static void deepcopy( const std::vector<StructPtr> & src, std::vector<StructPtr> & dest );

static void deepcopy( const std::vector<DialectGenericType> & src, std::vector<DialectGenericType> & 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 ) )
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -594,7 +616,8 @@ class StructMeta : public std::enable_shared_from_this<StructMeta>
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;
Expand All @@ -612,7 +635,7 @@ class StructMeta : public std::enable_shared_from_this<StructMeta>
using FieldMap = std::unordered_map<const char *,StructFieldPtr, hash::CStrHash, hash::CStrEq >;

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;
Expand Down Expand Up @@ -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 );
}

Expand Down Expand Up @@ -849,6 +871,15 @@ class StructStructField final : public NonNativeStructField
StructMetaPtr m_meta;
};

//Defined here to break decl dep
template<typename ElemT>
void ArrayStructField<ElemT>::deepcopy( const std::vector<StructPtr> & src, std::vector<StructPtr> & dest )
{
dest.resize( src.size() );
for( size_t i = 0; i < src.size(); ++i )
dest[i] = src[i] -> deepcopy();
}

template<typename T> struct StructField::upcast { using type = NotImplementedStructField<T>; };

template<> struct StructField::upcast<bool> { using type = BoolStructField; };
Expand Down
7 changes: 3 additions & 4 deletions cpp/csp/python/PyCspType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,11 @@ DialectGenericType::DialectGenericType( DialectGenericType &&rhs )
new( this ) csp::python::PyObjectPtr( reinterpret_cast<const csp::python::PyObjectPtr &&>(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<DialectGenericType>( deepcopy ) );
PyObject * pyVal = PyObject_CallFunction( pyDeepcopy, "O", python::toPythonBorrowed( *this ) );
return DialectGenericType( reinterpret_cast<const DialectGenericType &&>( std::move( csp::python::PyObjectPtr::own( pyVal ) ) ) );
}

DialectGenericType &DialectGenericType::operator=( const DialectGenericType &rhs )
Expand Down
28 changes: 20 additions & 8 deletions cpp/csp/python/PyStruct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ static PyObject * PyStructMeta_new( PyTypeObject *subtype, PyObject *args, PyObj
case csp::CspType::Type::ARRAY:
{
const CspArrayType & arrayType = static_cast<const CspArrayType&>( *csptype );
field = switchCspType( arrayType.elemType(), [csptype,keystr]( auto tag ) -> std::shared_ptr<StructField>
field = ArraySubTypeSwitch::invoke( arrayType.elemType(), [csptype,keystr]( auto tag ) -> std::shared_ptr<StructField>
{
using CElemType = typename decltype(tag)::type;
return std::make_shared<ArrayStructField<CElemType>>( csptype, keystr );
Expand Down Expand Up @@ -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;
}

Expand All @@ -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}
};
Expand Down
32 changes: 14 additions & 18 deletions csp/tests/impl/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]


Expand Down Expand Up @@ -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]

Expand All @@ -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()
Expand Down Expand Up @@ -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")
Expand Down

0 comments on commit 0fe40ce

Please sign in to comment.