Answers/Solutions to Exercises in Chapter 10, Exercise 7

E7: Write a simple C++ using smart pointers that use non-intrusive reference counting. Have them write into a log
(you can use the logging functions from Appendix D).

S7:  A sample program is below --- based on the class SmartCharPtr from previous exercises. Red indicates the modifications (additions) to SmartCharPtr class to accommodate this tracing.

#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

void sys_exit(char *fmt, ...);
void sys(char *fmt, ...);
void msg_exit(char *fmt, ...);
void msg(char *fmt, ...);
void Sys_exit(char *fmt, ...);
void Msg_exit(char *fmt, ...);
void Sys(char *fmt, ...);
void Msg(char *fmt, ...);
void print_msg(char *fmt,va_list ap,char* where);
void open_log(char* name);
char logpath[500] = "";
char buf[500];

// this class is a link in a double-linked list of references by X
template<class X> class REFLINK
{
public:
	REFLINK() { address=0; nextRef=prevRef=0; }
	REFLINK(void* ad) { address=ad; nextRef=prevRef=0; }
	void ResetFirst(X* x) { first=x; }
	void NewFirst(void* ad,X* x) {
		if (ad==address) {
			first=x;
			return;
		}else{
			if (nextRef==0) {
				nextRef = new REFLINK<X>;
				nextRef->address=ad;
				nextRef->nextRef=0;
				nextRef->prevRef=nextRef;
				nextRef->first=x;
				return;
			}else{
				nextRef->NewFirst(ad,x);
				return;
			}
		}
	}
	
	X* Add(void* ad,X* x) {    // Add x as the first, return the former first
		if (ad==address) {
			X* p;
			p=first;
			first=x;
			return p;
		}else{
			if (nextRef==0) {
				nextRef=new REFLINK<X>;
				nextRef->address=ad;
				nextRef->nextRef=0;
				nextRef->prevRef=nextRef;
				nextRef->first=x;
				return x;
			}else{
				return Add(ad,x);
			}
		}
	}

	REFLINK* RemoveRef(void* ad,REFLINK* st) {
		REFLINK* p;
		if (ad==address) {
			if (prevRef==0) {     // first reference
				if (nextRef==0) { // last reference
					delete this;
					return 0;   // no list, new empty start
				}else{          // not the last one
					nextRef->prevRef=0;
					p = nextRef;
					delete this;
					return p;   // new start
				}
			}else{              // not the first one
				if (nextRef==0) {  // last one
					prevRef->nextRef=0;
					p = prevRef;
					delete this;
					return st;  // keep original start
				}else{          // not the last one
					prevRef->nextRef=nextRef;
					nextRef->prevRef=prevRef;
					delete this;
					return st;  // keep original star
				}
			}	
		}else{
			return nextRef->RemoveRef(ad,st);
		}
	}
	

protected:
	void* address;        // this will be the root of ref siblink list for this address
	REFLINK* nextRef;     // previous ref
	REFLINK* prevRef;     // next ref
	X* first;             // first in the ref list
};


// this is "added" to the class X for reporting what th object of class X reference
// it is a link in "ref siblink list" --- it ties together the X objects that reference
// the same address
template<class X> class LINK
{
public:
	LINK() { prev=next=0; }
	void SetPrev(X* t) { prev=t; }
protected:
	X* prev;
	X* next;
	static REFLINK<X>** start;

	void Remove(void* p) {       // remove yourself from ref siblink list
		if (prev==0 && next==0) {  // you are the only one of ref siblink list
			*start=(*start)->RemoveRef(p,*start);
		}else if (prev==0) {           
			(*start)->NewFirst(p,next);// you are the first in ref siblink list
			next->prev=0;
			next=0;
			return;
		}else{                        // you are in the middle of ref siblink list
			if (prev) 
				prev->next=next;
			if (next)
				next->prev=prev;
			prev=next=0;
		}
	}
	void Add(void* p,X* x) {        // add yourself to the list of ref siblinks as first
		if (*start==0) {
			*start = new REFLINK<X>(p);
			(*start)->ResetFirst(x);
			prev=0;
		}else{
			prev=0;
			next=(*start)->Add(p,x);
			next->prev=x;
		}
	}
	void Report1(X* t) {
		X* p;
		int i;
		Msg("SmartCharPtr(%x) references address %x\n",t,(char*)t);
		for(i=1,p=prev; p!=0; p=p->prev,i++) 
			Msg("SmartCharPtr(%x) references address %x\n",p,(char*)p);
		for(p=next; p!=0; p=p->next,i++)
			Msg("SmartCharPtr(%x) references address %x\n",p,(char*)p);
		Msg("%d SmartCharPtr's reference address %x\n",i,(char*)t);
	}
};



class SmartCharPtr : private LINK<SmartCharPtr>
{
public:
	SmartCharPtr() { ptr=0; owner=0; }
	SmartCharPtr(char* p) {
		ptr=p;
		owner=1;
		Add((void*)ptr,this);
	}
	SmartCharPtr(SmartCharPtr& p) {
		ptr=p.ptr;
		owner=1;
		p.owner=0;
		Add((void*)ptr,this);
	}
	SmartCharPtr& operator=(SmartCharPtr& p) {
		if (ptr!=0) {
			Remove((void*)ptr);
			if (owner) delete[] ptr;
		}
		ptr=p.ptr;
		Add((void*)ptr,this);
		p.owner=0;
		return *this;
	}
	~SmartCharPtr() { 
		if (ptr!=0) {
			Remove((void*)ptr);
			if (owner) delete[] ptr; 
		}
	}
	SmartCharPtr& operator=(char* p) {
		if (ptr!=0) {
			Remove((void*)ptr);
			if (owner) delete[] ptr;
		}
		ptr = new char[strlen(p)+1];
		strcpy(ptr,p);
		owner=1;
		Add((void*)ptr,this);
		return *this;
	}
	void Report() { Report1(this); }
	operator char* () { return ptr; }
protected:
	char *ptr;
	int owner;
};

