Jump to content
  • Advertisement
Sign in to follow this  
dave

Handle Based Programming

This topic is 4894 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've been in very brief discussion with someone who is telling me about this and how it is more secure etc than objects. Well i still have no idea what he means by this method of programming. Can anyone explain what it is and also how it is implemented in C++. ace

Share this post


Link to post
Share on other sites
Advertisement
If it's what I think he's talking about, it's basically using handles instead of pointers.

The idea is that if you use handles, it abstracts the "reference" concept. So where you would normally use pointers, you'd use handles. Like all kinds of abstraction, the underlying implementation can change. You could use pointers, you could use indexes into an array. Because of this, you can also have relocation of objects in memory (like for run-time defragging of your memory). You could also have custom virtual-memory, delayed-loading of objects from disk, addressing objects over a network, addresing objects in a database, and other crazy things.

A C++ implementation would probably involve a custom memory manager and a smart-pointer type object that can translate whatever "handle" method is being used to C++ native pointers.

Of course, unless you have a specialized purpose, it's not all that useful. I don't really see how it can be naturally "more secure".

Share this post


Link to post
Share on other sites
I'm not sure that I got it right, but handle-based programming must mean that instead of using raw pointers, you use some kind of manager that will automatically clean up your objects when they are not used anymore, a kind of garbage manager.

In fact, if I'm not mistaken about the concept, I have implemented this mechanism and I called it "StaticPointer".


//Normally, you'd write:

Object * ptr = new Object;
// operate on object
delete ptr;

/* But this is not very flexible, you have to keep track of the original
pointer somewhere so that you'll delete it just once and you have to make
sure that nobody is still using that object */


// Now comes how you could use a handle instead

Handle<Object> ptr = new Object;
// operate on object

/* Here, the pointer knows it has 2 instances, so only when both instances
of the handle are destroyed will the content be destroyed, this is reference
counting */

Handle<Object> ptr2 = ptr;

// But now here's the tricky thing you can do with a handle:

// here you get the pointer inside the Handle
Object * p = ptr;

/* now you reassign that raw pointer to another handle. Normally this would
cause problem for reference counted pointers, because they would consider this
a new instance, but not for a handle */

Handle<Object> h = ptr;




Anyway, that's how I see it, the above behavior is implemented in my StaticPointer, because I keep a list of all allocated handles versus the real raw pointer.

Share this post


Link to post
Share on other sites
Edit: heh. Beaten to it :P

I'm no expert, but I believe handles are basically an alternative to pointers, which can provide benefits in some situations. Depending on how they're implemented, handles could provide:
1 - Increased safety, by automatically detecting when the object they're pointing to is destroyed, and effectively setting themselves to null (for example, if the handles are being used as proxies for pointers, and have a method to return a real pointer, then that method would return 0 after the object has been destroyed)

2 - Increased flexibility, by providing a method to move objects around in memory without having to find all the pointers to them (an alternative here is to use a forwarding pointer). If all references to an object go through a handle, then when the object gets moved (which might happen to deal with memory being fragmented, or as part of a copying garbage collection scheme) only the handle needs to be updated.

3 - Decreased memory usage. If you know that you're only ever going to have a few thousand objects around, and you reference them all using handles, then individual object references can be cut down to 16 bits instead of 32 (or 64 on a 64bit architecture). Whether you actually use less memory overall will depend on how everything is organised, how many references you need to store and so on, but even if you don't use less memory in total, it may still be advantageous to use handles to reduce the size of some specific types of object so that you can pass them around faster.

Handles can be implemented using a handle table - you set up an array of handle data (the data for each handle would include a pointer to the object that the handle is representing, and possibly other information used for garbage collection or memory usage statistics, or data type information or whatever). The handles themselves are then simply indexes into this handle table. There are variations on this - if you know you could have a large number of handles, but don't want to allocate a big block of memory to start with, you might split the handle table up into sections.

John B

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
i think that if you want to write code that's relying heavily on handles and memory management... i'd really concider the net framework. in general the effort of creating a managed environment is not worth it... it already exists. just a sidenote.

Share this post


Link to post
Share on other sites
Excerpted from here:

"

What is a handle to an object? Is it a pointer? Is it a reference? Is it a pointer-to-a-pointer? What is it? Updated!
[Recently slight wordsmithing thanks to Steve Fábián (in 4/05). Click here to go to the next FAQ in the "chain" of recent changes.]
The term handle is used to mean any technique that lets you get to another object — a generalized pseudo-pointer. The term is (intentionally) ambiguous and vague.

Ambiguity is actually an asset in certain cases. For example, during early design you might not be ready to commit to a specific representation for the handles. You might not be sure whether you'll want simple pointers vs. references vs. pointers-to-pointers vs. references-to-pointers vs. integer indices into an array vs. strings (or other key) that can be looked up in a hash-table (or other data structure) vs. database keys vs. some other technique. If you merely know that you'll need some sort of thingy that will uniquely identify and get to an object, you call the thingy a Handle.

So if your ultimate goal is to enable a glop of code to uniquely identify/look-up a specific object of some class Fred, you need to pass a Fred handle into that glop of code. The handle might be a string that can be used as a key in some well-known lookup table (e.g., a key in a std::map<std::string,Fred> or a std::map<std::string,Fred*>), or it might be an integer that would be an index into some well-known array (e.g., Fred* array = new Fred[maxNumFreds]), or it might be a simple Fred*, or it might be something else.

Novices often think in terms of pointers, but in reality there are downside risks to using raw pointers. E.g., what if the Fred object needs to move? How do we know when it's safe to delete the Fred objects? What if the Fred object needs to (temporarily) get serialized on disk? etc., etc. Most of the time we add more layers of indirection to manage situations like these. For example, the handles might be Fred**, where the pointed-to Fred* pointers are guaranteed to never move but when the Fred objects need to move, you just update the pointed-to Fred* pointers. Or you make the handle an integer then have the Fred objects (or pointers to the Fred objects) looked up in a table/array/whatever. Or whatever.

The point is that we use the word Handle when we don't yet know the details of what we're going to do.

Another time we use the word Handle is when we want to be vague about what we've already done (sometimes the term magic cookie is used for this as well, as in, "The software passes around a magic cookie that is used to uniquely identify and locate the appropriate Fred object"). The reason we (sometimes) want to be vague about what we've already done is to minimize the ripple effect if/when the specific details/representation of the handle change. E.g., if/when someone changes the handle from a string that is used in a lookup table to an integer that is looked up in an array, we don't want to go and update a zillion lines of code.

To further ease maintenance if/when the details/representation of a handle changes (or to generally make the code easier to read/write), we often encapsulate the handle in a class. This class often overloads operators operator-> and operator* (since the handle acts like a pointer, it might as well look like a pointer).

"

Share this post


Link to post
Share on other sites
Using handles means there isn't any data associated with the object wheras a pointer points to real data. If someone has the declaration of the type being pointed to they can know the layout of the data. This makes it relatively easy for a programmer to confidently access things they shouldn't have knowledge of. Also the idea of encapsulation in object oriented design means you wouldn't change the state of an object except through its interface functions.

Using a handle could mean that you hand an object of some token type to a user. Then you have a map which lets you look-up the proper object.

For example:

This is a complex number class which you can use to create complex numbers from either real and imaginary values or polar coordinates. It stores both representations internally and recalculates them when necessary. So it is lazy and is premature optimisation but it was a simple example for you to be able to grasp the points here, not necessarily realistic.

class Complex {
double real_,
imaginary_;
double radius_,
angle_;
bool needsRecalculating_;
public:
Complex();
static Complex CreatePolar(double radius, double angle);
static Complex CreateCartesian(double real, double imaginary);
Complex(const Complex& other);
double real() const;
double imaginary() const;
double radius() const;
double angle() const;
//etc
};



