std::string.resize() and std::string.length() - c++

I'm relatively new to C++ and I'm still getting to grips with the C++ Standard Library. To help transition from C, I want to format a std::string using printf-style formatters. I realise stringstream is a more type-safe approach, but I find myself finding printf-style much easier to read and deal with (at least, for the time being). This is my function:
using namespace std;
string formatStdString(const string &format, ...)
{
va_list va;
string output;
size_t needed;
size_t used;
va_start(va, format);
needed = vsnprintf(&output[0], 0, format.c_str(), va);
output.resize(needed + 1); // for null terminator??
va_end(va);
va_start(va, format);
used = vsnprintf(&output[0], output.capacity(), format.c_str(), va);
// assert(used == needed);
va_end(va);
return output;
}
This works, kinda. A few things that I am not sure about are:
Do I need to make room for a null terminator, or is this unnecessary?
Is capacity() the right function to call here? I keep thinking length() would return 0 since the first character in the string is a '\0'.
Occasionally while writing this string's contents to a socket (using its c_str() and length()), I have null bytes popping up on the receiving end, which is causing a bit of grief, but they seem to appear inconsistently. If I don't use this function at all, no null bytes appear.

With the current standard (the upcomming standard differs here) there is no guarantee that the internal memory buffer managed by the std::string will be contiguous, or that the .c_str() method returns a pointer to the internal data representation (the implementation is allowed to generate a contiguous read-only block for that operation and return a pointer into it. A pointer to the actual internal data can be retrieved with the .data() member method, but note that it also returns a constant pointer: i.e. it is not intended for you to modify the contents. The buffer return by .data() it is not necessarily null terminated, the implementation only needs to guarantee the null termination when c_str() is called, so even in implementations where .data() and .c_str() are called, the implementation can add the \0 to the end of the buffer when the latter is called.
The standard intended to allow rope implementations, so in principle it is unsafe to do what you are trying, and from the point of view of the standard you should use an intermediate std::vector (guaranteed contiguity, and there is a guarantee that &myvector[0] is a pointer to the first allocated block of the real buffer).
In all implementations I know of, the internal memory handled by std::string is actually a contiguous buffer and using .data() is undefined behavior (writting to a constant variable) but even if incorrect it might work (I would avoid it). You should use other libraries that are designed for this purpose, like boost::format.
About the null termination. If you finally decide to follow the path of the undefined... you would need to allocate extra space for the null terminator, since the library will write it into the buffer. Now, the problem is that unlike C-style strings, std::strings can hold null pointers internally, so you will have to resize the string down to fit the largest contiguous block of memory from the beginning that contains no \0. That is probably the issue you are finding with spurious null characters. This means that the bad approach of using vsnprintf(or the family) has to be followed by str.resize( strlen( str.c_str() ) ) to discard all contents of the string after the first \0.
Overall, I would advice against this approach, and insist in either getting used to the C++ way of formatting, using third party libraries (boost is third party, but it is also the most standard non-standard library), using vectors or managing memory like in C... but that last option should be avoided like the plague.
// A safe way in C++ of using vsnprintf:
std::vector<char> tmp( 1000 ); // expected maximum size
vsnprintf( &tmp[0], tmp.size(), "Hi %s", name.c_str() ); // assuming name to be a string
std::string salute( &tmp[0] );

Use boost::format, if you prefer printf() over streams.
Edit: Just to make this clear, actually I fully agree with Alan, who said you should use streams.

I think that there are no guarantees that the layout of the string as referenced by &output[0] is contiguous and that you can write to it.
Use std::vector instead as a buffer which is guaranteed to have contiguous storage since C++03.
using namespace std;
string formatStdString(const string &format, ...)
{
va_list va;
vector<string::value_type> output(1); // ensure some storage is allocated
size_t needed;
size_t used;
va_start(va, format);
needed = vsnprintf(&output[0], 0, format.c_str(), va);
output.resize(needed); // don't need null terminator
va_end(va);
// Here we should ensure that needed != 0
va_start(va, format);
used = vsnprintf(&output[0], output.size(), format.c_str(), va); // use size()
// assert(used == needed);
va_end(va);
return string(output.begin(), output.end());
}
NOTE: You'll have to set an initial size to the vector as the statement &output[0] can otherwise attempt to reference a non-existing item (as the internal buffer might not have been allocated yet).

1) You do not need to make space for the null terminator.
2) capacity() tells you how much space the string has reserved internally. length() tells you the length of the string. You probably don't want capacity()

