1

We’re updating a project to c++20, and are running into errors where we pass string literals into functions which take char *. I know this has been changed to make code more safe, but we are interfacing with libraries which we cannot change.

I’d rather not disable the strict treatment of literals via compiler flags, so is there a good way to wrap these literals just in these particular cases?

I was thinking of an inline function, that was named something specific to the library, that internally would use const_cast. That way later if we want to change the code because the library gets updated, we know exactly where to look.

Any other ideas?

12
  • 9
    What does this have to do with C++20? String literals have been const since C++98 and before. Commented Feb 12, 2022 at 3:25
  • 2
    @NicolBolas it could be that some C++20 version of some specific compiler has turned on a warning that used to default to off. But it would be useful if the question mentioned which compiler. Commented Feb 12, 2022 at 3:33
  • 2
    Are you using MSVC, previously not in standards conformance mode (/permissive-)? In that case /std:c++20 also now implies the conformance mode, resulting in the appropriate error. Or have you been using a pre-C++11 standard and ignored the deprecation warnings? I think it would benefit your question if you clarified that. Commented Feb 12, 2022 at 3:36
  • 2
    P.S. casting away the constness can lead to undefined behavior no matter what wrapper you use for it. Commented Feb 12, 2022 at 3:37
  • 2
    @Taekahn The cast itself is not UB, only trying to modify the literal through the non-const pointer is, but that has been true even when the implicit cast to char* was still possible and I don't think any compiler made stronger guarantees. So replacing the implicit casts with explicit ones will not affect whether the code has UB. I am assuming that the library simply doesn't specify const where it expects a const argument, in which case such a wrapper is fine. This may be a third-party library that they cannot modify. Commented Feb 12, 2022 at 4:18

2 Answers 2

1

"Any other ideas?"

static char my_string[] = "string"; ... //elsewhere in the code library_function(my_string); 

The only difference between passing a string like that, and passing a string literal is the section of the assembly the data is stored in.
A string literal is stored in .text, a non-modifiable section.
The non-const string will be stored in .data.

If you really, really care if you're passing a function a pointer to .text or a pointer to .data, and you really, really, trust the library to not modify the parameter now and for ever, then you can certainly cast away the const-ness of your string literals.

Ignoring the fact that documentation lags behind implementation, even if we could believe the documentation promise to not modify its inputs, if it doesn't enforce it through the interface, at any time, on purpose or on accident, that input could be modified.

Sign up to request clarification or add additional context in comments.

Comments

0

The following string literal creates a std::string for each literal and implicitly converts to char*.

#include <string> struct stringlitwrapper { constexpr stringlitwrapper(const char* c) : s(c) {}; operator char*() { return s.data(); } std::string s; }; constexpr stringlitwrapper operator"" _w (const char* c, std::size_t n) { return stringlitwrapper(c); } void libfunction(char* param) { // uses non-const char* as parameter } int main() { libfunction("string literal"_w); return 0; } 

For compilers, which do not support constexpr here (e.g. msvc does, clang not), leave both constexpr away.

By internally storing the literal as non-const string, there is no undefined behaviour involved.

(The library function of course should not overwrite at all or at least not write over the end of the string.)

To prevent heap allocations, the std::string could be replaced by a char array with fixed (maximal) size.

4 Comments

Uff, the performance cost of this though. It would do a heap allocation for every call to a libfunction, even for multiple calls with the same argument literal. It would also require the external library to be stateless, lest it store an argument char* to a heap allocation that will be freed at the end of the call expression.
@Ramon Any idea, how to improve it? char array with fixed (maximal) width, then the heap allocations would be prevented. User-defined string literals do not seem to pass the parameters (char* and size_t) as compile-time constants. Is the solution at least safe from not generating a dangling pointer after the implicit conversion? Your second remark about storing the pointer depends on what the library does with the literals. Perhaps it is not an issue, perhaps it is. But then it would neither be allowed to pass local const* variables.
I really don't think you're gonna get any overhead on this as long as its evaluated at compile time. You might try to see if you can make it consteval instead of constexpr. That being said, iirc std::string has been made constexpr in C++20 so there really should be no problem just using a std::string. i.e. static std::string("string literal");
@Taekahn: C++20 doesn’t allow allocations during constant evaluation to survive into runtime (and neither will C++23).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.