//Smartpointer.h
#ifndef SMARTPOINTER_H
#define SMARTPOINTER_H

#include <map>

extern std::map<const void *, unsigned int> referencecounter;

template <class T> class Smartpointer{
public:
	inline Smartpointer(){
		target = new T;
		referencecounter[target]++;
	}
	/*
	TODO: Fix constructors with a variadic template some day
	python code to generate constructors:
	s = ""
	for i in range(1, 11):
		s += "\ttemplate <"
		for j in range(1, i+1):
			s += "class A"+str(j)+", "
		s = s[:len(s) - 2]
		s += "> inline Smartpointer("

		for j in range(1, i+1):
			s += "const A"+str(j)+" &a"+str(j)+", "
		s = s[:len(s) - 2]
		s += "){\n\t\ttarget = new T("
		for j in range(1, i+1):
			s += "a"+str(j)+", "
		s = s[:len(s) - 2]
		s += ");\n\t\treferencecounter[target]++;\n\t}\n"
	print s
	*/
	template <class A1> inline Smartpointer(const A1 &a1){
		target = new T(a1);
		referencecounter[target]++;
	}
	template <class A1, class A2> inline Smartpointer(const A1 &a1, const A2 &a2){
		target = new T(a1, a2);
		referencecounter[target]++;
	}
	template <class A1, class A2, class A3> inline Smartpointer(const A1 &a1, const A2 &a2, const A3 &a3){
		target = new T(a1, a2, a3);
		referencecounter[target]++;
	}
	template <class A1, class A2, class A3, class A4> inline Smartpointer(const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4){
		target = new T(a1, a2, a3, a4);
		referencecounter[target]++;
	}
	template <class A1, class A2, class A3, class A4, class A5> inline Smartpointer(const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5){
		target = new T(a1, a2, a3, a4, a5);
		referencecounter[target]++;
	}
	template <class A1, class A2, class A3, class A4, class A5, class A6> inline Smartpointer(const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5, const A6 &a6){
		target = new T(a1, a2, a3, a4, a5, a6);
		referencecounter[target]++;
	}
	template <class A1, class A2, class A3, class A4, class A5, class A6, class A7> inline Smartpointer(const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5, const A6 &a6, const A7 &a7){
		target = new T(a1, a2, a3, a4, a5, a6, a7);
		referencecounter[target]++;
	}
	template <class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8> inline Smartpointer(const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5, const A6 &a6, const A7 &a7, const A8 &a8){
		target = new T(a1, a2, a3, a4, a5, a6, a7, a8);
		referencecounter[target]++;
	}
	template <class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8, class A9> inline Smartpointer(const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5, const A6 &a6, const A7 &a7, const A8 &a8, const A9 &a9){
		target = new T(a1, a2, a3, a4, a5, a6, a7, a8, a9);
		referencecounter[target]++;
	}
	template <class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8, class A9, class A10> inline Smartpointer(const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5, const A6 &a6, const A7 &a7, const A8 &a8, const A9 &a9, const A10 &a10){
		target = new T(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
		referencecounter[target]++;
	}

	inline Smartpointer(const Smartpointer &sp){
		target = sp.target;
		referencecounter[target]++;
	}
	template <class U> inline Smartpointer(Smartpointer<U> &sp){
		target = &(*sp);
		referencecounter[target]++;
	}
	inline Smartpointer<T> & operator = (const Smartpointer<T> &sp){
		if (target != sp.target){ //if they are equal nothing changes
			this->~Smartpointer(); //call the destructor to remove reference
			target = sp.target;
			referencecounter[target]++;
		}
		return *this;
	}
	inline bool operator < (const Smartpointer<T> &sp) const{
		return target < sp.target;
	}
	inline bool operator <= (const Smartpointer<T> &sp) const{
		return target <= sp.target;
	}
	inline bool operator > (const Smartpointer<T> &sp) const{
		return target > sp.target;
	}
	inline bool operator >= (const Smartpointer<T> &sp) const{
		return target >= sp.target;
	}
	inline bool operator == (const Smartpointer<T> &sp) const{
		return target == sp.target;
	}
	inline bool operator != (const Smartpointer<T> &sp) const{
		return target != sp.target;
	}
	inline T * operator -> (){
		return target;
	}
	inline const T * operator -> () const{
		return target;
	}
	inline T & operator * (){
		return *target;
	}
	inline const T & operator * () const{
		return *target;
	}
	inline ~Smartpointer(){
		if (referencecounter.count(target)){
			if (referencecounter[target] == 1){ //we are the last
				delete target;
				referencecounter.erase(target);
			}
			else //we are not the last
				referencecounter[target]--;
		}
	}
	static Smartpointer<T> adopt(T *t){
		Smartpointer<T> *st = static_cast<Smartpointer<T> *>(::operator new(sizeof st));
		st->target = t;
		Smartpointer<T> lst(*st);
		::operator delete(st);
		return lst;
	}
private:
	T *target;
};
 
//template <class T> std::map<const T *, unsigned int> Smartpointer<T>::referencecounter;

#endif //SMARTPOINTER_H


//Smartpointer.cpp
//#include "smartpointer.h"
std::map<const void *, unsigned int> referencecounter;

//main.cpp
//#include "smartpointer.h"
#include <iostream>

class DifficultClass{
public:
    DifficultClass(int i, double d){
		this->count = ++counter;
		std::cout << "created DCO " << count << '\n';
	}
	~DifficultClass(){
		std::cout << "destroyed DCO " << count << '\n';
	}
private:
	DifficultClass(const DifficultClass &dc);
	static int counter;
	int count;
};
int DifficultClass::counter;

class DerivedClass : public DifficultClass{
public:
	DerivedClass(const char *p) : DifficultClass(42, 17.){
	}
};

int main(){
	std::cout << "\nLine " << __LINE__ << " Simple scope --------------------------\n";
	{
		Smartpointer<DifficultClass> dcsp(42, 17.);
	}
	std::cout << "\nLine " << __LINE__ << " Copies and scope -------------------------------\n";
	{
		Smartpointer<DifficultClass> dcsp(42, 17.);
		Smartpointer<DifficultClass> dcsp2(dcsp);
		{
			Smartpointer<DifficultClass> dcsp3(dcsp);
			Smartpointer<DifficultClass> dcsp4(dcsp3);
		}
		Smartpointer<DifficultClass> dcsp3(dcsp);
	}
	std::cout << "\nLine " << __LINE__ << " assignment -------------------------------\n";
	{
		Smartpointer<DifficultClass> dcsp1(42, 17.);
		Smartpointer<DifficultClass> dcsp2(17, 42.);
		{
			dcsp1 = dcsp2;
		}
	}
	std::cout << "\nLine " << __LINE__ << " derived class with downcast -------------------------------\n";
	{
		Smartpointer<DerivedClass> dcsp("hello");
		{
			Smartpointer<DifficultClass> dcsp2(dcsp); //automatic downcast
		}
	}
	std::cout << "\nLine " << __LINE__ << " adopting pointers -------------------------------\n";
	{
		DifficultClass *dc = new DifficultClass(42, 7.);
		Smartpointer<DifficultClass> dcsp = Smartpointer<DifficultClass>::adopt(dc);
		Smartpointer<DifficultClass> dcsp2 = Smartpointer<DifficultClass>::adopt(dc);
		Smartpointer<DifficultClass> dcsp3 = Smartpointer<DifficultClass>::adopt(dc);
	}
}
