/*******************************************
	DML_ShMem.hpp
	
	version:
		1.3
		
	Description:


	The ShMem template class provides a slightly easier (but with some weird syntax and requirements)
	interface to the QNX shared memory subsystem. The coolness factor of SharedMemory
	objects is improved. :-)
	
	Syntax:	(take a note: syntax is WEIRD)
	-------------
	i.e. to create a shared memory object out of a struct of type "dataStruct"
	with a local variable name of "data_shm" and a SharedMemory name of "sharedDataStruct"
	
	1.)  ShMem<dataStruct> * data_shm = new("sharedDataStruct") ShMem<dataStruct>;
		
		OR
		
	2.)  dataStruct * data_shm = new("sharedDataStruct") ShMem<dataStruct>;
	
	(the second form works because ShMem<dataStruct> is a derivative of dataStruct. if you use
	this method, however, you will NOT be able to use the delete command to automatically remove the
	shared memory from the filesystem: if that's OK, go ahead and use #2.
		
	references to members of the original dataStruct type are accomplished with the 
	pointer structure dereference operator ->, for instance,
	
	if dataStruct has a member called "foo" for instance, this would reference it:
	data_shm->foo.
	
	( This really isn't any different from the QNX shared memory implementation: all structure referencing
		must be done with -> using that method too. )
		
	NOTE TO USERS OF PREVIOUS VERSIONS (1.1 and earlier)
	previously, you used the member function unlink() to unlink the shared memory and re-map the local pointer to NULL.
	This function is no longer available: use delete instead.
	
	In general, opening and deleting shared memory several times in a program is a bad idea. It's much easier to just
	create static shared memory references in each module that needs it, rather than opening and deleting references
	every time.
		
	Use the destroy_shm function in a signal (i.e. SIGTERM) handler to clean up the shared memory files
	when a program exits. NOTE: use of destroy_shm will completely delete the shared memory objects, so all processes
	which might be using that shared memory will be affected.
	
	
	example:
	-----------------------------
	struct myDataStruct {
		int p;
		float q;
	};
	const char myDataStructShmName[] = "myDataStruct_shm";
	
	void main( void )
	{
		myDataStruct *data1 = new(myDataStructShmName) ShMem<myDataStruct>;
		
		data1->p = 10;
		data1->q = -3e04;
		
		printf("data in myDataStruct_shm: %d  %f\n",data->p, data->q);
		
		//open up a SECOND reference to shared memory
		ShMem<myDataStruct> *data2 = new(myDataStructShmName) ShMem<myDataStruct>;
		
		//we can delete data2 if we want, but not data1
		delete data2;

		delete data1;	//Error! probably will fail at run-time. Uses global ::delete rather than
						//ShMem<data1>::delete
								
		//unless we re-cast data1 as a ShMem<myDataStruct> pointer.
		delete ( (ShMem<myDataStruct> *) data1 );
	}
	
	
	
	Requirements:
	--------------
	DO NOT attempt to use this shared memory creation scheme for single-value types,
	i.e. don't try to create a ShMem<int> instance, or a ShMem<float> instance. Use it
	only for struct/class shared memories! For single data types, use the included functions 
	create_shm, close_shm, and destroy_shm (defined in this header)
	
	The ShMem class template is ONLY usable by creating a pointer to the template instance
	and then using the "new" operator to allocate the space. Simply creating a (non-pointer)
	ShMem<Type> variable is sure to result in some type of error.
	
	Although it is possible to create two distinct shared memory objects of the same structure
	(i.e. different filenames), if you do so the program will NOT be thread-safe. Specifically,
	if two different objects with different filenames try to delete at the same time, the
	static_shm_name will be overwritten. So don't do this in a multi-threading program:
	
	dummy *foo1 = new("foo1") ShMem<dummy>;
	dummy *foo2 = new("foo2") ShMem<dummy>;
	
	It is, of course, perfectly fine in a non-multi-threaded program.
	
	
	History:
	----------------------
	author:  Ryan Findley
	
	when	who		what
	5/12/99	ryan	created (feeling sneaky with this class)
	7/29/99	ryan	checked it out: new instances to NOT automatically over-write zeros, and 
					multiple occurrences of template<types> across module boundaries are OK.
	8/2/99	ryan	Fixed the delete operator, and changed the new operator and constructor/destructor.
					ShMem now seems fairly bullet-proof.
	1/14/00 ryan	Ditched the unlink() operator which is no longer needed because earlier, I didn't fully understand the way
					delete works. (delete replaces unlink) Also made delete public
	1/18/00 ryan	when inheriting, if the inherited class has a non-virtual destructor, it issues a warning but works fine.
					If the destructor is virtual, then it pukes.
	1/18/00	ryan	Inheritance of regular functions seems to work just fine.
	1/18/00 ryan	Inheritance of VIRTUAL functions does NOT work. Don't use virtual functions in shared memory classes.
*********************************************************************/