The std::string class takes care of the null terminator for you.
However, as pointed out, since you're using vnsprintf to the raw underying string buffer (C anachronisms die hard...), you will have to ensure there is room for the null terminator.

My implementation for variable argument lists for functions is like this:
std::string format(const char *fmt, ...)
{
using std::string;
using std::vector;
string retStr("");
if (NULL != fmt)
{
va_list marker = NULL;
// initialize variable arguments
va_start(marker, fmt);
// Get formatted string length adding one for NULL
size_t len = _vscprintf(fmt, marker) + 1;
// Create a char vector to hold the formatted string.
vector<char> buffer(len, '\0');
int nWritten = _vsnprintf_s(&buffer[0], buffer.size(), len, fmt,
marker);
if (nWritten > 0)
{
retStr = &buffer[0];
}
// Reset variable arguments
va_end(marker);
}
return retStr;
}

To help transition from C, I want to
format a std::string using
printf-style formatters.
Just don't :(
If you do this, you're not actually learning C++ but coding C with a C++ compiler. It's a bad mindset, bad practice, and it propagates the problems that the std::o*stream classes were created to avoid.
I realise stringstream is a more
type-safe approach, but I find myself
finding printf-style much easier to
read and deal with (at least, for the
time being).
It's not a more typesafe approach. It is a typesafe approach. More than that, it minimizes dependencies, it lowers the number of issues you have to keep track of (like explicit buffer allocation and keeping track of the null char terminator) and it makes it easier to maintain your code.
Above that it is completely extensible / customizable:
you can extend locale formatting
you can define the i/o operations for custom data types
you can add new types of output formatting
you can add new buffer i/o types (making for example std::clog write to a window)
you can plug in different error handling policies.
std::o*stream family of classes is very powerful and once you learn to use it correctly there's little doubt you will not go back.
Unless you have very specific requirements your time will probably be much better spent learning the o*stream classes than writing printf in C++.

Related

Strncpy should only be used with fixed length arrays

According to this StackOverflow comment strncpy should never be used with a non-fixed length array.
strncpy should never be used unless you're working with fixed-width, not-necessarily-terminated string fields in structures/binary files. – R.. Jan 11 '12 at 16:22
I understand that it is redundant if you are dynamically allocating memory for the string but is there a reason why it would be bad to use strncpy over strcpy
strncpy will copy data up to the limit you specify--but if it reaches that limit before the end of the string, it'll leave the destination unterminated.
In other words, there are two possibilities with strncpy. One is that you get behavior precisely like strcpy would have produced anyway (except slower, since it fills the remainder of the destination buffer with NULs, which you virtually never actually want or care about). The other is that it produces a result you generally can't put to any real use.
If you want to copy a string up to a maximum length into a fixed-length buffer, you can (for example) use sprintf to do the job:
char buffer[256];
sprintf(buffer, "%255s", source);
Unlike strncpy, this always zero-terminates the result, so the result is always usable as a string.
If you don't want to use sprintf (or similar), I'd advise just writing a function that actually does what you want, something on this general order:
void copy_string(char const *dest, char const *source, size_t max_len) {
size_t i;
for (i=0; i<max_len-1 && source[i]; i++)
dest[i] = source[i];
dest[i] = '\0';
}
Since you've tagged this as C++ (in addition to C): my advice would be to generally avoid this whole mess in C++ by just using std::string.
If you really have to work with NUL-terminated sequences in C++, you might consider another possibility:
template <size_t N>
void copy_string(char const (&dest)[N], char const *source) {
size_t i;
for (i=0; i<N-1 && source[i]; i++)
dest[i] = source[i];
dest[i] = '\0';
}
This only works when the destination is an actual array (not a pointer), but for that case, it gets the compiler to deduce the size of the array, instead of requiring the user to pass it explicitly. This will generally make the code a tiny bit faster (less overhead in the function call) and much harder to screw up and pass the wrong size.
The argument against using strncpy is that it does not guarentee that your string will be null terminated.
The less error prone way to copy a string in C when using non-fixed length arrays is to use snprintf which does guarentee null termination of your string.
A good Blog Post Commenting on *n* functions.
These functions let you specify the size of the buffer but – and this is really important – they do not guarantee null-termination. If you ask these functions to write more characters than will fill the buffer then they will stop – thus avoiding the buffer overrun – but they will not null-terminate the buffer.
Which means that the use of strncpy and other such functions when not dealing with fixed arrays introduces unnessisary risk of non-null terminated strings which can be time-bombs in your code.
char * strncpy ( char * destination, const char * source, size_t num );
Limitations of strncpy():
It doesn't put a null-terminator on the destination string if it is completely filled. And, no null-character is implicitly appended at the end of destination if source is longer than num.
If num is greater than the length of source string, the destination string is padded with null characters up to num length.
Like strcpy, it is not a memory-safe operation. Because it does not check for sufficient space in destination before it copies source, it is a potential cause of buffer overruns.
Refer: Why should you use strncpy instead of strcpy?
We have 2 versions for copy string from one to another
1> strcpy
2> strncpy
These two versions is used for fixed and non-fixed length array. The strcpy don't check the upper bound for destination string when copy string, strncpy will check it. When the destination string is reached to this upper bound, the function strncpy will return error code, in the meantime the function strcpy will cause some effect in memory of the current process and terminate the process immediately. So that the strncpy is more secure than strcpy

