3D GUI

All off topic discussions go here. Everything from the funny thing your cat did to your favorite tv shows. Non-programming computer questions are ok too.
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

hmm...smart pointers just seem like too much overhead for such a commonly done thing(I tend to use too many pointers though)
It is not too hard. Here is one that is smart to check it's bounds. I decided to try and see if I could make something even remotely useful.

Code: Select all

template <class T> class cPointer
{
	private:
	T			*___ptr;
	uintptr_t	___bytes;
	uint32_t	___index;
	public:
	cPointer()
	{
		___ptr = 0;
		___bytes = 0;
		___index = 0;
	}
	~cPointer()
	{
		if(___ptr)
		{
			delete [] ___ptr;
		}
		___ptr = 0;
		___bytes = 0;
		___index = 0;
	}
	uint32_t New(uint32_t itemCount)
	{
		if(___ptr)
		{
			delete [] ___ptr;
		}
		if(itemCount < 1)
		{
			___ptr = 0;
			return 0;
		}
		___ptr = (T*)new T[itemCount];
		___bytes = sizeof(T) * itemCount;
		___index = 0;
		return ___bytes;
	}
	cPointer& operator=(cPointer &a)
	{
		___ptr = a.___ptr;
		return *this;
	}
	cPointer& operator=(T *a)
	{
		___ptr = a;
		return *this;
	}
	T& operator[](int index)
	{
		if((index * sizeof(T)) >= ___bytes)
		{
			throw("Access out of bounds for pointer to array.\n");
		}
		return ___ptr[index];
	}
	void operator++()
	{
		++___index;
	}
	void operator--()
	{
		--___index;
	}
	T& operator*()
	{
		if((___index * sizeof(T)) >= ___bytes)
		{
			throw("Access out of bounds for pointer to array.\n");
		}
		return ___ptr[___index];
	}
};


int main(int argc, char *argv[])
{
	cPointer<uint32_t> ptr;

	ptr.New(5);

	*ptr = 12;

	++ptr;
	--ptr;

	uint32_t x  = *ptr;

	printf("%u\n", x);

	return 1;
}
It does not appear to be much overhead. I say this also by noting that I am posting in the general ramblings and programming forum group. At the same time it might not cause that much of a performance drop if used in a kernel.

The majority of kernel and driver functions are going to deal with pages or regions of memory, but only occasionally (I figure) you would deal with a small array that was dynamically created, or passed to a driver through some mechanism. You might even create a static array at compile time and include bounds checking by using one similar to:

Code: Select all

template <class T, int C> cPointer{
  private:
  T ___const[C];
  ....
};
Almost all the overhead gets optimized away; if done correctly (in the context of bounds checking, and the normal expectation of a function call to check the bounds).
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Post by Colonel Kernel »

That does indeed look useful, but I would argue it is not a smart pointer in the traditional sense. It is more like an array class with bounds checking. Smart pointers are typically used to point to only a single object, not an array of objects. In general, the purpose of a smart pointer is to act as a proxy to that object so that some action can be taken automatically (e.g. -- automatic deleting of the object when it is no longer in use, automatic locking/unlocking of a mutex around every method call to the object, etc.)

Other than the support for bounds checking, std::vector basically does what your class does, but vectors can also grow in length as elements are added to them.

A few style points -- you don't need to cast the return type of the new operator:

Code: Select all

___ptr = (T*)new T[itemCount];
The (T*) in that code is redundant, since the return type of operator new matches the type used in the constructor expression, and T[] is trivially convertible to T*.

Also, it is a better design to keep the iteration facility (your operator++ and -- and the __index field) separate from the container class. Part of the reason is just separation of concerns -- containing and iterating are two different responsibilities, and having a class for each would result in more maintainable code in the long run than putting them both together. Also, it is sometimes useful to have multiple iterations happening on a container at the same time, which your design precludes. The STL always separates containers from iterators for these reasons.
Kevin McGuire wrote:Almost all the overhead gets optimized away; if done correctly (in the context of bounds checking, and the normal expectation of a function call to check the bounds).
IMO it is unfortunate that bounds-checked arrays are not part of C++. If they were, the compiler would have a fighting chance of optimizing away bounds checks when it can prove that the index being used is always in range (difficult, but probably possible often enough for decent performance gains). When bounds checking is done as part of a library like this, the compiler can't optimize it away.

