Reputation: 143
I'm trying to define constant strings that I can consume in a cpp file such that neither the data, nor the pointer can be modified. The linker complains the following:
main.obj : error LNK2001: unresolved external symbol "char const * const g_someString"
Sample code:
//constants.h
extern const char * const g_someString;
//constants.cpp
const char * const g_someString = "Hello";
// main.cpp
int main()
{
strcmp(g_someString, "Hello");
}
This doesn't happen when the const char * const is replaced with const char * though. Is the compiler (MSVC 2015) optimizing out the g_someString definition in constants.cpp?
Upvotes: 1
Views: 1298
Reputation: 145279
Before
const char * const g_someString = "Hello";
you need to declare it as extern
(e.g. by including the header), because const
namespace level variables have internal linkage by default.
That said, you can just define the strings in the header. The separate compilation buys you the ability to modify strings without incurring a rebuild of lots of files, but apart from that, IMHO it's premature optimization.
To make string definitions in a header formally safe for inline
functions, if that's desired, you need the strings (or least the pointers) to have extern
linkage. One way to do that is to leverage a special exemption for templates in the One Definition Rule. E.g. like this:
// Perhaps best generated by some code generation facility:
template< class Dummy >
struct Strings_
{
static char const* const some_string;
static char const* const some_other_string;
};
template< class Dummy >
char const* const Strings_<Dummy>::some_string = "Blah!";
template< class Dummy >
char const* const Strings_<Dummy>::some_string = "Oh, well.";
using Strings = Strings_<void>;
Then usage like
inline void foo() { cout << Strings::some_string << endl; }
Here the Strings::some_string
pointer will be the same in all translation units.
An alternative is to define the strings within an inline
function. Then you can use e.g. an enumeration to name them.
enum String_id { some_string, some_other_string };
inline
auto strings( String_id const id )
-> char const*
{
switch( id )
{
case some_string: return "Blah!";
case some_other_string: return "Oh, well.";
}
assert( false ); // Should never get here.
}
with usage like
inline void foo() { cout << strings( some_string ) << endl; }
The inline
function has extern
linkage, and so it's the same in all translation units.
Upvotes: 4