4

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

我已经阅读了 C++11 规范的 Schwarz Counters 和第 3.6.2 (basic.start.init) / 6.7 (stmt.decl) 部分,但我对编译器特定的行为和找不到其他人发布有关尝试实现此技巧的信息。

接受的答案:

正如约翰在下面指出的那样,所有类(可能)在 main() 之前初始化其静态成员,但鉴于此C++11 §9.4.2/5 [class.static.data]§9.8/4 [class.local]禁止本地类中的静态数据成员,该类在本地类上模板化并具有该类的静态数据成员可以在 start-time 完成其初始化。相当绝妙的洞察力,甚至比我最初想象的还要微妙!

// 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() 之前完成。

4

2 回答 2

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

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

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

于 2013-12-11T18:36:38.713 回答
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;
}

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

于 2013-12-11T19:13:45.920 回答