开发者

Mixing c++ standard strings and windows API

Many windows APIs take a pointer to a buffer and a size element but the result needs to go into a c++ string. (I'm using windows unicode here so they are wstrings)

Here is an example :-

#include <iostream>
#include <string>
#include <vector>
#include <windows.h>

using namespace std;

// This is the method I'm interested in improving ...
wstring getComputerName()
{
    vector<wchar_t> buffer;
    buffer.resize(MAX_COMPUTERNAME_LENGTH+1);
    DWORD size = MAX_COMPUTERNAME_LENGTH;

    GetComputerNameW(&buffer[0], &size);

    return wstring(&buffer[0], size);
}

int main()
{
    wcout << getComputerName() << "\n";
}

My question really is, is this the best way to write the getComputerName function so that it fits into C++ better, or is there a better way? I don't see any way to use a string directly without going via a vector unless I missed something? It works fine, but somehow seems a little ugly. The question isn't about that particular API, it开发者_如何学JAVA's just a convenient example.


In this case, I don't see what std::vector brings to the party. MAX_COMPUTERNAME_LENGTH is not likely to be very large, so I would simply use a C-style array as the temporary buffer.


See this answer to another question. It provides the source to a StringBuffer class which handles this situation very cleanly.


I would say, since you are already at task of abstracting Windows API behind a more generic C++ interface, do away with vector altogether, and don't bother about wstring constructor:

wstring getComputerName()
{
    wchar_t name[MAX_COMPUTERNAME_LENGTH + 1];
    DWORD size = MAX_COMPUTERNAME_LENGTH;

    GetComputerNameW(name, &size);

    return name;
}

This function will return a valid wstring object.


I'd use the vector. In response to you saying you picked a bad example, pretend for a moment that we don't have a reasonable constant upper bound on the string length. Then it's not quite as easy:

#include <string>
#include <vector>
#include <windows.h>

using std::wstring;
using std::vector;

wstring getComputerName()
{
    DWORD size = 1; // or a bigger number if you like
    vector<wchar_t> buffer(size);
    while ((GetComputerNameW(&buffer[0], &size) == 0))
    {
        if (GetLastError() != ERROR_BUFFER_OVERFLOW) aargh(); // handle error
        buffer.resize(++size);
    };
    return wstring(&buffer[0], size);
}

In practice, you can probably get away with writing into a string, but I'm not entirely sure. You certainly need additional guarantees made by your implementation of std::wstring, beyond what's in the standard, but I expect MSVC's strings are probably OK.

I think that if wstring::reference is wchar_t& then you're sorted. 21.3.4 defines that non-const operator[] returns a reference, and that it returns data()[pos]. So if reference is just a plain wchar_t& then there's no scope for exciting copy-on-write behaviour through the reference, and the string must in fact be modifiable through the pointer &buffer[0]. I think. The basic problem here is that the standard allowed implementations more flexibility than turned out to be needed.

That's a lot of effort and commenting though, just to avoid copying a string, so I've never felt the need to avoid an intermediate array/vector.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