27

I have string constants, for strings that I use in multiple places in my app:

namespace Common{
    static const std::string mystring = "IamAwesum";
}

When posting a question about something else (What happens to a .h file that is not included in a target during compilation?), another user made the following comment :

be aware that your static string are global in this case. So they are could create an exception at anytime and can't be catch. I advise you to use function who return a reference of your string. std::string const &mystring { static std::string const mystring = "IamAwesum"; return mystring} by this way your object is only construct when needed

Can someone explain why using static const strings in the manner that I do so above, risks throwing exceptions ?

1
  • something like this happened to me during the making of an embedded application. The dynamic memory allocation might not work yet. Commented Nov 2, 2016 at 14:13

3 Answers 3

33

N4140 § 3.6.2 [basic.start.init]/ 4

It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main.

N4140 § N4140 15.3 [except.handle]/ 13

Exceptions thrown in destructors of objects with static storage duration or in constructors of namespace-scope objects with static storage duration are not caught by a function-try-block on main().

You simply cannot catch an exception generated by the string's constructor - say, std::bad_alloc.

(opinion) That being said, for such small strings I find this kind of consideration to be paranoid.

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

19 Comments

The problem is that the static could be initialized by the runtime before any of your code can "run". Meaning that, if your string is long enough and your memory small enough you might run into a bad_alloc without having run even 1 line of code in your own main, so there's no way to catch these exceptions.
@John, there are basicly two models in which initialization code is inserted into your programs entry point by the compiler. (1) The compiler injects them into your main function definition (2) The compiler wraps your main function in another, where initialization occurs, and then your main is called. In the second case, you cannot catch an exception in your main function, since the code is executed a stack frame above it, so to speak.
@John read the opinion part. Unless you're developing for embedded, I find it extremely unlikely that you'll have such a problem.
Also, @krzaq even if this would happen I wonder what the downsides would be. It's not like catching a bad_alloc allows you to do stuff you wouldn't be able to do when not catching it since you can't guarantee anything at this point.
Of course on certain embeded systems all code is already in ROM, there's a limited number of processes and threads whose stacks are pre-allocated, limits on the size of modifiable static data areas, all that stuff, which might mean that program start-up is guaranteed, and furthermore the consequences of std::terminate() might be worse than you could achieve by catching the exception. Then sure, you want to make sure you catch the exception. If you're writing a Windows app, the system is already so screwed by the time that 10-byte allocation fails, nothing you do is likely to help...
|
5

The only "issue" -- if you can call it that -- which I see with your code is that you are being wasteful by needlessly copying data that is already constant into a dynamically allocated buffer (which is formally constant, but not in reality). This uses twice as much physical memory as necessary and does a needless copy.

Does it matter? Almost certainly, no. Even on a "rather limited memory" system, this will nowadays hardly be noticeable, neither from an execution time point of view, nor by its memory consumption.

As for exceptions, it is of course technically true that the allocation that std::string has to make could fail, and therefore the constructor could throw, and you wouldn't be able to catch it. But please be realistic.
This is almost guaranteed not to happen, but even if it does... if something as trivial as allocating memory for a couple of string fails while your program starts up, you have a really, really serious issue on a completely different scale!
Besides, as pointed out in a comment on another answer above: Assuming this does happen, what are you going to do about it? The program is utterly unable to run, so there's not much short of killing the program that you could conceivably do.

Now, with C++17 not being far away and string_view already being available in std::experimental on several mainstream compilers, there's another thing you could try: Use the correct thing.

A string_view will, contrary to a string, not allocate non-constant memory, copy constant data into that, and then pretend it's constant. Instead, it will manage a pointer directly to the constant data, and that's all.
That way, your constants are truly (not just formally) constant, there are no allocations, no possibility of exceptions, and no double memory usage. And for the most part, it still looks and smells like a string. The only notable differences being that a string_view doesn't guarantee nul-termination (but the character constant it points to does, so this is irrelevant), and the fact that it's really constant, not modifiable... which is exactly what you want.

3 Comments

Using strings could be the right thing to do™ if he passes them to lots of functions taking string by const ref. Generally I agree with you, though.
Beware that std::string might not be needed that often anymore. I am transitioning to string_view (if I remember the name correctly) for many of my functions, currently relying on boost::string_ref. Unless you need a 0-terminated string, string_view has many advantages over std::string const&..
Is there an alternative? Is there any way to initialize a global string variable?
4

The pdf document mostly refers to exceptions from the object ctor and initialization order fiasco with static or dynamically linked libraries.

The only danger I see in your code for exceptions is if the ctor of std::string will throw when it is called.

If you want to really be on the safe side you can use static const char* mystring instead which will not call to a C++ ctor.

There is also the matter of the code being in a shared library which then needs to be placed in the address space of the process. I don't see that as a major problem if you don't use complicated ctors(ctors that can throw).

4 Comments

Can't creation of a const char* result in allocation errors that you can't react to as well?
If you use new or malloc then yes. If you use a string literal the compiler will store the string inside the executable and there won't be any C++ construction and destruction involved.
So the application won't even start if there's too little memory available?
No, the application will start. This is where virtual memory kicks in. The operating system makes the app think it has all the machine memory for itself but in fact it swaps parts of the memory to the disk. When the app tries to access that memory the operating system detects that and loads the needed memory data back to the memory.

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.