Re: A trick for dealing with functions that take a pointer and return a result via it
Jon Colverson wrote:
void GiveMeANumber(int *p) { *p = 42; }
[...]
long a;
{
int b;
GiveMeANumber(&b);
a = b;
}
This is fine, but you might get sick of it you had to do it often.
In such legacy functions, the return value is often reserved for error
indicator, so the right technique is to wrap it with another function:
int wrapped::GiveMeANumber()
{
int b;
bool ok = GiveMeANumber(&b);
if (!ok) throw something_bad();
return ok;
}
But well, you may need an automated tool to write those wrappers, such
as VC #import directive.
[...]
The class works by providing automatic conversion to the required
pointer type,
You are talking about "type conversion", so we can call it "type_cast".
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.
[...]
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.
Yet another pointer type is the case when your template should be
explicitly specialized.
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.
It will be better to separate reference conversion from pointer
conversion, as it has been done with standard C++ conversions
(static_cast, const_cast, etc.), so the following should be possible:
long a;
GiveMeANumber(referer_cast<int*>(&a)); // pointer cast
GiveMeANumber(&referer_cast<int&>(a)); // reference cast
I call it "referer_cast" to illustrate that we are casting something
that refers to something valuable, i.e. "referer", which can be
either reference, or pointer, or smart 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.
That's not a good idea. Initialization is the user responsibility. You
loose performance, but you should care about potential side effects
here: some compilers have runtime checks for the use of uninitialized
variables, and you disable their logic by substituting arbitrary value.
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.
This case is optimized by most compilers, the copy constructor is not
called, but you cannot prohibit it due to logical rules described in
C++ standard 12.2.
[...]
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.
Consider the following implementation that illustrates my comments.
I use three specializations: for pointers, for references, and for
std::auto_ptr, which may fit for boost::shared_ptr too. And there are
two helper functions for end user, and their usage example.
#include <iostream>
#include <memory>
using namespace std;
template <class Result, class Variable> class referer_cast_t;
template <class Result, class Variable>
class referer_cast_t<Result*,Variable*> {
public:
referer_cast_t(Variable* ptr) : ptr(ptr) { }
~referer_cast_t() { *ptr = result; }
operator Result*() { return &result; }
private:
Variable* ptr;
Result result;
};
template <class Result, class Variable>
class referer_cast_t<Result&,Variable&> {
public:
referer_cast_t(Variable& ref) : ref(ref) { }
~referer_cast_t() { ref = result; }
operator Result&() { return result; }
Result* operator&() { return &result; }
private:
Variable& ref;
Result result;
};
template <class Result, class Variable>
class referer_cast_t<Result**, auto_ptr<Variable>* > {
public:
referer_cast_t(auto_ptr<Variable>* pptr) : pptr(pptr) { }
~referer_cast_t() { pptr->reset(result); }
operator Result**() { return &result; }
private:
auto_ptr<Variable>* pptr;
Result* result;
};
template<class Result, class Variable>
referer_cast_t<Result,Variable*> referer_cast(Variable* var)
{
return referer_cast_t<Result,Variable*>(var);
}
template<class Result, class Variable>
referer_cast_t<Result,Variable&> referer_cast(Variable& var)
{
return referer_cast_t<Result,Variable&>(var);
}
void GiveMeANumber(int value, int *p) { *p = value; }
void GiveMeAPointer(int value, int** pp) { *pp = new int(value); }
int main()
{
long a;
GiveMeANumber(42, referer_cast<int*>(&a));
cout << a << endl;
GiveMeANumber(43, &referer_cast<int&>(a));
cout << a << endl;
auto_ptr<int> p;
GiveMeAPointer(44, referer_cast<int**>(&p));
cout << *p << endl;
return 0;
}
--
Andrei Polushin
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]