1

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?

4
  • The linker error goes away with an extern declaration before the definition in constants.cpp Commented Nov 6, 2015 at 2:18
  • make sure that constants.cpp includes constants.h (and you don't have circular dependencies) Commented Nov 6, 2015 at 2:18
  • void main is illegal in C++, even if your compiler offers a non-conforming extension it's not good to write code that relies on such extensions Commented Nov 6, 2015 at 2:19
  • 1
    @M.M: I think maybe we should fix all those void main, in addition to commenting, in the cases where the problem isn't related. Commented Nov 6, 2015 at 2:27

1 Answer 1

4

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.

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

7 Comments

Separate compilation can also help the compiler perform string pooling.
A pitfall with defining strings in the header is that if an inline function uses such a string, it causes undefined behaviour. (Not to say that you shouldn't define strings in the header, just that one must be aware of this pitfall)
@M.M: Thanks, I didn't think of that (although I was aware of it at one time). I only thought about whether to mention how to do this in the header with external linkage, but decided that would just complicate the answer. Now I'm not so sure, hm. Anyway, to define the strings in a header and with external linkage, one can just use the old templated constant trick.
Could you post a link to an example of 'old templated constant trick' ?
@Karthik: SO is evidently not as a googleable as desired, because simple googling didn't turn up anything. And I've answered that umpteen times here. Although it may be google itself, because it's also been discussed in Usenet groups etc. Oh well, adding to this answer then.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.