[Native] TArray<BYTE> workaround for modern compilers

Discussions about Coding and Scripting
Post Reply
Skydev
Posts: 1
Joined: Fri Jan 02, 2015 2:30 pm

[Native] TArray<BYTE> workaround for modern compilers

Post by Skydev »

I have made a workaround today for TArray<BYTE> errors for myself in half a hour using explicit specialization and exporting of the specialized class.
At UnTemplate.h add right after end of TArray<T> definition:

Code: Select all

template<> class DLL_EXPORT TArray<BYTE> : public FArray {
public:
	typedef BYTE ElementType;
	TArray()
	:	FArray()
	{}
	TArray( INT InNum )
	:	FArray( InNum, 1 )
	{}
	TArray( const TArray& Other )
	:	FArray( Other.ArrayNum, 1 )
	{
		guardSlow(TArray::copyctor);
		appMemcpy( &(*this)(0), &Other(0), ArrayNum );
		unguardSlow;
	}
	TArray( ENoInit )
	: FArray( E_NoInit )
	{}
	~TArray()
	{
		checkSlow(ArrayNum>=0);
		checkSlow(ArrayMax>=ArrayNum);
		Remove( 0, ArrayNum );
	}
    BYTE& operator()( INT i )
	{
		guardSlow(TArray::operator());
		checkSlow(i>=0);
		checkSlow(i<=ArrayNum);
		checkSlow(ArrayMax>=ArrayNum);
		return ((BYTE*)Data)[i];
		unguardSlow;
	}
	const BYTE& operator()( INT i ) const
	{
		guardSlow(TArray::operator());
		checkSlow(i>=0);
		checkSlow(i<=ArrayNum);
		checkSlow(ArrayMax>=ArrayNum);
		return ((BYTE*)Data)[i];
		unguardSlow;
	}
	BYTE Pop()
	{
		guardSlow(TArray::Pop);
		check(ArrayNum>0);
		checkSlow(ArrayMax>=ArrayNum);
		BYTE Result = ((BYTE*)Data)[ArrayNum-1];
		Remove( ArrayNum-1 );
		return Result;
		unguardSlow;
	}
	BYTE& Last( INT c=0 )
	{
		guardSlow(TArray::Last);
		check(c<ArrayNum);
		checkSlow(ArrayMax>=ArrayNum);
		return ((BYTE*)Data)[ArrayNum-c-1];
		unguardSlow;
	}
	const BYTE& Last( INT c=0 ) const
	{
		guardSlow(TArray::Last);
		checkSlow(c<ArrayNum);
		checkSlow(ArrayMax>=ArrayNum);
		return ((BYTE*)Data)[ArrayNum-c-1];
		unguardSlow;
	}
	void Shrink()
	{
		guardSlow(TArray::Shrink);
		FArray::Shrink( 1 );
		unguardSlow;
	}
	UBOOL FindItem( const BYTE& Item, INT& Index ) const
	{
		guardSlow(TArray::FindItem);
		for( Index=0; Index<ArrayNum; Index++ )
			if( (*this)(Index)==Item )
				return 1;
		return 0;
		unguardSlow;
	}
	INT FindItemIndex( const BYTE& Item ) const
	{
		guardSlow(TArray::FindItemIndex);
		for( INT Index=0; Index<ArrayNum; Index++ )
			if( (*this)(Index)==Item )
				return Index;
		return INDEX_NONE;
		unguardSlow;
	}
	friend FArchive& operator<<( FArchive& Ar, TArray& A )
	{
		guard(TArray<<);
		A.CountBytes( Ar );
		// Serialize simple bytes which require no construction or destruction.
		Ar << AR_INDEX(A.ArrayNum);
		if( Ar.IsLoading() )
		{
			A.ArrayMax = A.ArrayNum;
			A.Realloc( 1 );
		}
		Ar.Serialize( &A(0), A.Num() );
		return Ar;
		unguard;
	}
	void CountBytes( FArchive& Ar )
	{
		guardSlow(TArray::CountBytes);
		FArray::CountBytes( Ar, 1 );
		unguardSlow;
	}

	// Add, Insert, Remove, Empty interface.
	INT Add( INT n=1 )
	{
		guardSlow(TArray::Add);
		return FArray::Add( n, 1 );
		unguardSlow;
	}
	void Insert( INT Index, INT Count=1 )
	{
		guardSlow(TArray::Insert);
		FArray::Insert( Index, Count, 1 );
		unguardSlow;
	}
	void InsertZeroed( INT Index, INT Count=1 )
	{
		guardSlow(TArray::InsertZeroed);
		FArray::InsertZeroed( Index, Count, 1 );
		unguardSlow;
	}
	void Remove( INT Index, INT Count=1 )
	{
		guardSlow(TArray::Remove);
		check(Index>=0);
		check(Index<=ArrayNum);
		check(Index+Count<=ArrayNum);
		FArray::Remove( Index, Count, 1 );
		unguardSlow;
	}
	void Empty( INT Slack=0 )
	{
		guardSlow(TArray::Empty);
		FArray::Empty( 1, Slack );
		unguardSlow;
	}

	// Functions dependent on Add, Remove.
	TArray& operator=( const TArray& Other )
	{
		guardSlow(TArray::operator=);
		if( this != &Other )
		{
			Empty( Other.ArrayNum );
			appMemcpy( &(*this)(0), &Other(0), ArrayNum );
			//for( INT i=0; i<Other.ArrayNum; i++ )
			//	new( *this )BYTE( Other(i) );
		}
		return *this;
		unguardSlow;
	}
	INT AddItem( const BYTE& Item )
	{
		guardSlow(TArray::AddItem);
		INT Index=Add();
		(*this)(Index)=Item;
		return Index;
		unguardSlow;
	}
	INT AddZeroed( INT n=1 )
	{
		guardSlow(TArray::AddZeroed);
		return FArray::AddZeroed( 1, n );
		unguardSlow;
	}
	INT AddUniqueItem( const BYTE& Item )
	{
		guardSlow(TArray::AddUniqueItem);
		for( INT Index=0; Index<ArrayNum; Index++ )
			if( (*this)(Index)==Item )
				return Index;
		return AddItem( Item );
		unguardSlow;
	}
	INT RemoveItem( const BYTE& Item )
	{
		guardSlow(TArray::RemoveItem);
		INT OriginalNum=ArrayNum;
		for( INT Index=0; Index<ArrayNum; Index++ )
			if( (*this)(Index)==Item )
				Remove( Index-- );
		return OriginalNum - ArrayNum;
		unguardSlow;
	}

	// Iterator.
	class TIterator
	{
	public:
		TIterator( TArray<BYTE>& InArray ) : Array(InArray), Index(-1) { ++*this;      }
		void operator++()      { ++Index;                                           }
		void RemoveCurrent()   { Array.Remove(Index--); }
		INT GetIndex()   const { return Index;                                      }
		operator UBOOL() const { return Index < Array.Num();                        }
		BYTE& operator*()   const { return Array(Index);                               }
		BYTE* operator->()  const { return &Array(Index);                              }
		BYTE& GetCurrent()  const { return Array( Index );                             }
		BYTE& GetPrev()     const { return Array( Index ? Index-1 : Array.Num()-1 );   }
		BYTE& GetNext()     const { return Array( Index<Array.Num()-1 ? Index+1 : 0 ); }
	private:
		TArray<BYTE>& Array;
		INT Index;
	};
};
Now my native AllObjects iterator perfectly compiles and works in Visual Studio 2012!
P.S: I am using some modified headers too (That are for compiling stuff on modern visual studios)...
Higor
Godlike
Posts: 1866
Joined: Sun Mar 04, 2012 6:47 pm

