Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
701 views
in Technique[技术] by (71.8m points)

c++ - C++11 enum with class members and constexpr link-time optimization

In my project I have a lot of enumerations that need to have additional attributes associated with the enumeration members and auxiliary static methods associated with the enumeration type.

As much as I know, this is not possible to have with the standard enum class MyItem {...}, so for each enum class in my project I have an auxiliary class MyItemEnum that encapsulates these auxiliary static methods and also instantiates auxiliary instances of itself, so that I can access their methods in order to get additional attributes.

Bellow an example (simplified as much as possible but I believe all the features to be discussed stayed there).

MyItem.h

enum class MyItem : unsigned int {
    Item1   = 1,
    Item2   = 5
};

class MyItemEnum {
private:
    MyItem myItem;
    size_t extInfo;

    MyItemEnum(const MyItem& myItem, size_t extInfo);
    ~MyItemEnum();
public:
    static MyItemEnum Item1;
    static MyItemEnum Item2;
    static const MyItemEnum &get(MyItem myItem);

    operator MyItem() const;
    size_t getExt() const;
    bool hasNext() const;
    MyItem next() const;
};

I think the meaning is obvious and I don't need to provide here the .cpp part... I use the MyItem as an argument to be passed in the interfaces and MyItemEnum when I need to access the extended functionality.

My first question is, is the approach above ok, or I should consider something completely different?

My second question concerns an optimization of this enumeration that I am trying to do using constexpr:

enum class MyItem : unsigned int {
    Item1   = 1,
    Item2   = 5
};

class MyItemEnum {
private:
    MyItem myItem;
    size_t extInfo;

    constexpr MyItemEnum(const MyItem& myItem, size_t extInfo);
public:
    static MyItemEnum Item1;
    static MyItemEnum Item2;
    static constexpr MyItemEnum &get(MyItem myItem);

    constexpr operator MyItem();
    constexpr size_t getExt();
    constexpr bool hasNext();
    constexpr MyItem next();
};

It compiles but apparently the constexpr doesn't get chance to get used because if I access:

MyItemEnum::Item1.getExt()

so the compiler doesn't know what values was Item1 instantiated with. Is there a chance that the expression above will be evaluated as constexpr during the link time optimization? Alternatively I could use

static constexpr MyItemEnum Item1 = MyItemEnum(MyItem::Item1, 123);

This would active the constexpr compile time optimizations but I am afraid that in some cases, when the constexpr is not possible to be compile-time evaluated, the compiler would have to create a local instance of MyItemEnum (instead of using reference to a single global static instance) and I am afraid that this could lead to a performance penalty (my real enums has more attributes than just a single member so the local instantiation can take some time?). Is this a justified concern?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I haven't direct experience with the use of constexpr and the resulting compiler optimizations yet, but I can tell you that simply using const on the members of the class itself or the instances will both VS2012 and g++ 4.7 compilers to do cross-module optimization:

class MyItemEnum {
private:
    // make sure to put const here...
    const MyItem myItem;
    const size_t extInfo;

    MyItemEnum(const MyItem& myItem, size_t extInfo);
    ~MyItemEnum();
public:
    // and put const in here too...
    static const MyItemEnum Item1;
    static const MyItemEnum Item2;
};

The caveat is that the constructor must use the C++ style initializer list syntax, which shouldn't be a problem if you're just populating them with constant values anyway. (initializer lists only become a pain when non-trivial setup is required).

I have not verified this on Clang/LLVM, so if that's your toolchain then I strongly recommend you take this simplified example and disasm the result yourself. Disassembly of simple test cases can be pretty easy to parse even if you're not familiar with assembly languages. And in this case you can compile two builds: one set in a single module, and one split into two modules - and compare the results to make sure LTO is doing the job you need it to.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...