本地静态对象的非延迟初始化

Non-deferred initialization of local static objects?

本文关键字:延迟 初始化 对象 静态      更新时间:2023-10-16

对于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

我已经阅读了Schwarz Counters和C++11规范的第3.6.2节(basic.start.init)/6.7节(stmt.decl),但我对编译器特定的行为没有那么多了解,也没有找到其他人发布关于尝试实现这一技巧的帖子。

接受的答案:

正如John在下面所指出的,所有类(可能)都在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();
}

还要注意,对集合使用Meyers单例方法可以完成此方法的总体安全性。但是,可能必须锁定集合,以防止同时对点进行静态初始化。我仍然需要检查规范来确认这方面的规范,以及静态成员初始化是否真的必须在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和ideonehttp://ideone.com/Z3n1U0

这确实需要使用宏,并且要调用doStuff(),必须执行.p.doStuff(。

这是通过定义一个本地类来实现的,该类用作具有静态成员的模板类的参数。通过在函数中引用该模板,可以强制编译器实例化该模板的静态成员。

如果你对这项技术有任何疑问,请告诉我。

您可以这样做:

#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;
}

这将在函数中维护配置文件点容器的单个实例,并在转换单元初始化期间初始化每个配置文件点。