Re: [Native] TArray<BYTE> workaround for modern compilers

Post by Higor »

Bookmarked.
Chris
Experienced
Posts: 134
Joined: Mon Nov 24, 2014 9:27 am

Re: [Native] TArray<BYTE> workaround for modern compilers

Post by Chris »

For anyone looking to use this piece of code, the assignment operator is broken.

Code: Select all

TArray& operator=( const TArray& Other )
	{
		guardSlow(TArray::operator=);
		if( this != &Other )
		{
			Empty( Other.ArrayNum );
			appMemcpy( &(*this)(0), &Other(0), ArrayNum );
			//for( INT i=0; i<Other.ArrayNum; i++ )
			//	new( *this )BYTE( Other(i) );

		}
		return *this;
		unguardSlow;
	}
While the call to Empty does optionally reallocate the array, it doesn't set the used size of the array.
In other words, you're just emptying the array without actually copying any data.

This is my fixed version:

Code: Select all

TArray& operator=( const TArray& Other )
	{
		guardSlow(TArray::operator=);
		if( this != &Other )
		{
			Empty( Other.ArrayMax );
			ArrayNum = Other.ArrayNum;
			appMemcpy( Data, Other.Data, ArrayNum );
			//for( INT i=0; i<Other.ArrayNum; i++ )
			//	new( *this )BYTE( Other(i) );

		}
		return *this;
		unguardSlow;
	}
Post Reply