How to avoid providing length along with char*?

There is a function which sends data to the server:
int send(
_In_ SOCKET s,
_In_ const char *buf,
_In_ int len,
_In_ int flags
);
Providing length seems to me a little bit weird. I need to write a function, sending a line to the server and wrapping this one such that we don't have to provide length explicitly. I'm a Java-developer and in Java we could just invoke String::length() method, but now we're not in Java. How can I do that, unless providing length as a template parameter? For instance:
void sendLine(SOCKET s, const char *buf)
{
}
Is it possible to implement such a function?
Use std string:
void sendLine(SOCKET s, const std::string& buf) {
send (s, buf.c_str(), buf.size()+1, 0); //+1 will also transmit terminating \0.
}
On a side note: your wrapper function ignores the return value and doesn't take any flags.
you can retrieve the length of C-string by using strlen(const char*) function.
make sure all the strings are null terminated and keep in mind that null-termination (the length grows by 1)
Edit: My answer originally only mentioned std::string. I've now also added std::vector<char> to account for situations where send is not used for strictly textual data.
First of all, you absolutely need a C++ book. You are looking for either the std::string class or for std::vector<char>, both of which are fundamental elements of the language.
Your question is a bit like asking, in Java, how to avoid char[] because you never heard of java.lang.String, or how to avoid arrays in general because you never heard of java.util.ArrayList.
For the first part of this answer, let's assume you are dealing with just text output here, i.e. with output where a char is really meant to be a text character. That's the std::string use case.
Providing lenght seems to me a little bit wierd.
That's the way strings work in C. A C string is really a pointer to a memory location where characters are stored. Normally, C strings are null-terminated. This means that the last character stored for the string is '\0'. It means "the string stops here, and if you move further, you enter illegal territory".
Here is a C-style example:
#include <string.h>
#include <stdio.h>
void f(char const* s)
{
int l = strlen(s); // l = 3
printf(s); // prints "foo"
}
int main()
{
char* test = new char[4]; // avoid new[] in real programs
test[0] = 'f';
test[1] = 'o';
test[2] = 'o';
test[3] = '\0';
f(test);
delete[] test;
}
strlen just counts all characters at the specified position in memory until it finds '\0'. printf just writes all characters at the specified position in memory until it finds '\0'.
So far, so good. Now what happens if someone forgets about the null terminator?
char* test = new char[3]; // don't do this at home, please
test[0] = 'f';
test[1] = 'o';
test[2] = 'o';
f(test); // uh-oh, there is no null terminator...
The result will be undefined behaviour. strlen will keep looking for '\0'. So will printf. The functions will try to read memory they are not supposed to. The program is allowed to do anything, including crashing. The evil thing is that most likely, nothing will happen for a while because a '\0' just happens to be stored there in memory, until one day you are not so lucky anymore.
That's why C functions are sometimes made safer by requiring you to explicitly specify the number of characters. Your send is such a function. It works fine even without null-terminated strings.
So much for C strings. And now please don't use them in your C++ code. Use std::string. It is designed to be compatible with C functions by providing the c_str() member function, which returns a null-terminated char const * pointing to the contents of the string, and it of course has a size() member function to tell you the number of characters without the null-terminated character (e.g. for a std::string representing the word "foo", size() would be 3, not 4, and 3 is also what a C function like yours would probably expect, but you have to look at the documentation of the function to find out whether it needs the number of visible characters or number of elements in memory).
In fact, with std::string you can just forget about the whole null-termination business. Everything is nicely automated. std::string is exactly as easy and safe to use as java.lang.String.
Your sendLine should thus become:
void sendLine(SOCKET s, std::string const& line)
{
send(s, line.c_str(), line.size());
}
(Passing a std::string by const& is the normal way of passing big objects in C++. It's just for performance, but it's such a widely-used convention that your code would look strange if you just passed std::string.)
How can I do that, unless providing lenght as a template parameter?
This is a misunderstanding of how templates work. With a template, the length would have to be known at compile time. That's certainly not what you intended.
Now, for the second part of the answer, perhaps you aren't really dealing with text here. It's unlikely, as the name "sendLine" in your example sounds very much like text, but perhaps you are dealing with raw data, and a char in your output does not represent a text character but just a value to be interpreted as something completely different, such as the contents of an image file.
In that case, std::string is a poor choice. Your output could contain '\0' characters that do not have the meaning of "data ends here", but which are part of the normal contents. In other words, you don't really have strings anymore, you have a range of char elements in which '\0' has no special meaning.
For this situation, C++ offers the std::vector template, which you can use as std::vector<char>. It is also designed to be usable with C functions by providing a member function that returns a char pointer. Here's an example:
void sendLine(SOCKET s, std::vector<char> const& data)
{
send(s, &data[0], data.size());
}
(The unusual &data[0] syntax means "pointer to the first element of the encapsulated data. C++11 has nicer-to-read ways of doing this, but &data[0] also works in older versions of C++.)
Things to keep in mind:
std::string is like String in Java.
std::vector is like ArrayList in Java.
std::string is for a range of char with the meaning of text, std::vector<char> is for a range of char with the meaning of raw data.
std::string and std::vector are designed to work together with C APIs.
Do not use new[] in C++.
Understand the null termination of C strings.

How to get a non-const pointer to the internal string inside std::string [duplicate]

It is well defined C++ to pass std::vector to C APIs that expect an output array like so because std::vector is contiguous:
std::vector<char> myArray(arraySize);
cStyleAPI(&myArray[0], arraySize);
Is it safe to pass std::string in the same manner to C APIs? Is there any guarantee in standard C++03 that std::string is contiguous and works in the same way as std::vector does in this situation?
If the C API function requires read-only access to the contents of the std::string then use the std::string::c_str() member function to pass the string. This is guaranteed to be a null terminated string.
If you intend to use the std::string as an out parameter, C++03 doesn't guarantee that the stored string is contiguous in memory, but C++11 does. With the latter it is OK to modify the string via operator[] as long as you don't modify the terminating NUL character.
So, I know this has been answered already, but I saw your comment in Praetorian's answer:
It's an OpenGL driver bug that results in the return value for the
maximum length string being broken. See
https://forums.geforce.com/default/topic/531732/glgetactiveattrib-invalid/.
glGetActiveAttrib won't try to write to the pointer returned by the
new[] call with the 0 size allocation, but the string isn't null
terminated. Then, later in the code, the non-null terminated string is
copied into a std::string for storage, which results in a read buffer
overflow. Very confusing to me too, and just checking here to see if
std::string would make things easier to follow.
Uhm... forgive me but if this is your problem then all these solutions seem to be overly complicated. If the problem boils down to the fact that you get a 0 as the buffer size that you need (which means that you will end up with a string that's not NULL-terminated, since there's no space for the NULL terminator) then simply make sure a NULL terminator is always present:
int arraySize;
/* assume arraySize is set to the length we need */
...
/* overallocate by 1 to add an explicit NULL terminator. Just in case. */
char *ptr = malloc(arraySize + 1);
if(ptr != NULL)
{
/* zero out the chunk of memory we got */
memset(ptr, 0, arraySize + 1);
/* call OpenGL function */
cStyleAPI(ptr, arraySize);
/* behold, even if arraySize is 0 because of OpenGL, ptr is still NULL-terminated */
assert(ptr[arraySize] == 0);
/* use it */
...
/* lose it */
free(ptr);
}
This seems, to me, to be the simplest, sanest solution.
Yes, but you'll need to pass them using the c_str method to guarantee null-termination.
No, it isn't, but generally because C strings are assumed to be zero-terminated, which your pointer-to-char isn't. (If you use string, you can use string::c_str() instead, that's 0-terminated.)
Anyways, C++11 does require the elements of vector to be contiguous in memory.
cStyleAPI(&myArray[0], arraySize);
If your cStyleAPI receives a char* for input, that is what std::string::c_str() is for.
If it receives a pre-allocated char* for output, then no. In that case, you should use a std::vector<char> or std::array<char>.
use resize_and_overwrite
https://en.cppreference.com/w/cpp/string/basic_string/resize_and_overwrite
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1072r10.html
[[nodiscard]] static inline string formaterr (DWORD errcode) {
string strerr;
strerr.resize_and_overwrite(2048, [errcode](char* buf, size_t buflen) {
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage
return FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
errcode,
0,
buf,
static_cast<DWORD>(buflen),
nullptr
);
});
return strerr;
}

String operations and memory management

I want to write a convenient wrapper to a C-style function strftime. And I've come to some options to convert char-array to string and vise-versa. Here is my code:
std::string Time::getAsFormattedString ( const std::string& format , const size_t& maxStringSize = 999 )
{
char* timeArray = 0;
std::string timeString;
// [OPTION_0]
timeArray = reinterpret_cast <char*> (malloc(sizeof(char)*maxStringSize)));
// [OPTION_1]
timeArray = const_cast <char*> (timeString.c_str());
// [OPTION_2]
timeArray = &(*(timeString.begin()));
strftime(timeArray,maxStringSize,format.c_str(),&this->time);
timeString = timeArray;
// [OPTION_0]
free(timeArray);
return timeString;
}
№0 option looks safe since no exceptions can be thrown before memory freeing (Edit: timeString = timeArray can throw one, try-catch needed around that line)
№1 const-casting always looks like a hack
№2 seems to be the best by I do not know if there could be some issues with it
Can you please tell me, which one is the most safe, correct, optimal and maybe kind of best-practice.
Thank you.
None of the options you propose are really acceptable; the
second and the third won't even work. Globally, there are two
"acceptable" solutions.
The simplest is:
char buffer[ 1000 ];
size_t n = strftime( buffer, sizeof( buffer ), format.c_str(), &time );
if ( n == 0 ) {
throw SomeError; // or you might just abort...
}
return std::string( buffer );
This has the advantage of simplicity, but you do have to
document the maximum size as a constraint in your interface.
(It seems like a reasonable constraint to me.)
Alternatively, you can remove the constraint:
std::vector<char> buffer( 100 );
size_t n = strftime( &buffer[0], buffer.size(), format.c_str(), &time );
while ( n == 0 ) {
buffer.resize( 2 * buffer.size() );
n = strftime( &buffer[0], buffer.size(), format.c_str(), &time );
}
return std::string( buffer.begin(), buffer.begin() + n );
(In C++11, and in practice in C++03, you can do this with
std::string directly, rather than std::vector. In which
case, you need to call resize( n ) on the resulting string
before returning it.)
As a start, the only option that isn't going to crash is option 0 here. The others will crash as you are saying you have allocated 999 bytes but infact the internal string will probably only have 1 byte allocated to it, and sad things will happen.
However I would probably do this by allocating a large chunk of characters on the stack here.
char timeArray[2048];
strftime(timeArray,2048,format.c_str(),&this->time);
return string(timeArray);
This way you don't have to do any casting or dynamic allocations and will almost certainly be neater and faster.
Option 1 and 2 are not good, as you are not meant to alter the string you get from std::string::c_str() (c is for constant). Option 2 will need a "resize" of the string before you can use it. But I'm not sure strings are guaranteed to copy from their same buffer...
My solution would be to have:
char timeArray[1000];
(Although that is way excessive. Unless you are repeating the same format specifier several times, it's unlikely to achieve more than 100 characters, and that is very lengthy - so "sane" combinations won't reach anywhere near 1000 characters.)
Note that timeString = timeArray can throw an exception for bad_alloc, so if you want to not leak memory in that situation, you either need to use stack-based storage (as in my suggestion), a smart pointer or a try/catch block around parts of the code.
Option 2 (or better still, &timeString[0]) should be preferred.
You're right about the const_cast being bad in option 1, and in option 0, you could, at the very least, clean up the code a bit by using new instead of malloc (and avoid the cast)
But prefer option 2.
(Oh, and as commenters have pointed out, if you're writing into the string itself, you obviously have to first resize it to be big enough that you won't write out of bounds. Given the downvotes, I should probably have been explicit about that)
The only possible solution is option 0 (though some minor adjustments can be performed). As others have pointed out, the standard says that the space returned by the c_str() method is not meant to be used for writing purposes, actually it is a way to allow std::string's to be read by the C standard library (which is part of the C++ standard library).
The Standard reads:
Returns: A pointer to the initial element of an array of length size()
+ 1 whose first size() elements equal the corresponding elements of the string controlled by *this and whose last element is a null
character specified by charT().
Requires: The program shall not
alter any of the values stored in the array. Nor shall the program
treat the returned value as a valid pointer value after any subsequent
call to a non-const member function of the class basic_string that
designates the same object as this.
So, I'd just do a quick fix to your code:
const char * Time::getAsFormattedString(const std::string& format)
{
static char timeArray[256];
std::strftime( timeArray, 256, format.c_str(), &this->time );
return timeArray;
}
This makes the buffer for your method to be created in program startup, and reused continuously, so no memory errors can be expected from that side (as the heap is untouched).
The only problem is that there is space in the stack in order to create the string in which you will store the result of the function, but anyway this will happen after calling the function, the function itself won't touch the heap, and only a minimum of the stack.
In practical terms, the usefulness of the function is untouched, since there is an automatic conversion from const char * to std::string, sou you can safely call it in the usual way:
std::string strTime = time.getAsFormattedString( "%F %T" );
Hope this helps.