Looks like we're getting way OT (this used to be about 3D GUIs, didn't it?) Maybe a mod can split the thread (I assume just anybody can't)...
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

In general, the purpose of a smart pointer is to act as a proxy to that object so that some action can be taken automatically (e.g. -- automatic deleting of the object when it is no longer in use, automatic locking/unlocking of a mutex around every method call to the object, etc.)
I decided to see if I could add automatic garbage collection when no longer in use anywhere in the memory space.

Code: Select all

struct sPointerGlobal
{
	uint32_t	instance;
};

template <class T, int E> class cPointer
{
	private:
	T					*___ptr;
	uintptr_t			___bytes;
	uint32_t			___index;
	sPointerGlobal		___global;
	public:
	cPointer()
	{
		___ptr = 0;
		___bytes = 0;
		___index = 0;
		___global = 0;
	}
	~cPointer()
	{
		if(___ptr)
		{
			--___global->instance;
			if(___global->instance == 0)
			{
				delete [] ___ptr;
				delete ___global;
			}
		}
		___ptr = 0;
		___bytes = 0;
		___index = 0;
		___global = 0;
	}
	uint32_t New(uint32_t itemCount)
	{
		if(___ptr)
		{
			delete [] ___ptr;
		}
		if(itemCount < 1)
		{
			___ptr = 0;
			return 0;
		}
		___ptr = (T*)new T[itemCount];
		___bytes = sizeof(T) * itemCount;
		___index = 0;
		___global = (sPointerGlobal*)new sPointerGlobal;
		___global->instance = 1;
		return ___bytes;
	}
	cPointer& operator=(cPointer &a)
	{
		___ptr = a.___ptr;
		___index = a.___index;
		___bytes = a.___bytes;
		___global = a.___global;
		++___global->instance;
		return *this;
	}
	cPointer& operator=(T *a)
	{
		___ptr = a;
		return *this;
	}
	T& operator[](int index)
	{
		if((index * sizeof(T)) >= ___bytes)
		{
			throw("Access out of bounds for pointer to array.\n");
		}
		return ___ptr[index];
	}
	void operator++()
	{
		++___index;
	}
	void operator--()
	{
		--___index;
	}
	T& operator*()
	{
		if((___index * sizeof(T)) >= ___bytes)
		{
			throw("Access out of bounds for pointer to array.\n");
		}
		return ___ptr[___index];
	}
};
Do you want me to implement the automatic locking and unlocking of a mutex for every method call?
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

Oh. Yeah. I forgot to tell you, and I remembered when reading back over my post.
Also, it is a better design to keep the iteration facility (your operator++ and -- and the __index field) separate from the container class. Part of the reason is just separation of concerns -- containing and iterating are two different responsibilities, and having a class for each would result in more maintainable code in the long run than putting them both together. Also, it is sometimes useful to have multiple iterations happening on a container at the same time, which your design precludes. The STL always separates containers from iterators for these reasons.
This I have never thought about. I do not use the STL hardly at all, but I will do some thinking about it. It makes very good sense, and I appreciate the tip.
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Post by Colonel Kernel »

Kevin McGuire wrote:I decided to see if I could add automatic garbage collection when no longer in use anywhere in the memory space.
You've managed to duplicate the shared reference-counting technique used by most smart pointers. Take a look at boost::shared_ptr. It works similarly, although I believe it is somewhat customizable so that, for example, you can use it with objects that store their reference count internally and expose it via a pair of methods (e.g. -- AddRef() and Release() in COM).

BTW, I think your implementations of operator= have a few bugs.
Do you want me to implement the automatic locking and unlocking of a mutex for every method call?
I said that you could use smart pointers to implement such a thing -- it has nothing to do with what I want. ;) Here is an excerpt from "Modern C++ Design" that discusses this technique.
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

Do you think a smart pointer could automatically lock and unlock a mutex for any arbitrary method call. My explanation is most likely very hard to understand. Let me make a example below using my pointer class above.


cKevinPointer<cColonelKernelObject> pColonelKernelObject;
pColonelKernelObject.New(1);
/// The ___ptr is the same as in my cPointer class. T *___ptr.
pColonelKernelObject.___ptr->ColonelKernelMethod(1, 2, 3);


Where the developer of cColonelKernelObeject never modified the cKevinPointer implementation, and the developer of cKevinPointer never knew of the existance of the cColonelKernelObject implementation.

class cColonelKernelObject
{
public:
uint32_t ColonelKernelMethod(int32_t, int32_t, int32_t);
};


Alright. Now, what I am asking is when I do:

pColonelKernelObject.___ptr->ColonelKernelMethod(1, 2, 3);

Will the cKevinPointer object be able to lock the mutex on this call, and unlock the mutex on return from this call for the cColonelKernelObject when the cColonelKernelObject never included any functionality to help or support the locking and unlocking of a mutex?

Or, to be more specific I can ask is this possible to do and how would you speculate or guess that it would be done? And, Yes. I have two answers. :P
Last edited by Kevin McGuire on Sun Jun 17, 2007 1:07 pm, edited 1 time in total.
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

Looks like we're getting way OT (this used to be about 3D GUIs, didn't it?) Maybe a mod can split the thread (I assume just anybody can't)...
They used to split a lot more when the forum was at mega-tokyo.
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Post by Colonel Kernel »

Kevin McGuire wrote:Do you think a smart pointer could automatically lock and unlock a mutex for any arbitrary method call.
Yes, I think so. First, read the article I linked to (the excerpt from Modern C++ Design). That article gives the basis of the technique (returning a "locking proxy" smart pointer from another smart pointer's operator->). The limitation of the example in that article is that it depends on the target object having Lock() and Unlock() methods.

To get around this limitation, just do what boost::shared_ptr does -- use some auxiliary functions to wrap the "lock" and "unlock" operations so you could customize them. In this case, you can allocate a mutex using new and keep a pointer to it from each of the "main" smart pointers (i.e. -- the smart pointers that return the locking proxy from their operator-> implementations). This is exactly the same technique you used above (and that the default implementation of boost::shared_ptr uses) to add a reference count to an instance of a class without having to modify the class' code.
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
Speewave
Member
Member
Posts: 40
Joined: Fri Jun 22, 2007 1:11 pm
Location: The USA
Contact:

Post by Speewave »

there is things for linux that shows 4 desktop screens when you hit a button. i think there is one that might be similar to Windows Vista Flip!
Post Reply