Page 1 of 1

[Native] TArray<BYTE> workaround for modern compilers

Posted: Fri Jan 02, 2015 2:35 pm
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)...

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

Posted: Fri Jan 02, 2015 4:29 pm
by Higor
Bookmarked.

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

Posted: Tue Sep 24, 2019 11:00 pm
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;
	}