1

I am trying to create an application in which I have a function where I am trying to duplicate a wide character string. I am currently using _wcsdup(), since it is a Windows application and everything is working fine for me. But I need to create a multi-platform function, so _wcsdup() (which is a Windows function) will not work out for me.

Now, my code looks something like this:

wchar_t* out = _wcsdup(wstring.str().c_str());

where wstring is a string stream.

Now, I am looking for a common function for both Windows and Linux to make this function work properly.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Why you don't use `std::copy`? – Ghasem Ramezani Aug 16 '21 at 17:16
  • 6
    Why not `std::wstring out = wstring.str();`? – Galik Aug 16 '21 at 17:17
  • 2
    btw, `wstring` is a terrible name for a variable given that it is the same as the Standard Library type `std::wstring`. – Galik Aug 16 '21 at 17:21
  • @Galik the OP did say: "*where wstring is a string stream*" – Remy Lebeau Aug 16 '21 at 17:23
  • @GhasemRamezani Is there an overload of `std::copy` that returns a `wchar_t *` string, that can later be released by `free`? – Kaz Aug 16 '21 at 17:28
  • @Kaz `std::copy()` doesn't allocate anything, so there is nothing to free. It merely copies data from one buffer to another. It is your responsibility to allocate and free those buffers as needed. – Remy Lebeau Aug 16 '21 at 17:30
  • @RemyLebeau So I don't understand the first comment; how will `std::copy` solve the problem of "I have a C++ `wstring`. The API I'm using needs a `wchar_t *` string that it owns, and calls `free` on later." – Kaz Aug 16 '21 at 17:43
  • @kaz see the examples in [my answer](https://stackoverflow.com/a/68806946/65863). – Remy Lebeau Aug 16 '21 at 18:29

3 Answers3

4

The standard cross-platform equivalent would be to allocate/free a wchar_t[] buffer using new[]/delete[] (or, if absolutely needed, malloc()/free() to mirror the behavior of _wcsdup()), using std::copy() or std::memcpy() to copy characters from wstring into that buffer, eg:

std::wstring w = wstring.str();
wchar_t* out = new wchar_t[w.size()+1];
std::copy(w.begin(), w.end(), out);
w[w.size()] = L'\0';
...
delete[] out;

/*
std::wstring w = wstring.str();
wchar_t* out = (wchar_t*) malloc((w.size() + 1) * sizeof(wchar_t));
std::copy(w.begin(), w.end(), out);
w[w.size()] = L'\0';
...
free(out);
*/
std::wstring w = wstring.str();
size_t size = w.size() + 1;
wchar_t* out = new wchar_t[size];
std::memcpy(out, w.c_str(), size * sizeof(wchar_t));
...
delete[] out;

/*
std::wstring w = wstring.str();
size_t size = (w.size() + 1) * sizeof(wchar_t);
wchar_t* out = (wchar_t*) malloc(size);
std::memcpy(out, w.c_str(), size);
...
free(out);
*/

But, either way, since str() returns a std::wstring to begin with, you are better off simply sticking with std::wstring instead of using wchar_t* at all:

std::wstring out = wstring.str();

You can use out.c_str() or out.data() if you ever need a (const) wchar_t*, such as when passing out to C-style functions that take null-terminated string pointers.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Great Approach. Thanks for this. – Rohit Nagpal Aug 16 '21 at 17:42
  • Personally I'd prefer using `std::unique_ptr` instead of calling `new[]` and `delete[]` myself... You may need to call `get()` to pass the value to some functions, but that seems to be the only downside to me... – fabian Aug 16 '21 at 18:17
  • @fabian personally, I would use `std::vector` before using `std::unique_ptr`, but using `std::wstring` by itself would be even better. – Remy Lebeau Aug 16 '21 at 18:32
1

The traditional way is to have a configuration system in your program which tells you what the platform has and doesn't have:

In some source file dedicated to portability, you have:

#if !HAVE_WCSDUP
wchar_t *wcsdup(const wchar_t *orig)
{
  #if HAVE_MICROSOFT_WCSDUP
    return _wcsdup(orig);
  #else
    size_t nwch = wcslen(orig) + 1;
    wchar_t *copy = wmalloc(nwch);
    if (copy)
      wmemcpy(copy, orig, nwch);
    return copy;
  #endif
}
#endif

In some header file (also included by the above), you have this:

#if !HAVE_WSCDUP
extern "C" wchar_t wcsdup(const wchar_t *);
#endif

to provide the missing declaration. A possible approach also is this. In the header file, you do:

#if HAVE_WCSDUP
// nothing to provide
#elif HAVE_MICROSOFT_WCSDUP
// just alias to the Microsoft one via #define
#define wcsdup _wcsdup
#else
// declare ours: provided in portability.cc
extern "C" wchar_t wcsdup(const wchar_t *);
#endif

Then in portability.cc:

#if !HAVE_WCSDUP && !HAVE_MICROSOFT_WCSDUP
wchar_t *wcsdup(const wchar_t *orig)
{
  size_t nwch = wcslen(orig) + 1;
  wchar_t *copy = wmalloc(nwch);
  if (copy)
    wmemcpy(copy, orig, nwch);
  return copy;
}
#endif

You need a build configuration system around you program to supply the values of these HAVE_ constants. On some systems, a shell script can inspect the environment and throw them into a config.h. For some systems, you can have canned configurations; e.g. the configuration step for building on Windows might consist of copying a hand-maintained config-msvc.h to config.h. In config-msvc.h you have:

#define HAVE_MICROSOFT_WCSDUP 1

I'm assuming that you need a malloc-duplicated string due to communicating with some API that consumes one. Therefore in my answer I have refrained from pontificating about using C++ library features to solve the problem.

However, in C++ code we should probably be referring to the C functions as std::wcslen and so forth. Or portability.cc can just be portability.c, if it is providing missing C functions.

Kaz
  • 55,781
  • 9
  • 100
  • 149
0

Assuming the string needs to be passed to a function that expects to free it, you can use malloc and memcpy:

auto const ws = wstring.str();
auto const ptr = std::malloc(sizeof wchar_t * (ws.size() + 1));
std::memcpy(ptr, ws.c_str(), sizeof wchar_t * (ws.size() + 1));
// pass ptr to another function, or std::free(ptr)

The + 1 is to account for the null terminator, which is not included in size().

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Does that compile for you? I don't think `sizeof` can be applied to an unparenthesized type name: it's either `sizeof expr` or `sizeof (type)`. `expr` can have parentheses, but `(type)` cannot omit them. – Kaz Aug 16 '21 at 21:32