Существует ли какой-либо шаблон или другой нестандартный механизм для gcc (4.8) или icc (14.0), который может гарантировать раннее и безопасное создание статических локальных объектов?
Мне нужна глобальная коллекция ссылок на локальные статические объекты для целей грубого профилирования, управляемого во время выполнения. Меня активно беспокоит стандартное отложенное построение (а также работа с заблокированными или избыточными коллекциями thread_local), и было бы очень полезно иметь полные списки точек во время запуска.
Есть ли надежда достичь этого?
#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
Я читал о счетчиках Шварца и разделах 3.6.2 (basic.start.init) / 6.7 (stmt.decl) спецификации C ++ 11, но у меня не так много знаний о поведении, специфичном для компилятора, и не смог найти никого, кто бы писал о попытках добиться этого трюка.
Принятый ответ:
Как отмечает Джон ниже, все классы (может) их статические члены инициализируются перед main (), но учитывая что C++11 §9.4.2/5 [class.static.data]
а также §9.8/4 [class.local]
запретить статические данные членов в локальных классах, класс, который основан на локальном классе и имеет статический член данных этого класса, может инициализироваться во время запуска. Довольно блестящее понимание, и даже более тонкое, чем я думал!
// 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();
}
Отметим также, что использование одноэлементного метода Мейерса для сбора данных завершает общую безопасность этого подхода. Однако, возможно, придется заблокировать коллекцию для защиты от одновременной статической инициализации точек. Мне все еще нужно проверить спецификацию, чтобы подтвердить спецификацию для этого и выяснить, действительно ли инициализация статического члена действительно должна быть выполнена перед main ().
Попробуй это
#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_;
};
template<class IdDescription>
struct ProfilePoint_{
static ProfilePoint p;};
template<class IdDescription>
ProfilePoint ProfilePoint_<IdDescription>::p( IdDescription::id(), IdDescription::description() );
#define PROFILE_POINT(theid,thedescription) \
struct ppdef_static_class{ \
static int id(){ return theid; } \
static const char* description(){ return thedescription; } \
};\
static ProfilePoint_<ppdef_static_class>
// 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
PROFILE_POINT(2, "description is a string literal") pp;
pp.p.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(); } // Does what you want
Это работает для MSVC 2013 и Ideone http://ideone.com/Z3n1U0
Это требует использования макроса и для вызова doStuff () вы должны сделать .p.doStuff (). Вы также не можете иметь более 1 точки профиля в функции (но это легко исправить).
Это работает путем определения локального класса, который используется в качестве параметра для шаблона класса, который имеет статический член. Ссылаясь на этот шаблон в функции, вы заставляете компилятор создавать экземпляр статического члена шаблона.
Дайте мне знать, если у вас есть какие-либо вопросы об этой технике.
Вы можете сделать это так:
#include <iostream>
#include <deque>
#include <memory>
#include <map>
class ProfilePoint
{
public:
typedef unsigned Identifier;
private:
struct Data {
Identifier id;
const char* information;
unsigned count;
Data(Identifier id, const char* information)
: id(id), information(information), count(0)
{}
};
public:
static void dump();
const char* information() const { return m_data.information; }
Identifier id() const { return m_data.id; }
ProfilePoint(const char* information)
: m_data(*get_data(0, information))
{}
void apply() const {
++m_data.count;
}
private:
static Data* get_data(Identifier, const char* information);
Data& m_data;
};ProfilePoint::Data* ProfilePoint::get_data(Identifier id, const char* information) {
typedef std::deque<Data> StaticData;
StaticData static_data;
if( ! information) return &static_data[id];
else {
static_data.push_back(Data(static_data.size(), information));
for(auto d: static_data)
std::cout << d.information << std::endl;
return &static_data.back();
}
return 0;
}
void ProfilePoint::dump() {
std::cout << "dump" << std::endl;
Data* data;
for(Identifier i = 0; (data = get_data(i, 0)); ++i) {
std::cout
<< "Profile Point: " << data->information
<< ", Count: " << data->count << std::endl;
}
}namespace {
ProfilePoint pf("Function");
void f() {
pf.apply();
pf.apply();
pf.apply();
ProfilePoint::dump();
}
} // namespace
int main()
{
f();
return 0;
}
Это поддерживает один экземпляр контейнера точек профиля в функции и инициализирует каждую точку профиля во время инициализации модуля перевода.