Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

vector of string to array of c string

55 views
Skip to first unread message

Christopher Pisz

unread,
Oct 16, 2015, 11:50:16 AM10/16/15
to

I am trying to pass a vector of strings to my wrapper method, which
wraps some icky Windows API calls.

I cannot seem to get the conversion from the vector of strings to the
array of icky windows type, which after digging through defines is
really just const wchar_t *


Can someone help me out?

Here is the original code that worked:

LPCWSTR types[4];
types[0] = L"text/html";
types[1] = L"text/xml";
types[2] = L"application/json";
types[3] = 0;


HINTERNET requestHandle = WinHttpOpenRequest(m_connectHandle,
L"POST",
wideResource.c_str(),
NULL,
WINHTTP_NO_REFERER,
types,
m_secure?
WINHTTP_FLAG_SECURE : NULL);


Here is my attempt at getting the data in the form of a vector of strings:

// Ermehgerd Windows API casting to silly string types
std::vector<std::string> things{"text/html", "text/xml",
"application/json"};

LPCWSTR * typesArray = new LPCWSTR[things.size()+ 1];

for(size_t index = 0; index < things.size(); ++index)
{
std::wstring temp = Shared::MultibyteToWide(things[index].c_str());
typesArray[index] = new wchar_t[temp.size() + 1];
StringCchCopyW(const_cast<wchar_t *>(typesArray[index]),
temp.size() + 1, temp.c_str());
}

typesArray[things.size()] = 0;

HINTERNET requestHandle = WinHttpOpenRequest(m_connectHandle,
L"POST",
wideResource.c_str(),
NULL,
WINHTTP_NO_REFERER,
typesArray,
m_secure?
WINHTTP_FLAG_SECURE : NULL);


When I compile, I get no errors. However, as I debug through, I get the
first string into the array and on the second iteration through the for
loop it becomes empty for some reason.

If someone can help, but doesn't like looking at the Windows types, here
is a snippet what I am trying to do, translated to the best of my ability:

const wchar_t ** Convert(std::vector<string> & data)
{
// What goes here?
}







--
I have chosen to troll filter/ignore all subthreads containing the
words: "Rick C. Hodgins", "Flibble", and "Islam"
So, I won't be able to see or respond to any such messages
---

Christopher Pisz

unread,
Oct 16, 2015, 12:40:57 PM10/16/15
to
On 10/16/2015 10:49 AM, Christopher Pisz wrote:
>
> I am trying to pass a vector of strings to my wrapper method, which
> wraps some icky Windows API calls.
>
> I cannot seem to get the conversion from the vector of strings to the
> array of icky windows type, which after digging through defines is
> really just const wchar_t *
>
>
> Can someone help me out?
>
> Here is the original code that worked:
>
> LPCWSTR types[4];
> types[0] = L"text/html";
> types[1] = L"text/xml";
> types[2] = L"application/json";
> types[3] = 0;
>
>
> HINTERNET requestHandle = WinHttpOpenRequest(m_connectHandle,
> L"POST",
> wideResource.c_str(),
> NULL,
> WINHTTP_NO_REFERER,
> types,
> m_secure?
> WINHTTP_FLAG_SECURE : NULL);

Here is a more friendly compilable example, where I am just having some
problems where the comments are:

// Standard Library
#include <iostream>
#include <string>
#include <vector>

// Windows Includes
#include "Strsafe.h" // We can replace with C strcpy or some such

//--------------------------------------------------------------------------------------------------
char ** Convert(const std::vector<std::string> & data)
{
char ** out = new char *[data.size() + 1];

for(size_t index = 0; index < data.size(); ++index)
{
const std::string & element = data[index];
out[index] = new char[element.size() + 1];
StringCchCopy(out[index], element.size() + 1, element.c_str());
}

out[data.size()] = 0;

return out;
}

//--------------------------------------------------------------------------------------------------
std::vector<std::string> Convert(const char ** data)
{
std::vector<std::string> out;

// What kind of magic goes here?

return out;
}

//--------------------------------------------------------------------------------------------------
int main()
{
std::vector<std::string> data;
data.push_back("String1");
data.push_back("String2");
data.push_back("String3");

char ** yucky = Convert(data);

// Cleanup allocated memory
// How do we do such a thing? We have to iterate through somehow


return 0;
}



Alf P. Steinbach

