Hacker News new | past | comments | ask | show | jobs | submit login
Data initialization in C++ (woboq.com)
58 points by Tsiolkovsky on May 17, 2013 | hide | past | favorite | 17 comments



File-level static non-POD instances are a big no-no. If you really (really) need a global static, move it into function scope in a function which returns the static (singleton pattern). (Additional tricks can be used to ensure it is only initialized once in a thread-safe manner.)

e.g. https://github.com/bloomberg/bsl/blob/master/groups/bsl/bslm...


Static init/de-init order were huge issues at my last job, working on embedded devices. When I say huge, I really mean issues that would seem to come and go randomly with builds, and in some cases brick devices until someone could JTAG it and get it up on a debugger to load a new boot block or determine the root cause. As the guy that got to do this, let me tell you that stepping through the ARM assembly of the runtime library made me feel like an uber hacker, but it wasn't exactly fun.

The nifty counter pattern[0] ended up being the most helpful for generating a static initialization and de-initialization order without conflicts. The tradeoffs are that it increases codesize and that it looks like magic to a lot of people. I don't remember the exact reason that wrapping everything in functions returning references didn't work - I think it was more to do with the de-init side of things, since the function wrappers guarantee the init order but not the de-init order.

[0] http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Count...


There's an alternative nifty counter implementation that doesn't use as much memory. See near the bottom of http://www.petebecker.com/js/js199905.html


I read through the page, but nothing popped out to me as saving memory. Am I missing something obvious? They both look like they cost 1 static counter, plus sizeof(class instance with no member variables) per translation unit the header is included in.


In the alternate version, only one instance of the initializer object is maintained. In the standard version, there's one object instantiated and maintained for every file that included the header.


How happy are you with C++ on embedded devices? If possible, could you specify what platform you are working on and what compiler support is like?

I hear its popular among the Arduino crowd, but I've so far preferred C kernel style code for embedded.


I was working at Garmin - it was a mix of C and C++ on the devices. Most of Garmin's devices at the time ran Garmin's own proprietary OS, usually called GarminOS. The majority of the low level system code was written in C or ARM assembly, and the stuff in the application layer was more generally C++. There was definitely some of the C/C++ you read about too.

I was happy with using C++ there - it didn't cause significant increases in code size, allowed STL container use, and was what I learned in undergrad, so it was easy. But it does present problems when you try to do everything the "C++ way" and get overzealous with OO design, then find out you now need to write a C wrapper so legacy code can call it.

We used the official armcc compiler iirc, which we had binaries for linux/windows/etc. There were issues with building the code with gcc targeted at ARM and with clang I believe, it wasn't a priority to get it working but I'm sure it could have been done.


Uhm... but why would you have globals with constructors in a code where de/initialization order matters?


Massive, legacy codebase, C wrappers around new C++ functionality... to be honest I don't remember the exact reason. It mostly had to do with concurrency primitives not being initialized when they needed to be due to a lack of a way to initialize a class instance the same way POD instances are initialized (aka int x = 0 in global scope.)

You work with what you're given, sometimes! ;)


It's not just globals with constructors and the order is not obvious. For example, few people (including yours truly) realize the whole complexity of the code like this:

const float epsilon = 0.001f; const float one_sup = 1.f + epsilon; const float one_inf = 1.f - epsilon;

I once spent a day trying to figure out what's going on when similar code suddenly (like after several years of constant use without any issues) broke.


Are you saying this to violently agree with the OP? He covers exactly this in his section "What is the problem with global objects with constructor?", where he even points out the function-scope macro that can be used to help alleviate the problem.


Yes, but I feel it is not stated prominently/strongly enough. Having them can easily lead to a lot of other problems down the line, so to me it's not so much a recommendation as an order -- don't do this!


This is my first exposure to constexpr and it looks very interesting. According to this article it's a second C++ metaprogramming language: http://cpptruths.blogspot.ca/2011/07/want-speed-use-constexp....

