Re: A trick for dealing with functions that take a pointer and return a result via it
Jon Colverson wrote:
I came up with a little trick which I think is rather nice so I thought
I'd show it off (and subject it to merciless criticism) here:
It concerns functions that take a pointer and use it to return a result.
My class is for when you don't have an object of the appropriate type
for the parameter, but you have one which is assignment compatible. For
example, say you want to use the function:
void GiveMeANumber(int *p) { *p = 42; }
but you want to put the result into a long instead of an int. You can't
just say:
long a;
GiveMeANumber(&a); // wrong, can't convert a long * to an int *
but since you can assign an int to a long you can just use a short-lived
int:
long a;
{
int b;
GiveMeANumber(&b);
a = b;
}
This is fine, but you might get sick of it you had to do it often. My
class simply encapsulates the idiom above allowing you to write:
long a;
GiveMeANumber(AssignResultTo<long, int>(a));
The class works by providing automatic conversion to the required
pointer type, in the form of a pointer to a member of the AssignResultTo
object. Then on destruction it assigns this member to the destination.
See the code below for the details.
Another example where this situation comes up is where a function wants
to give you a pointer (so it takes a pointer to a pointer as an
argument), but you have a smart pointer instead. This is the case that
prompted me to come up with AssignResultTo. I was using DirectX (which
uses COM) and Loki SmartPtrs:
SmartPtr<IDirect3D9, COMRefCounted> d3d(
Direct3DCreate9(D3D_SDK_VERSION));
SmartPtr<IDirect3DDevice9, COMRefCounted> device;
d3d->CreateDevice(
/* ... */
AssignResultTo<SmartPtr<IDirect3DDevice9, COMRefCounted>,
IDirect3DDevice9 *>(device));
Here's the code:
template <class DestinationType, class ResultType>
class AssignResultTo {
public:
AssignResultTo(
DestinationType &d, const ResultType &r = ResultType())
: destination(&d), result(r) {}
operator ResultType*() { return &result; }
operator ResultType&() { return result; }
~AssignResultTo() { *destination = result; }
private:
DestinationType *destination;
ResultType result;
// Disable copying
AssignResultTo(const AssignResultTo &);
AssignResultTo &operator=(const AssignResultTo &);
};
Some notes:
boost::shared_ptr doesn't work with this class because it doesn't
support assignment from the equivalent raw pointer type. This case can
be dealt with by another class (I called it ResetWithResult) which works
the same way but calls reset(T*) on the destination instead of doing an
assignment.
As you can see, the class also provides a conversion to a reference for
use with functions that take one of those instead of a pointer. There's
also a second parameter for the constructor which provides a default
value which will be assigned if the result isn't modified by the
function.
Pleasingly, it seems that everything except the crucial assignment can
be optimised away by compilers so there's no run-time penalty.
It's a shame that the DestinationType can't be deduced from the
constructor argument as that would make the syntax more concise. I tried
to achieve this by making a helper function that just returned an
instance by value but that required making a (destructive) copy
constructor and that made things much harder for the optimisers and led
to a significant performance penalty.
You can't have both a helper function and disallow copy construction,
but that's the result of doing something non-trivial in the
destructor--unless you kept a boolean indicating that assignment
shouldn't occur in the destructor--which loses the lightweight problem.
I also experimented by making a version that leaves the result member
uninitialised (by removing the default value from the second constructor
parameter and adding another constructor with just the first parameter).
I thought that this might allow more optimisation in the common case
where it is known that the value will be overwritten, but I was unable
to come up with a test case that would test this theory adequately.
Since it's presumably only a single CPU instruction that would be
avoided, I decided it wasn't worth worrying about.
Thanks for reading all the way to the end. I'm interested in any and all
comments. I doubt that I'm the only person who has invented this, so has
anyone seen something similar described elsewhere? I'd like to compare
implementations. Also, I'd love suggestions for a better name for the
class.
It's an interesting idea. I'm not sure that it can work in the general
case.
This is something of a proxy object, and they're one of the best cases
for implementing a conversion operator (I think of the conversion
operator as saying something close to "isa", like "is very much like
a"). But it's dangerous to implement one for ResultType and ResultType
* because it violates the principle of say what you mean/mean what you
say. It allows the caller to accidentally say pointer when value is
intended. It would be better to have pointer and non-pointer versions.
Another concern is that the destructor calls a function (assignment
operator) that is not guaranteed to be exception safe (for non-POD
types), allowing that it could throw an exception. You could guard
against this by using a BOOST_STATIC_ASSERT on
type_traits::is_pod<DestinationType>.
I'd be interest in a variant that can deduce the source type and has
cast-like syntax. The performance impact might be negligible because
you're crossing an API boundary--which often indicates significant
computation.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]