REFLINK<SmartCharPtr>* start1=0;
REFLINK<SmartCharPtr>** LINK<SmartCharPtr>::start=&start1;


SmartCharPtr* doit()
{
	char* p = new char[30];
	strcpy(p,"string1");
	
	SmartCharPtr p1(p);
	SmartCharPtr p2;
	p2 = p1;
	p1.Report();
	Msg("-----------------------\n");
	SmartCharPtr* p3;
	p1.Report();
	Msg("-----------------------\n");
	p3 = new SmartCharPtr(p2);
	p1.Report();
	Msg("-----------------------\n");
	return p3;
}

int main()
{
	open_log("mylog");

	SmartCharPtr* p;
	p = doit();
	p->Report();
	delete p;
	//now the value of start1 should be 0, as all references are deleted, so let us see
	Msg("start1=%d\n",start1);

	return 0;
}

/* simplified logging functions --- without locking, without time, without multiprocessing,
   without multithreading */

/* function sys_exit ------------------------------------------- */
void sys_exit(char *fmt, ...)
{
 va_list ap;

 printf("[system error] ");
 va_start(ap,fmt);
 print_msg(fmt,ap,0);
 va_end(ap);
 exit(1);
}/* end sys_exit */

/* function sys ------------------------------------------- */
void sys(char *fmt, ...)
{
 va_list ap;
 
 printf("[system error] ");
 va_start(ap,fmt);
 print_msg(fmt,ap,0);
 va_end(ap);
}/* end sys */

/* function msg_exit ------------------------------------------- */
void msg_exit(char *fmt, ...)
{
 va_list ap;
 
 va_start(ap,fmt);
 print_msg(fmt,ap,0);
 va_end(ap);
 exit(1);
}/* end msg_exit */
/* function msg ------------------------------------------- */
void msg(char *fmt, ...)
{
 va_list ap;
 
 va_start(ap,fmt);
 print_msg(fmt,ap,0);
 va_end(ap);
}/* end msg */

/* function Sys_exit ------------------------------------------- */
void Sys_exit(char *fmt, ...)
{
 va_list ap;

 va_start(ap,fmt);
 print_msg(fmt,ap,"[system error] ");
 va_end(ap);
 exit(1);
}/* end Sys_exit */

/* function Msg_exit ------------------------------------------- */
void Msg_exit(char *fmt, ...)
{
 va_list ap;
 
 va_start(ap,fmt);
 print_msg(fmt,ap,"[]");
 va_end(ap);
 exit(1);
}/* end Msg_exit */

/* function Sys ------------------------------------------- */
void Sys(char *fmt, ...)
{
 va_list ap;
 
 va_start(ap,fmt);
 print_msg(fmt,ap,"[]");
 va_end(ap);
}/* end Sys */
 
/* function Msg ------------------------------------------- */
void Msg(char *fmt, ...)
{
 va_list ap;
 
 va_start(ap,fmt);
 print_msg(fmt,ap,"[]");
 va_end(ap);
}/* end Msg */
 
/* function print_msg ------------------------------------------- */
void print_msg(char *fmt,va_list ap,char* where)
{
 FILE* fp;
 
 vsprintf(buf,fmt,ap);
 if (where==0) 
   printf("%s\n",buf);
 else{
   fp = fopen(logpath,"a");
   if (fp == NULL) {
     printf("problem to open log\n");
     exit(1);
   }
   fprintf(fp,"%s%s\n",where,buf);
 }
}/* end print_msg */

/* function open_log ------------------------------------------ */
void open_log(char* name)
{
 FILE* fp;

 strcpy(logpath,name);
 fp = fopen(logpath,"w");
 if (fp == NULL) {
   printf("error opening log\n");
   exit(1);
 }
 fprintf(fp,"log opened\n");
 fclose(fp);
}/* end open_log */

(Annotated) Log:

log opened                                         (p1 set to string, p2 set to p1)
[]SmartCharPtr(12fefc) references address 12fefc  
[]SmartCharPtr(12feec) references address 12feec   
[]2 SmartCharPtr's reference address 12fefc
[]-----------------------                          (p3 defined)
[]SmartCharPtr(12fefc) references address 12fefc    
[]SmartCharPtr(12feec) references address 12feec
[]2 SmartCharPtr's reference address 12fefc
[]-----------------------
[]SmartCharPtr(12fefc) references address 12fefc    (p3 set to p2)
[]SmartCharPtr(12feec) references address 12feec
[]SmartCharPtr(431fe0) references address 431fe0
[]3 SmartCharPtr's reference address 12fefc
[]-----------------------                           (p1,p2 go out of scope)
[]SmartCharPtr(431fe0) references address 431fe0    (p3 as returned by doit())
[]1 SmartCharPtr's reference address 431fe0
[]start1=0                                          (after delete)

Back to Answers/Solutions Index                          Back to Answers/Solutions for Chapter 10 Index