Topic: Passing vector data to/from C-style functions
Author: "Jim Barry" <jim.barry@bigfoot.com>
Date: 1998/05/20 Raw View
Jim Barry wrote in message <6jkd79$q5l@netlab.cs.rpi.edu>...
>template<class T> bool IsPointer(T x) { return false; }
>template<class T> bool IsPointer(T* x) { return true; }
>template<class T> bool IsContiguous(const T& x)
>{ return IsPointer(x.begin()); }
>
>error C2667: 'IsPointer' : none of 2 overload have a best conversion
>error C2668: 'IsPointer' : ambiguous call to overloaded function
I found a way to make this work. If I change the first version to take
a reference, then everything works as intended:
template<class T> bool IsPointer(const T& x) { return false; }
template<class T> bool IsPointer(const T* x) { return true; }
If anyone can explain what is going on here, please jump in.
So now I can modify my function templates, thus:
template<class FwdIt>
MutableBuffer<FwdIt> MakeMutableBuffer(FwdIt& first, FwdIt& last)
{return MutableBuffer<FwdIt>(first, last);}
template<class InIt>
ConstBuffer<InIt> MakeConstBuffer(InIt& first, InIt& last)
{return ConstBuffer<InIt>(first, last);}
template<class P>
P* MakeMutableBuffer(P* first, P* last)
{return first;}
template<class P>
const P* MakeConstBuffer(const P* first, const P* last)
{return first;}
And everything appears to work perfectly.
There is a problem with preventing usage like this:
vector<int> v(10);
int* p = MakeMutableBuffer(v.begin(), v.end()); // Bad move
p[0] = 5; // *Boom*! Temporary buffer has been deleted!
The only thing I can think of is to rename the functions to
PassConstBuffer and PassMutableBuffer, to make it clearer that they
should only be used in arguments to other functions.
- Jim
--
Jim Barry, Thermoteknix Systems Ltd., Cambridge, UK.
http://www.thermoteknix.co.uk Queen's Award for Export 1998
Antispam address courtesy of http://www.bigfoot.com
Or e-mail direct: jim at thermoteknix dot co dot uk
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Jim Barry" <jim.barry@bigfoot.com>
Date: 1998/05/16 Raw View
This is a follow-up to those enormously long threads about whether
vector should allocate contiguously or not.
Sysnopsis: Although mainstream STL implementations (SGI, HP) implement
vector with a representation compatible with built-in arrays
(contiguous), the standard does not require this, as vector::iterator
is implementation-defined. Therefore, it is not portable to pass
vector data directly to functions which take a pointer and element
count.
A workaround based on function template overloading was suggested by
Andrew Koenig. The idea is to test at runtime whether the vector's
iterators are pointers by writing a template function and specialising
it for pointers. If the specialised version gets called then the
iterator is a pointer, therefore the underlying storage must be
contiguous. For example, a function to test whether any particular
vector (or indeed any container) has raw pointers for iterators would
go something like this:
template<class T> bool IsPointer(T x) { return false; }
template<class T> bool IsPointer(T* x) { return true; }
template<class T> bool IsContiguous(const T& x)
{ return IsPointer(x.begin()); }
// Test whether vector of int is contiguous
if (IsContiguous(vector<int>()) ...
Unfortunately, on my compiler (VC5) this gives:
error C2667: 'IsPointer' : none of 2 overload have a best conversion
error C2668: 'IsPointer' : ambiguous call to overloaded function
After having a look at section 14.5.5.2 of CD2, I have to conclude
that VC5's function template overloading is completely clueless. If I
have got that wrong, somebody please set me straight, but I suspect
that it is related to the fact that VC5 does not support partial
specialisation for class templates.
Given that this approach will not work on VC5, I'll explain how my
solution would work on a proper compiler. I define buffer classes
ConstBuffer and MutableBuffer, objects of which are intended to exist
only as temporaries. These buffer objects copy a sequence of elements
to a dynamically allocated array which exists for as long as the
buffer object. The buffer classes overload the implicit cast operator
to return a pointer to the array. In the case of MutableBuffer, the
array data is copied back to the sequence when the buffer object is
destroyed.
The workaround is orchstrated by two template functions,
MakeConstBuffer and MakeMutableBuffer. In the case where the sequence
iterators are non-pointers, the functions return a temporary buffer
object which holds a contiguous copy of the sequence. In the case
where the iterators are pointers, then the sequence must already be
contiguous, so the functions simply return a pointer to the the first
element of the sequence.
Interestingly, this approach means that the sequence need not be a
vector, but could be any container. Data can be prepared using e.g. a
list, and sent to a function that expects a pointer and element count
(at the expense of copying the sequence, of course). Whether or not
this is actually of any value is another matter. If the sequence
happens to be a contiguously allocating vector, then there is no
copying overhead.
A contrived example of usage (in Win32 parlance) might go something
like this:
int len = GetWindowTextLength(hwnd) + 1;
vector<char> v(len);
GetWindowText(hwnd, MakeMutableBuffer(v.begin(), v.end()), len);
string s = MakeConstBuffer(v.begin(), v.end());
OK, here is the code:
template<class InIt>
class ConstBuffer {
public:
typedef ConstBuffer<InIt> self;
typedef InIt::value_type value_type;
typedef InIt::value_type* pointer;
ConstBuffer(InIt it1, InIt it2)
: first(it1), last(it2)
{
ptr = new value_type[distance(first, last)];
copy(first, last, ptr);
}
~ConstBuffer() { delete[] ptr; }
operator const pointer() { return ptr; }
ConstBuffer(const self& x); // undefined
private:
self& operator=(const self&); // no assignment
InIt first, last;
pointer ptr;
};
template<class FwdIt>
class MutableBuffer {
public:
typedef MutableBuffer<FwdIt> self;
typedef FwdIt::value_type value_type;
typedef FwdIt::value_type* pointer;
MutableBuffer(FwdIt it1, FwdIt it2)
: first(it1), last(it2)
{
ptr = new value_type[distance(first, last)];
copy(first, last, ptr);
}
~MutableBuffer()
if (ptr) copy(ptr, ptr + distance(first, last), first);
delete[] ptr; }
operator pointer() { return ptr; }
MutableBuffer(const self& x); // undefined
private:
self& operator=(const self&);
FwdIt first, last;
pointer ptr;
};
template<class FwdIt>
MutableBuffer<FwdIt> MakeMutableBuffer(FwdIt first, FwdIt last)
{return MutableBuffer<FwdIt>(first, last);}
template<class InIt>
ConstBuffer<InIt> MakeConstBuffer(InIt first, InIt last)
{return ConstBuffer<InIt>(first, last);}
template<class P>
P* MakeMutableBuffer(P* first, P* last)
{return first;}
template<class P>
const P* MakeConstBuffer(const P* first, const P* last)
{return first;}
I just wish it would compile on VC5! I would prefer it if the common
code was factored out of ConstBuffer/MutableBuffer but I think I need
a compiler that supports specialisation properly before I can try
that.
Discuss!
- Jim
--
Jim Barry, Thermoteknix Systems Ltd., Cambridge, UK.
http://www.thermoteknix.co.uk Queen's Award for Export 1998
Antispam address courtesy of http://www.bigfoot.com
Or e-mail direct: jim at thermoteknix dot co dot uk
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]