'Ensure that char pointers always point to the same string literal
Given the code
// somewhere in the program
const char* p1 = "Hello World";
// somewhere else in the program
const char* p2 = "Hello World";
is there a way to ensure that p1 == p2
is always satisfied within the entire program / library? By that I mean that p1
and p2
always refer to the same string literal.
The Reason behind it
What I'm trying to achieve is to use const char*
as a key for std::map<const char*, something>
. I have a macro
#define nameof(id) #id
that mimics the behavior of the nameof
keyword in C# (I know this is already flawed) and I want to use it to access a registry like structure, for example
void foo()
{
auto x = getMapping(nameof(foo));
}
// different place in code
void registerFoo(something x)
{
setMapping("foo", x);
}
Solution 1:[1]
As Barry shows in their answer the behavior you want is not guaranteed. You're going to have to pay the cost of string comparisons, but you can at least avoid any memory allocations or writing a comparator by using a std::string_view
. A std::string_view
is a lightweight view of a string that holds a pointer to the string data and the size of the string and it has a built in operator <
that will do a lexicographical comparison. That would change your map to
std::map<std::string_view, something>
Solution 2:[2]
There is no such requirement. [lex.string]/15:
Whether all string literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified.
Best you can do is assert()
or just avoid repeating yourself and stick the thing in a function:
char const* my_literal() { return "Hello World"; }
char const* p1 = my_literal();
char const* p2 = my_literal();
Solution 3:[3]
Identical literal strings are not guaranty to be identical, but as you use MACRO to create the string, you can change it to return identical string.
gcc/clang have an extension to allow to build UDL from literal string:
template<typename Char, Char... Cs>
struct CsHelper
{
static constexpr const Char s[] = {Cs..., 0}; // The unique address
};
// That template uses the extension
template<typename Char, Char... Cs>
constexpr auto operator"" _cs() -> const Char (&)[1 + sizeof...(Cs)] {
return CsHelper<Char, Cs...>::s;
}
and then
#define nameof(id) #id ## _cs
See my answer from String-interning at compiletime for profiling to have MAKE_STRING
macro if you cannot used the extension (Really more verbose, and hard coded limit for accepted string length).
Solution 4:[4]
There is no requirement that two string literals with the same text are the same object. So the two mentions of ”Hello world”
may or may not refer to a single string in memory. That means that
const char* p1 = "Hello World";
const char* p2 = "Hello World";
Does not necessarily make p1
equal to p2
. To do that, you have to set one of them equal to the other:
const char* p2 = p1;
But either one of those pointers can be modified, and the other pointer won’t track that change. To make sure that such changes can’t be done, make the pointers const:
const char* const p1 = "Hello World";
const char* const p2 = p1;
Or, if p1
needs to be modifiable, make p2
a reference:
const char* p1 = "Hello World";
const char*& p2 = p1;
Now p2
will point at whatever p1
points at.
Solution 5:[5]
I do not know if it helps, but in Visual Studio
there is a C++ option called "Enable String Pooling".
/GF (Eliminate Duplicate Strings)
Enables the compiler to create a single copy of identical strings in the program image and in memory during execution. This is an optimization called string pooling that can create smaller programs.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Barry |
Solution 3 | |
Solution 4 | |
Solution 5 | TrueY |