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

时间:2013-12-11 17:52:01

标签: c++ static static-initialization

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计数器和C ++ 11规范的3.6.2(basic.start.init)/ 6.7(stmt.decl)部分,但我对编译器没有太多了解 - 具体的行为,并没有找到任何其他人发布关于试图实现这一伎俩。

接受的答案:

正如John在下面所说,所有类(可能)都在main()之前初始化了静态成员,但是C++11 §9.4.2/5 [class.static.data]和{{1} }禁止本地类中的静态数据成员,在本地类上模板化并具有该类的静态数据成员的类可以在启动时完成初始化。相当精彩的见解,甚至比我初想的更微妙!

§9.8/4 [class.local]

另请注意,使用Meyers单例方法进行收集可以完善此方法的整体安全性。但是,可能必须锁定该集合以防止点的并发静态初始化。我仍然需要检查规范以确认此规范以及静态成员初始化是否实际上强制在main()之前完成。

2 个答案:

答案 0 :(得分:3)

试试这个

#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个配置文件点(但这很容易修复)。

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

如果您对此技术有任何疑问,请与我们联系。

答案 1 :(得分: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;
}

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