Is there any pattern or other nonstandard mechanism for either gcc (4.8) or icc (14.0) that can guarantee the early, safe construction of static locals?
I need a global collection of local static objects references for the purposes of coarse profiling controllable at run-time. I am actively hurt by standard deferred construction (as well as by dealing with locked or redundant thread_local collections), and it would be highly advantageous to have complete point lists at start time.
Any hope to achieve this?
#include <iostream>
#include <deque>
// Really want to build this list before main() started!
struct ProfilePoint;
static std::deque<ProfilePoint *> pps;
// Costly construction, but only ever with literal/constexpr params.
// Templating, etc., also discourages non-local building in reality.
struct ProfilePoint {
ProfilePoint(int id, char const *i) : id_(id), inf_(i) { pps.push_back(this); }
void doStuff() { /* ... */ }
int id_;
char const *const inf_;
};
// Functions like this will be called concurrently in reality.
void bar(int cnt) {
for (int i = 0; i < cnt; ++i) {
// Dropping in a local definition/call should be enough to hook in to system
static ProfilePoint pp(2, "description in a string literal");
pp.doStuff();
/* ... */
}
}
void dump() {
std::cout << "[";
for (ProfilePoint *pp: pps) { std::cout << " " << pp->id_ << ":" << pp->inf_; }
std::cout << " ]" << std::endl;
}
int main() { dump(); bar(5); dump(); } // "[ ]" then "[ 2 ]" in gcc/icc
I've read up on Schwarz Counters and sections 3.6.2 (basic.start.init) / 6.7 (stmt.decl) of the C++11 spec, but I don't have as much knowledge about compiler-specific behavior and haven't been able to find anyone else posting about trying to achieve this trick.
Accepted answer:
As John notes below, all classes (may) have their static members initialized before main(), but given that C++11 §9.4.2/5 [class.static.data] and §9.8/4 [class.local] forbid static data members in local classes, a class that is templated over a local class and has a static data member of that class can have its initialization done at start-time. Quite a brilliant insight, and even more subtle than I first thought!
// John Bandela's solutions (slightly condensed):
template <class TPPDesc> struct PPWrapper_T { static ProfilePoint p; };
template <class TPPDesc>
ProfilePoint PPWrapper_T<TPPDesc>::p(TPPDesc::id(), TPPDesc::desc());
#define PROFILE_POINT(ID, DESC, NAME) \
struct ppdef_##NAME { \
static int id() { return ID; } \
static char const *desc() { return DESC; } \
}; \
static PPWrapper_T<ppdef_##NAME> NAME // semicolon must follow!
// ...
void foo() {
PROFILE_POINT(2, "another_description", pp);
pp.p.doStuff();
}
Note also that using a Meyers singleton method for the collection completes the overall safety of this approach. The collection may have to be locked to guard against concurrent static initializations of the points, however. I still need to check spec to confirm the specification for this and whether the static member initialization is actually forced to be done before main().