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().
Try this
This works for MSVC 2013 and ideone http://ideone.com/Z3n1U0
This does require use of macro and to call doStuff() you have to do .p.doStuff(). You also cannot have more than 1 profile point in a function (but this can easily be fixed).
This works by defining a local class that is used as a parameter to a template class that has a static member. By referencing that template in the function, you force the compiler to instantiate the static member of the template.
Let me know if you have any questions about this technique.