#ifndef DML_SHM_H
#define DML_SHM_H

//------------------------ public includes --------------------------
#include <stddef.h>
#include <string.h> 
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <limits.h>

//------------------------ public definitions -----------------------
#define MAX_SHMEM_NAME_LENGTH NAME_MAX

//------------------------ public prototypes ------------------------

void * 	create_shm 	( const char * shm_name, size_t sz, int oflag );
void 	close_shm	( const char * shm_name );
void 	destroy_shm ( const char * shm_name );


//splits up the parts of create_shm... so you can have access to the shm_fd.
int 	open_shm_fd	( const char * shm_name, size_t sz, int oflag = O_RDWR );
void * 	map_shm		( int shm_fd, size_t sz, int oflag = O_RDWR );




//------------------------ class definitions ------------------------
template <class Type> class ShMem : public Type
{
  public:
    void * operator new( size_t, const char *name, int oflag = O_RDWR );
    void   operator delete( void *, size_t );

	ShMem();
	~ShMem();
	
	int fd();
	
  private:
    char 			shm_name[MAX_SHMEM_NAME_LENGTH +1];
	int 			shm_fd;
	static int 		static_shm_fd;
    static char * 	static_shm_name;
};




//------------------------- template definitions ------------------------

/************************************************
	re-map the operator new definition
	now the constructor allocates space from shared memory, 
	rather than from the free store memory area.
*************************************************/
template <class Type>
    void * 
    ShMem<Type>::operator new(size_t sz, const char * name, int oflag)
{
    void * the_ptr;
	
	static_shm_fd = open_shm_fd( name, sz, oflag );
    the_ptr = map_shm(static_shm_fd, sz, oflag );
	
    strncpy(static_shm_name,name,MAX_SHMEM_NAME_LENGTH);
	if(strlen(name) > MAX_SHMEM_NAME_LENGTH )
		printf("shmem name %s was too long: truncated to %s.\n", name, static_shm_name);	
	
	return(the_ptr); 
}


/*****************************
	returns the file descriptor, if you want it.
******************************/
template <class Type>
	ShMem<Type>::fd()
{
	return(shm_fd);
}


/*************************************************
	re-map the operator delete definition
	it will try to free the memory and zero the contents.
	we need to stop this behavior.
**************************************************/
template <class Type>
    void 
    ShMem<Type>::operator delete(void *ptr, size_t sz)
{
    if( ptr )
	{
		if( munmap(ptr,sz) )
			printf("Shared memory error: Error un-mapping %s!!!\n",static_shm_name);
		close_shm(static_shm_name);
	}
}


/**************************************************
	constructor and destructor move static data to local data,
	and handle the shared memory file descriptor.
***************************************************/
template <class Type>
	ShMem<Type>::ShMem()
{
	strcpy(shm_name, static_shm_name);
	shm_fd = static_shm_fd;
}

template <class Type>
	ShMem<Type>::~ShMem()
{
	strcpy(static_shm_name, shm_name);
	close(shm_fd);
}
	


/*************************************************
	static member initialization
**************************************************/
template <class Type>
	char *
	ShMem<Type>::static_shm_name = new char[MAX_SHMEM_NAME_LENGTH+1];

template <class Type>
	int
	ShMem<Type>::static_shm_fd = -1;	
	
	




/**********************************************
Here's another idea.... see if you can make it work!!!

The idea is to make a base class from which shared memory classes would
inherit. it's the opposite of the ShMem<> template class above.

Basically, the goal would be to create a base class from which any class
might inherit. You could use the same code from above... might have to adapt
it just slightly.

The advantages:
- you wouldn't need the crappy template class syntax
- regular class instances would be non-shared memory, but
  use of the "new" operator would be shared memory.

disadvantages:
- it's intrusive: you couldn't just take any random class and turn it into
  shared memory. it would have to be changed in the header.
- you couldn't make shared memory out of previously defined classes
- it might be hard to tell the difference between a shared version (i.e. "new" operator-created)
  and a non-shared version.
  
Give it a try, just for fun!
****************************************************/

/*
class ShMemBase
{
  public:
    void * operator new( size_t, const char *name, int oflag = O_RDWR );
    void   operator delete( void *, size_t );

	ShMemBase();
	~ShMemBase();
	
	int fd();										//returns the FileDescriptor
	
  private:
    char shm_name[MAX_SHMEM_NAME_LENGTH +1];
    static char * static_shm_name;

	int shm_fd;
	static int static_shm_fd;
};
**********************/

#endif //DML_SHM_H