#include <stdint.h>
#include <time.h>
#include <stdio.h>
#include <atomic>

const size_t NUM_CALLS = 10000000;

//////////////////////////////// timing helpers ////////////////////////////////

double GetSeconds()
{
	return ( double )clock() / CLOCKS_PER_SEC;
}

void PrintElapsedTime( double ElapsedTime )
{
	printf( "%f s/Mcalls\n", float( ElapsedTime / double( NUM_CALLS / 10000000 ) )  );
}

//////////////////////////////// adhoc intrusive smartpointer ////////////////////////////////

class iIntrusiveCounter
{
public:
	iIntrusiveCounter():FRefCounter(0) {};
	virtual ~iIntrusiveCounter() {}
	void    IncRefCount() { FRefCounter++; }
	void    DecRefCount() { if ( --FRefCounter == 0 ) { delete this; } }
private:
	std::atomic<int> FRefCounter;
};

/// Intrusive smart pointer
template <class T> class clPtr
{
public:
	/// default ctor
	clPtr(): FObject( 0 ) {}
	clPtr( const clPtr& Ptr ): FObject( Ptr.FObject ) { FObject->IncRefCount(); }
	clPtr( T* const Object ): FObject( Object ) { FObject->IncRefCount(); }
	~clPtr() { FObject->DecRefCount(); }
	clPtr& operator = ( const clPtr& Ptr )
	{
		T* Temp = FObject;
		FObject = Ptr.FObject;
		Ptr.FObject->IncRefCount();
		Temp->DecRefCount();
		return *this;
	}
	inline T* operator -> () const { return FObject; }
private:
	T*    FObject;
};

////////////////////////////////

class clTestObject: public iIntrusiveCounter
{
public:
	clTestObject():FPayload(32167) {}

	void Do()
	{
		FPayload+=3;
	}

private:
	int FPayload;
};

////////////////////////////////

void ProcessByValue( clPtr<clTestObject> O )
{
	O->Do();
}

void ProcessByConstRef( const clPtr<clTestObject>& O )
{
	O->Do();
}

////////////////////////////////

int main()
{
	clPtr<clTestObject> Obj = new clTestObject;

	for ( size_t j = 0; j != 3; j++ )
	{
		double StartTime = GetSeconds();

		for ( size_t i = 0; i != NUM_CALLS; i++ ) { ProcessByValue( Obj ); }

		PrintElapsedTime( GetSeconds() - StartTime );
	}

	printf( "\n" );

	for ( size_t j = 0; j != 3; j++ )
	{
		double StartTime = GetSeconds();

		for ( size_t i = 0; i != NUM_CALLS; i++ ) { ProcessByConstRef( Obj ); }

		PrintElapsedTime( GetSeconds() - StartTime );
	}

	return 0;
}