unread,
Oct 16, 2015, 1:48:00 PM10/16/15
to
On 10/16/2015 5:49 PM, Christopher Pisz wrote:
>
[snip]
>
> Can someone help me out?
>
> Here is the original code that worked:
>
> LPCWSTR types[4];
> types[0] = L"text/html";
> types[1] = L"text/xml";
> types[2] = L"application/json";
> types[3] = 0;
>
>
[snip]
>
> Here is my attempt at getting the data in the form of a vector of strings:
>
> // Ermehgerd Windows API casting to silly string types
> std::vector<std::string> things{"text/html", "text/xml",
> "application/json"};
>
> LPCWSTR * typesArray = new LPCWSTR[things.size()+ 1];
>
> for(size_t index = 0; index < things.size(); ++index)
> {
> std::wstring temp =
> Shared::MultibyteToWide(things[index].c_str());
> typesArray[index] = new wchar_t[temp.size() + 1];
> StringCchCopyW(const_cast<wchar_t *>(typesArray[index]),
> temp.size() + 1, temp.c_str());
> }
>
> typesArray[things.size()] = 0;
>

Just create a vector of pointers.

vector< wchar_t const* > types( things.size() );
for( auto& p : types ) { p = things.c_str(); }

Then in the call to some C style function supply &types[0].

That's it, roughly.

You do need to be careful with zero size array and lifetimes and
modifying operations and such.

Cheers & hth.,

- Alf

Alf P. Steinbach

unread,
Oct 16, 2015, 1:49:43 PM10/16/15
to
On 10/16/2015 5:49 PM, Christopher Pisz wrote:
>
> // Ermehgerd Windows API casting to silly string types
> std::vector<std::string> things{"text/html", "text/xml",
> "application/json"};
>

Make that vector<wstring>, and use wide string literals.


mark