Is it safe to pass std::string to C style APIs?

It is well defined C++ to pass std::vector to C APIs that expect an output array like so because std::vector is contiguous:
std::vector<char> myArray(arraySize);
cStyleAPI(&myArray[0], arraySize);
Is it safe to pass std::string in the same manner to C APIs? Is there any guarantee in standard C++03 that std::string is contiguous and works in the same way as std::vector does in this situation?
If the C API function requires read-only access to the contents of the std::string then use the std::string::c_str() member function to pass the string. This is guaranteed to be a null terminated string.
If you intend to use the std::string as an out parameter, C++03 doesn't guarantee that the stored string is contiguous in memory, but C++11 does. With the latter it is OK to modify the string via operator[] as long as you don't modify the terminating NUL character.
So, I know this has been answered already, but I saw your comment in Praetorian's answer:
It's an OpenGL driver bug that results in the return value for the
maximum length string being broken. See
https://forums.geforce.com/default/topic/531732/glgetactiveattrib-invalid/.
glGetActiveAttrib won't try to write to the pointer returned by the
new[] call with the 0 size allocation, but the string isn't null
terminated. Then, later in the code, the non-null terminated string is
copied into a std::string for storage, which results in a read buffer
overflow. Very confusing to me too, and just checking here to see if
std::string would make things easier to follow.
Uhm... forgive me but if this is your problem then all these solutions seem to be overly complicated. If the problem boils down to the fact that you get a 0 as the buffer size that you need (which means that you will end up with a string that's not NULL-terminated, since there's no space for the NULL terminator) then simply make sure a NULL terminator is always present:
int arraySize;
/* assume arraySize is set to the length we need */
...
/* overallocate by 1 to add an explicit NULL terminator. Just in case. */
char *ptr = malloc(arraySize + 1);
if(ptr != NULL)
{
/* zero out the chunk of memory we got */
memset(ptr, 0, arraySize + 1);
/* call OpenGL function */
cStyleAPI(ptr, arraySize);
/* behold, even if arraySize is 0 because of OpenGL, ptr is still NULL-terminated */
assert(ptr[arraySize] == 0);
/* use it */
...
/* lose it */
free(ptr);
}
This seems, to me, to be the simplest, sanest solution.
Yes, but you'll need to pass them using the c_str method to guarantee null-termination.
No, it isn't, but generally because C strings are assumed to be zero-terminated, which your pointer-to-char isn't. (If you use string, you can use string::c_str() instead, that's 0-terminated.)
Anyways, C++11 does require the elements of vector to be contiguous in memory.
cStyleAPI(&myArray[0], arraySize);
If your cStyleAPI receives a char* for input, that is what std::string::c_str() is for.
If it receives a pre-allocated char* for output, then no. In that case, you should use a std::vector<char> or std::array<char>.
use resize_and_overwrite
https://en.cppreference.com/w/cpp/string/basic_string/resize_and_overwrite
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1072r10.html
[[nodiscard]] static inline string formaterr (DWORD errcode) {
string strerr;
strerr.resize_and_overwrite(2048, [errcode](char* buf, size_t buflen) {
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage
return FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
errcode,
0,
buf,
static_cast<DWORD>(buflen),
nullptr
);
});
return strerr;
}