I need to use "std::vector<unsigned char> buf" as a buffer
that can be passed to some C functions as "f(&buf[0], buf.size())".
When building test, I tried to initialize that buffer manually
from a string literal or from std::string. I did finally use something
like this with strings:
typedef std::vector<unsigned char> vec;
string penguin_zero_penguin("penguin\0penguin");
CPPUNIT_ASSERT(penguin_zero_penguin.length() == 15);
vec vpenguin_zero_penguin(penguin_zero_penguin.length());
strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
penguin_zero_penguin.c_str(), penguin_zero_penguin.length());
or similarly with literals:
vec vpenguin_zero_penguin(15);
strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
"penguin\0penguin", 15);
I am not very good in standard C++ library. I believe that there must be
some
nicer way to do the same with comparable performance.
Thanks for your help and experience,
Petr
--
Petr Prikryl (prikrylp at skil dot cz)
You're supposed to be able to do it using iterators. Are you using VC6
or VC7(.1)? If the former, you'll likely run into trouble with missing
member templates. You may be able to edit the <vector> header to
restore them. It's been my experience that there are no problems
restoring the template member functions, but the template constructors
don't work.
Then you can do something like,
buf.insert(penguin_zero_penguin.begin(),
penguin_zero_penguin.end());
Alternatively, there's std::copy:
buf.reserve(penguin_zero_penguin.length());
std::copy(penguin_zero_penguin.begin(), penguin_zero_penguin.end(),
std::back_inserter(buf));
As long as you reserve first, so that you only allocate once, this
method should do well.
> When building test, I tried to initialize that buffer manually
> from a string literal or from std::string. I did finally use something
> like this with strings:
>
> typedef std::vector<unsigned char> vec;
>
> string penguin_zero_penguin("penguin\0penguin");
> CPPUNIT_ASSERT(penguin_zero_penguin.length() == 15);
>
> vec vpenguin_zero_penguin(penguin_zero_penguin.length());
> strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
> penguin_zero_penguin.c_str(), penguin_zero_penguin.length());
Then again, I don't like the unsigned char vs. char thing here, but
otherwise this isn't so bad.
--
Craig Powers
MVP - Visual C++
This won't work. You are using std::string constructor that takes
NULL-terminated C string. It will stop at the first '\0'.
> CPPUNIT_ASSERT(penguin_zero_penguin.length() == 15);
This should assert, penguin_zero_penguin.length() should be 7.
> vec vpenguin_zero_penguin(penguin_zero_penguin.length());
> strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
> penguin_zero_penguin.c_str(),
penguin_zero_penguin.length());
strncpy copies up to the first NULL character or to the provided length,
whichever comes first. Thus it too will stop after first "penguin".
> or similarly with literals:
>
> vec vpenguin_zero_penguin(15);
> strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
> "penguin\0penguin", 15);
Same problem with strncpy.
This should work:
char penguin[] = "penguin\0penguin";
const int len = sizeof(penguin) - 1; // sizeof counts terminating NULL.
vec vpenguin(len);
std::copy((unsigned char*)penguin, (unsigned char*)(penguin + len),
vpenguin.begin());
// or
memcpy(&vpenguin[0], penguin, len);
--
With best wishes,
Igor Tandetnik
"For every complex problem, there is a solution that is simple, neat,
and wrong." H.L. Mencken
> char penguin[] = "penguin\0penguin";
> const int len = sizeof(penguin) - 1; // sizeof counts terminating NULL.
>
By the way if you wan tto init the string:
string penguin_zero_penguin(penguin, len);
Yes. I did not pay attention here. I was composing the content
in several steps in the original code like (pseudo):
string penguin("penguin");
string zero(1, '\0');
string penguin_zero_penguin(penguin + zero + penguin);
> > vec vpenguin_zero_penguin(penguin_zero_penguin.length());
> > strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
> > penguin_zero_penguin.c_str(),
> > penguin_zero_penguin.length());
>
> strncpy copies up to the first NULL character or to the provided length,
> whichever comes first. Thus it too will stop after first "penguin".
Oh yes. Thanks many times. It would be a bug in my source. (Being spoiled
by std::string, I did forgot the details of the hard-core C functions ;)
> [...] This should work:
> char penguin[] = "penguin\0penguin";
> const int len = sizeof(penguin) - 1; // sizeof counts terminating NULL.
> vec vpenguin(len);
> std::copy((unsigned char*)penguin, (unsigned char*)(penguin + len),
> vpenguin.begin());
> // or
> memcpy(&vpenguin[0], penguin, len);
Thanks for that code. I have finally also found the std::copy() algorithm.
But I had some problem in cases when I did not know exactly the size
of the output vector. Because of that I did use:
vec v;
string s;
[... some code here ...]
s = ...filled somehow...
v.reserve(my_size); // knowing that the block will be big and aproximately
of that size.
copy(s.begin(), s.end(), back_inserter(v));
The key difference here was to use the back_inserter() to adjust the size
according
to the copied content.
Question: Is the space reserved for a vector ever shortened? I mean, when
I do v.clear() or whatever, can the v.capacity() be lowered during the
lifetime
of the vector? Does reserve() with smaller argument free the "not needed"
space?
Thanks for your valuable comments,
Thanks. I did not use that version of the constructor (with non-default
len).
So, the possibility to use it did not came to my mind until reading your
post
and checking the documentation.
Petr
"Craig Powers" wrote:
> Petr Prikryl wrote:
> > I need to use "std::vector<unsigned char> buf" as a buffer
> > that can be passed to some C functions as "f(&buf[0], buf.size())".
>
> You're supposed to be able to do it using iterators. [...]
> Then you can do something like,
> buf.insert(penguin_zero_penguin.begin(),
> penguin_zero_penguin.end());
Thanks, the insert() method is new (for me) alternative to std::copy()
with back_inserter() that I finally used and that you also mentioned in your
post.
> > vec vpenguin_zero_penguin(penguin_zero_penguin.length());
> > strncpy(reinterpret_cast<char *>(&vpenguin_zero_penguin[0]),
> > penguin_zero_penguin.c_str(),
penguin_zero_penguin.length());
>
> Then again, I don't like the unsigned char vs. char thing here, but
> otherwise this isn't so bad.
Caught you! ;-) As Igor Tandetnik noticed, the strncpy stops at the first
zero character.
The reason for using vector<unsigned char > is that I am calling zlib
functions that expect unsigned char buffer[n]. This way I did avoid
the reinterpret_cast<> many times later.
Thanks again,
Petr
Never. But you can use the following "shrink-to-fit" idiom:
vec myVec; // overallocated
vec(myVec).swap(myVec);
This creates a temporary vector, copies the contents of the original one
into the temp, then swaps the contents of the two, so the temp holds
overallocated space and the original vector holds a freshly allocated
space. Finally, temporary vector is destroyed and frees overallocated
memory.
This relies on the fact that most STL implementations allocate just
enough memory to hold existing elements in this situation, or maybe
slighly larger.