Search code examples
c++initializationc++14non-static

Can I place "non-static blocks" of code in class definitions?


Is there non-static block in C++?

If no, how to emulate it elegantly?

I want to replace something like :-

class C{
    public: void ini(){/* some code */}
};
class D{
    std::vector<C*> regis; //will ini(); later
    public: C field1; 
    public: C field2;  
    public: C field3;             //whenever I add a new field, I have to ... #1
    public: D(){
        regis.push_back(&field1);
        regis.push_back(&field2);
        regis.push_back(&field3); //#1 ... also add a line here
    }
    public: void ini(){
        for(auto ele:regis){
            ele->ini();
        }
    }
};

with :-

class D{
    std::vector<C*> regis;                        
    public: C field1;{regis.push_back(&field1);}//less error-prone (because it is on-site)
    public: C field2;{regis.push_back(&field2);}
    public: C field3;{regis.push_back(&field3);}
    public: D(){    }  //<-- empty
    public: void ini(){
        for(auto ele:regis){
            ele->ini();
        }
    }
};

I found many questions related to static-block in C++, but didn't found any one about non-static-block.

To make it easy to answer, here is a full code.

It can be done using X-MACRO (wiki link), but I am trying to avoid it.

Edit

In real case, fieldX can has any types that derived from a certain C.

I consider another bad workaround :-

class D{
    std::vector<C*> regis;     
    char f(C& c){   regis.push_back(&c); return 42;}                 
    public: C field1; char dummyWaste1=f(field1);
    public: C field2; char dummyWaste2=f(field2);
    public: C field3; char dummyWaste3=f(field3);

Edit2 (bounty reason)

skypjack's answer is very useful, but I am curious to find out more alternatives.
The final objective is to emulate general non-static block that has more variety.
In other words, it would be nice if new solution can solve this :-

class D{
    int field1=5;
    { do something very custom; /* may access field1 which must = 5 */}
    //^ have to be executed after "field1=5;" but before "field2=7"
    int field2=7;
    int field3=8;
    { do something very custom ; /* e.g. "field1=field2+field3" */}
    //^ have to be executed after "field3=8;"
};

without wasting 1 char (or more - for alignment) for each block.


Solution

  • how to emulate it elegantly?

    You can initialize regis directly:

    std::vector<C*> regis = { &field1, &field2, &field3 };
    

    That is, define your class as:

    class D{
    public:
        C field1;
        C field2;
        C field3;
    
        void ini(){
            for(auto ele:regis){
                ele->ini();
            }
        }
    
    private:
        std::vector<C*> regis = { &field1, &field2, &field3 };
    };
    

    Otherwise, if you can add a constructor to C, revert the logic and have it adding itself to the vector:

    #include<vector>
    
    struct C {
        C(std::vector<C*> &vec) {
            vec.push_back(this);
            // ...
        }
    
        void ini() {}
    };
    
    class D{
        std::vector<C*> regis{};
    
    public:
        C field1 = regis;
        C field2 = regis;
        C field3 = regis;
    
        void ini(){
            for(auto ele:regis){
                ele->ini();
            }
        }
    };
    
    int main() { D d{}; d.ini(); }
    

    ------ EDIT ------

    As requested in the comments:

    C is a holy class for me. Is it possible to not hack C?

    Here is a possible alternative that doesn't require you to modify C:

    #include<vector>
    
    struct C {
        void ini() {}
    };
    
    struct Wrapper {
        Wrapper(std::vector<C*> &vec) {
            vec.push_back(*this);
            // ...
        }
    
        operator C *() { return &c; }
    
    private:
        C c;
    };
    
    class D{
        std::vector<C*> regis{};
    
    public:
        Wrapper field1{regis};
        Wrapper field2{regis};
        Wrapper field3{regis};
    
        void ini(){
            for(auto ele:regis){
                ele->ini();
            }
        }
    };
    
    int main() { D d{}; d.ini(); }