2

I want to write a list of strings (ATL::CString) stored in a std::vector to a REG_MULTI_SZ value in the Windows registry. I know how to do this in plain C (iterate once to get the total length, allocate a buffer, copy the strings to the buffer separated by "\0").

Know I tried the following using STL (sorry that I have to use VS2010 with "for each"):

std::vector<TCHAR> multiline_sz;
for each ( CString entry in myStringList )
{
   TCHAR* buf = entry.GetBuffer();
   multiline_sz.insert(multiline_sz.end(), &buf[0], &buf[entry.GetLength()]);
   multiline_sz.push_back(L'\0');
}
multiline_sz.push_back(L'\0');

This works, but I wonder if there is a more elegant or faster way using STL.

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
majo
  • 48
  • 3
  • Near-duplicate of http://stackoverflow.com/questions/9277906/stdvector-to-string-with-custom-delimiter . The two real differences are the chosen delimiter (you want a `\0` rather than a comma), and tacking on the extra nul at the end. – cHao Apr 18 '13 at 06:01
  • Looks reasonable (given that the loop header is pseudocode). *"more elegant"*? - Probably not, since `CString` doesn't have an iterator interface. *"faster"*? - Maybe you could sum up the sizes (including the `0`s in between) beforehand and do an appropriate `multiline_sz.reserve()`, though I won't give any guarantee for a significant performance improvement (I guess/hope you won't spam the registry with entire books anyway). – Christian Rau Apr 18 '13 at 09:23

2 Answers2

2

I'd suggest using CString::GetString() (instead of CString::GetBuffer()), as it returns a pointer to the NUL-terminated string for read-only access.

Moreover, note also that CString::GetBuffer() requires a matching CString::ReleaseBuffer() call.

I'd also simplify your code, without the unnecessary &buf[0], &buf[<index>] syntax.

// Your pseudo code: 
// for each ( CString entry in myStringList )
{
  multiline_sz.insert(
      multiline_sz.end(), 
      entry.GetString(), // <-- current string's start
      entry.GetString() + entry.GetLength() + 1 // <-- include terminating NUL
  );
}

As a further optimization, you may scan the whole input string list, and pre-compute the total required length, and call std::vector::reserve() to pre-allocate room in the vector, before the vector::insert() calls. That would save you some reallocations and copies during the insertion process.


P.S. If you want to iterate through a std::vector<CString> with C++11 range-for loops, you can use this syntax:

for (const auto& entry : myStringList) 
Mr.C64
  • 41,637
  • 14
  • 86
  • 162
1

CString::GetBuffer() is already zero-terminated, so it's valid to do

for each ( CString entry in myStringList )
{
   TCHAR const* buf = entry.GetBuffer();
   multiline_sz.insert(multiline_sz.end(), &buf[0], &buf[entry.GetLength()+1]);
}
MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Thanks. And I promise to use "for(Cstring: entry in myStringList)" as soon as I am allowed to switch to VS2012 :). – majo Apr 18 '13 at 12:45
  • @majo Won't work either. It's `for(auto &&entry : myStringList)`, or `for(auto iter=myStringList.begin(); iter!=myStringList.end(); ++iter)` if you're not allowed to switch. – Christian Rau Apr 18 '13 at 15:02
  • @ChristianRau: `for (const auto& entry : myStringList)` would work just fine for compilers implementing C++11 range-for loops. BTW I think this answer from 2013 is wrong since it uses `CString::GetBuffer()` instead of **`CString::GetString()`**. Moreover, the `&buf[index]` syntax is superfluous in this case and can be simplified. I've posted what I think is a better answer a few days ago. – Mr.C64 Oct 10 '16 at 14:52