unread,
Oct 16, 2015, 2:02:45 PM10/16/15
to
On 2015-10-16 18:40, Christopher Pisz wrote:
> std::vector<std::string> Convert(const char ** data)
> {
> std::vector<std::string> out;
>
> // What kind of magic goes here?
>
> return out;
> }
>
> //--------------------------------------------------------------------------------------------------
>
> int main()
> {
> std::vector<std::string> data;
> data.push_back("String1");
> data.push_back("String2");
> data.push_back("String3");
>
> char ** yucky = Convert(data);
>
> // Cleanup allocated memory
> // How do we do such a thing? We have to iterate through somehow

Are you hung up on the pointer array syntax?

std::vector<std::string> ConvertAndCleanup(char** data)
{
std::vector<std::string> out;
for(char** elem_ptr = data; *elem_ptr != nullptr; ++elem_ptr) {
out.emplace_back(std::string(*elem_ptr));
delete[] *elem_ptr;
};
delete[] data;
return out;
}

You put a sentinel 0 / nullptr at the end of the char* array that you
can check against for the loop termination.

crisd...@gmail.com

unread,
Oct 16, 2015, 2:21:05 PM10/16/15
to
I think this basically does what you want:

<code>
#include <iostream>
#include <string>
#include <vector>

//--------------------------------------------------------------------------------------------------
std::vector<const char*> Convert(const std::vector<std::string> & data)
{
std::vector<const char *> out;

for(const std::string& s : data)
out.push_back(s.c_str());

return out;
}

void IckyFunction(const char ** in, size_t size)
{
for(size_t i = 0; i < size; ++i)
std::cout << in[i] << std::endl;

return;
}

//--------------------------------------------------------------------------------------------------
int main()
{
std::vector<std::string> data;
data.push_back("String1");
data.push_back("String2");
data.push_back("String3");

std::vector < const char * > out = Convert(data);

IckyFunction(out.data(), out.size());

// Cleanup allocated memory
// How do we do such a thing? We have to iterate through somehow


return 0;
}
</code>

Critiques more than welcome. I use very little C++ and am trying to dabble a little more ...

Christopher Pisz

unread,
Oct 16, 2015, 3:36:06 PM10/16/15
to
On 10/16/2015 12:47 PM, Alf P. Steinbach wrote:
> vector< wchar_t const* > types( things.size() );
> for( auto& p : types ) { p = things.c_str(); }


p = things.c_str();

Assigning std::string::c_str() to type char * does not work.. When the
string goes out of scope, so does its buffer, I believe.

The vector has to have each element point to allocated memory, or where
are we going to store the string?

Nor does variable 'things' have a c_str() method, it's a vector. So, the
for(auto...syntax can't be used, because you need an index, no?

Christopher Pisz

unread,
Oct 16, 2015, 3:44:04 PM10/16/15
to
> out.push_back(s.c_str());'


I don't think the above line is safe, because the string can have its
buffer changed or it can go out of scope.

crisd...@gmail.com

unread,
Oct 16, 2015, 5:15:18 PM10/16/15
to
On Friday, October 16, 2015 at 12:44:04 PM UTC-7, Christopher Pisz wrote:
> On 10/16/2015 1:20 PM, I wrote
The line itself is safe. You just have to be aware. The buffer won't change if you don't change the string. The same is true of _any_ use of c_str or iterators or data(), etc., etc. I believe that my usage in my example is perfectly safe.


Ian Collins

unread,
Oct 16, 2015, 5:28:27 PM10/16/15
to
crisd...@gmail.com wrote:
>
> I think this basically does what you want:

A couple of critiques as requested!

> <code>
> #include <iostream>
> #include <string>
> #include <vector>
>
> //--------------------------------------------------------------------------------------------------
> std::vector<const char*> Convert(const std::vector<std::string> & data)
> {
> std::vector<const char *> out;
>
> for(const std::string& s : data)
> out.push_back(s.c_str());

Could be written

for( const auto& s : data)
out.push_back(s.c_str());

>
> return out;
> }
>
> void IckyFunction(const char ** in, size_t size)
> {
> for(size_t i = 0; i < size; ++i)
> std::cout << in[i] << std::endl;
>
> return;
> }
>
> //--------------------------------------------------------------------------------------------------
> int main()
> {
> std::vector<std::string> data;
> data.push_back("String1");
> data.push_back("String2");
> data.push_back("String3");

Could be written

std::vector<std::string> data {"String1","String2","String3"};

> std::vector < const char * > out = Convert(data);

This one would definitely benefit from auto:

const auto out = Convert(data);

> IckyFunction(out.data(), out.size());
>
> // Cleanup allocated memory
> // How do we do such a thing? We have to iterate through somehow

I don't thank you have allocated any?

>
> return 0;
> }
> </code>
>
> Critiques more than welcome. I use very little C++ and am trying to dabble a little more ...
>


--
Ian Collins

Alf P. Steinbach

unread,
Oct 17, 2015, 12:30:26 AM10/17/15
to
On 10/16/2015 9:35 PM, Christopher Pisz wrote:
> On 10/16/2015 12:47 PM, Alf P. Steinbach wrote:
>> vector< wchar_t const* > types( things.size() );
>> for( auto& p : types ) { p = things.c_str(); }
>
>
> p = things.c_str();
>
> Assigning std::string::c_str() to type char * does not work.

ITYM, "assigning std::wstring::c_str() to type char* does not work". The
solution to that involves using the Unicode API functions, not the
1990's Windows ANSI functions. Define the macro symbol UNICODE before
including <windows.h>. Use wchar_t, not char. Remember to add const as
appropriate: assigning std::wstring::c_str() result to wchar_t* is not
valid, but wchar_t const* is OK.


> When the
> string goes out of scope, so does its buffer, I believe.

Yes. Don't let the vector with the strings go out of scope yet.

Quting myself on that, "You do need to be careful with zero size array
and lifetimes and modifying operations and such.".

I.e., there are also a few more such possible pitfalls.


> The vector has to have each element point to allocated memory, or where
> are we going to store the string?

Uh, I think this refers to some flawed understanding.

Maybe that has to do with failing to quote the relevant declarations, above.


> Nor does variable 'things' have a c_str() method, it's a vector. So, the
> for(auto...syntax can't be used, because you need an index, no?

It can be used, with some backing (e.g. à la Python's enumerate), but
since you don't have that already in your toolbox it's simpler to just
use an index-based loop. Sorry for just sketching the code.

mark

unread,
Oct 17, 2015, 3:28:55 AM10/17/15
to
On 2015-10-16 20:02, mark wrote:
> std::vector<std::string> ConvertAndCleanup(char** data)
> {
> std::vector<std::string> out;
> for(char** elem_ptr = data; *elem_ptr != nullptr; ++elem_ptr) {
> out.emplace_back(std::string(*elem_ptr));
> delete[] *elem_ptr;
> };
> delete[] data;
> return out;
> }

There is a performance bug in the code. The emplace_back call creates a
temporary string + copy.

std::vector<std::string> ConvertAndCleanup(char** data)
{
std::vector<std::string> out;
for(char** elem_ptr = data; *elem_ptr != nullptr; ++elem_ptr) {
out.emplace_back(*elem_ptr);
0 new messages