The more I learn about C++11 the more I like it. There's some really amazing new features that I've only seen in Lisps, plus there's full static typing, which is the one thing I miss from Lisps.


The more I use C++11 the less I like it.

constexpr has many problems:

- Its syntax is that of C++s, but limited to a single statement and so is about as expressive as a block of wood. Templates are much closer to functional languages and so are naturally more expressive. Seriously, use templates instead!

- It's inefficient. If you want to do heavy computations then you're better off writing a program to generate code. If you want efficient run-time performance then good luck optimizing a single statement.

- It's specification is full of quirks and pitfalls. Quite simply, constexpr does not follow the path of least surprise. Plus, I hear it was a nightmare for the compiler writers.


After reading up on Lisp macro programming (PG's on OnLisp and others) you will see that it is always advised to use only purely functional code when doing metaprogramming. In Common Lisp and all the others Lisps I've seen, this is not strictly enforced by the language and only advised. The code should be purely functional because you don't know when and how many times it can be called by the compiler leading to some grand mysteries when something does not go as expected during compilation due to unintended state.

The constexpr design is taking this to heart and enforcing that the code to be purely functional. I don't see this as a disadvantage but take it that designers of C++ are forcing best practices for the features they are adding. Best practices are generally not the most convenient, but by doing it this way, they're giving you less 'rope to shoot yourself in the foot'. Lisp was more of a research language woring as a testbed of features that are now moving into mainstream languages now that there is some experience with how to use them in the least damaging way.

Purely functional programming is not as 'easy' to write because almost everyone learns imperative first, which is the most natural way to code (do this, then do this, then do this,...). Using functional code requires you put the control that you would normally put into code into the data. It's a different way of thinking and is not natural in the way that imperative programming is.

And regarding that implementing constexpr wsa a 'nightmare for compiler writers', it should be! Strong abstractions are difficult to design and implement, but once they are complete everyone benefits. Compilers are the one things that should take on the hard features since everyone uses them.

Thanks for your experiences with constexpr and I'll form my own opinions of it after some practial use and maybe i'll find it just as lacking as you do in the end :). I'll stand by my statement that C++11 is heading in the right direction, even though it may have some warts and tradeoffs that it can't be avoided, like anything that matures. It will be interesting to see what C++ goes in the next 10 or more years.


Hi! Thanks for your thoughts :)

I pretty much agree with everything you said, with the exception of defending constexpr. What follows is more ranting on constexpr and praise for templates.

> The constexpr design is taking this to heart and enforcing that the code to be purely functional.

You're falling into the common trap of confusing purity with functional. constexpr is only enforcing code to be free of side effects; it's even less functional than C.

Templates on the other hand, are functional. With templates, you get pattern matching, easy recursive data types, high order functions, and more. You can literally take Haskell code, change the syntax a bit, and end up with a complete metaprogram. That's not possible with constexpr.

Templates can work with values, but more importantly, they can work with types. Being able to actually to modify the types and code of the program is essential to metaprogramming. None of that comes with constexpr. Hell, I wouldn't even consider constexpr to be metaprogramming.

In addition, templates can actually form abstractions, even ones that aren't functional. Boost.MPL, over a decade old, emulated C++-style containers at compile time using only templates. As a personal experience, I've implemented a stack-based language akin to Forth using templates, see: http://pubby8.wordpress.com/2012/10/15/stack-based-template-...

Well guess what? Templates suck. They slow the compile down to a crawl, their syntax is incomprehensible, and don't get me started on their error message. The only reason to use them is because there is nothing better.

C++11 had a chance to fix this. Instead, we got constexpr.


I feel the same with every new coming C++ feature. From the recent I like auto, and I only wish anonymous functions call syntax was a bit easier.

Here is a good usage of constexpr - http://www.macieira.org/blog/2011/07/initialising-an-array-w...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: