Handles and pointers

S

Simon Elliott

I have a data management class which uses a handle:

typedef void* barHandle;

class foo
{
private:
class fooImpl;
fooImpl* pimpl_;
public:
foo(void);
virtual ~foo(void);
barHandle FindBar(const std::string& key);
std::string getvaluefrombar(barHandle handle, const std::string& key);
};

This would be used as follows:

std::string my_value;
barHandle my_handle = my_foo.Findbar("some_bar");
if (my_handle)
{
my_value = my_foo.getvaluefrombar(my_handle, "some_value");
}

I want the implementation to be opaque to the user; it could be using
XML, an SQL database or whatever. Currently it's using XML, and the
implementation has code along these lines:

barHandle foo::fooImpl::FindBar(const std::string& key)
{
TsomeXMLcontainer* elementBar FindBar(elementMain,key);
return(elementBar); // casts TsomeXMLcontainer* to void*
}

std::string foo::fooImpl::getvaluefrombar
(barHandle handle, const std::string& key)
{
TsomeXMLcontainer* elementBar =
static_cast<TsomeXMLcontainer*>(barHandle);
elementBar->GoAndFindValue(key);
...

Fine as far as it goes, but not very type safe. Any recommendations for
improving this by replacing the void* with something else?
 
H

Howard

Simon Elliott said:
I have a data management class which uses a handle:

typedef void* barHandle;

This is just a pointer. I'm not sure if there's any "standard" for the term
"handle", but in the code I work on, a handle is a pointer to a pointer.
(This is true both in Windows and Mac, so at least it's common, if not
"standard".)
class foo
{
private:
class fooImpl;
fooImpl* pimpl_;
public:
foo(void);
virtual ~foo(void);
barHandle FindBar(const std::string& key);
std::string getvaluefrombar(barHandle handle, const std::string& key);
};

This would be used as follows:

std::string my_value;
barHandle my_handle = my_foo.Findbar("some_bar");
if (my_handle)
{
my_value = my_foo.getvaluefrombar(my_handle, "some_value");
}

I want the implementation to be opaque to the user; it could be using
XML, an SQL database or whatever. Currently it's using XML, and the
implementation has code along these lines:

barHandle foo::fooImpl::FindBar(const std::string& key)
{
TsomeXMLcontainer* elementBar FindBar(elementMain,key);
return(elementBar); // casts TsomeXMLcontainer* to void*
}

std::string foo::fooImpl::getvaluefrombar
(barHandle handle, const std::string& key)
{
TsomeXMLcontainer* elementBar =
static_cast<TsomeXMLcontainer*>(barHandle);
elementBar->GoAndFindValue(key);
...

Fine as far as it goes, but not very type safe. Any recommendations for
improving this by replacing the void* with something else?

I don't see why you need to cast to anything. Why not just use a
TsomeXMLcontainer* pointer wherever you need it? Casting to void* is, as
you've said, not type safe. Hiding implementation details doesn't
neccessarily mean hiding data types. If the name's too combersome, you can
always use a typedef for that pointer (or even a #define, for simple
symbolic replacement). Casting back and forth to void* is just a waste of
time, and dangerous. Unless you have some third-party API/SDK that requires
a void* parameter somewhere, don't bother. Just use the pointer type you
need. (Even in the case where you *do* need a void*, it's best to cast it
to that at the point of use, not carry around a void* that can get abused in
other code.) Just my 2 cents worth...

-Howard
 
S

Simon Elliott

I don't see why you need to cast to anything. Why not just use a
TsomeXMLcontainer* pointer wherever you need it? Casting to void*
is, as you've said, not type safe. Hiding implementation details
doesn't neccessarily mean hiding data types.

In this case the data type exposes the underlying implementation too
much. One day the users might want to replace the XML based
implementation with something else, perhaps an SQL database. They would
then need to change their source code, replacing every instance of
TsomeXMLcontainer* with TsomeSQLresource*.
 
H

Howard

Simon Elliott said:
In this case the data type exposes the underlying implementation too
much. One day the users might want to replace the XML based
implementation with something else, perhaps an SQL database. They would
then need to change their source code, replacing every instance of
TsomeXMLcontainer* with TsomeSQLresource*.

That's where using a typedef or #define can help. Define a new symbol in
the header for the class that implements those functions, and when you
change the underlying functionality, you can change the definition to match.

A better approach might be to use another class in between, which hides the
passing of this pointer around entirely. If the user has no need to know
what the actual variable type is, then they probably also don't actually
need to *touch* that variable. (After all, you can't do anything useful
with a variable if you don't know what it is, right? :)) Instead of
returning a pointer from a function and then passing that pointer to another
function, just call one (or two) functions in this new class to do the work
for you, hiding the pointer entirely within that wrapper class. That's much
more in the OO-style of doing things, I think.

-Howard
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top