Okay, the internal data needs to not be touched by outside people as it knows how to keep all the values up to date but someone tinkering might not. It would be easy to define a new class, say Complex_internal which gets at the data easily:


class Complex_internal {
double real_,
imaginary_;
double radius_,
angle_;
bool needsRecalculating_;
};

Complex c = Complex::CreatePolar(5, 90);
Complex_internal& ci = reinterpret_cast<Complex_internal&>(c);
ci.real_ = 15;
ci.needsRecalculating_ = false;//oooh naughty!



Anyway, what can you do to deal with this? There are a couple of ways to go. One is to not show any data at all and to have an abstract interface. This would be overkill in many situations because it would mean even the most basic class needs to be derived and have virtual functions.

Another way is to have an implementation class which has all the data and functions which do the proper work in it. This is quite popular and has got a name - The Pimpl Idiom. See GotW.
e.g.

class ComplexImpl;//forward declaration.
class Complex {
ComplexImpl* pImpl_;//secret stuff hidden
public:
Complex();
static Complex CreatePolar(double radius, double angle);
static Complex CreateCartesian(double real, double imaginary);
Complex(const Complex& other);
Complex& add(const Complex& other);
double real() const;
double imaginary() const;
double radius() const;
double angle() const;
//etc
};



So the secret stuff is hidden away. Functions will forward on to ComplexImpl which will do all the housekeeping and make sure the data is in a good state.
Now, users of the class have less to go on but will still be able to work out the memory address where the internal data is stored. By trial and error they could work out the internal workings of the class. On the whole people aren't going to go to these lengths and this is a sufficient solution for most uses. I would call this an opaque pointer. A handle can go one stage further.


struct HComplex { //Handle on a Complex type
int id;
};

HComplex CreatePolar(double radius, double angle);
HComplex CreateCartesian(double real, double imaginary);
void Destroy(HComplex complex);
double Real(HComplex complex);
double Imaginary(HComplex complex);
double Radius(HComplex complex);
double Angle(HComplex complex);



If this was given to your customers they would have a harder time finding out where data was stored or what its structure was.

The program basically will give them a Handle which will map into the real object so Imaginary might be implemented like this:


double Imaginary(HComplex complex) {
//'handles' is a map of type std::map<HComplex, Complex*>
Complex* theobject = handles[complex];
return theObject->imaginary();
}



This gives you the next layer of 'security'. There are always going to be ways for malicious coders to get around things. A lot of the 'security' features of C++ are there to prevent programmer error rather than providing real protection.

[Edited by - petewood on May 25, 2005 7:07:53 AM]

Share this post


Link to post
Share on other sites
In C, handles are great for hiding implementation details. It's a useful way to support some of the OO paradigm since the language is lacking:


// pointer based handle
#define MY_DECLARE_HANDLE(x) typedef struct x##__{int i;}*x
#define MY_INVALID_HANDLE 0

MY_DECLARE_HANDLE(hlogger);

typedef enum
{
LOG_TEXTFILE,
LOG_CONSOLE
} eLogger_t;

extern hlogger Logger_Create(eLogger_t type, const char *name);
extern void Logger_Write(hlogger hlog, const char *fmt, ...);
extern void Logger_Close(hlogger **hlog);




Using this sort of system, you could create all sorts of loggers behind the scenes, and the client never need know what sort of logger object he is working with once it is created, nor does he have direct access to the data fields of the logger object as the declaration is hidden away in the source modules. Of course, in a pointer based handle system it is still possible to break things if you know how a particular object is declared, but if that's a concern then table based handles can be used.

In languges that already have features which support the OO paradigm, such as C++, handles can be useful for other cases. There is an article by Scott Bilas in Game Programming Gems 1 (which is also available on his website describing a handle based resource system in C++. I've never read in in depth nor had need to use it, but you may find it useful.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!