Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 971
javaLover Avatar asked Apr 27 '17 05:04

javaLover


4 Answers

I'd turn it around and initialise the vector with the "real" objects of type C, let the members field0 .. field2 be of type "reference to C", i.e. C&, and initialise them with the respective elements of the vector. The advantage of this "turn around" is that the various C-elements are placed one after each other in the vector, which is the most compact way and without padding, while still providing "named" data members (i.e. field0..field2) for accessing the elements without exposing the vector member itself.

The custom "static" code can then be done with a comma expression, which lets you place almost arbitrary code. For code that is not allowed in comma expressions, e.g. the declaration of variables, one could still call member functions or use lambda-expressions. So we do not need any dummy members for "static" code executed between the various fields.

The only place where we needed a dummy member is for code to be executed after the last field_x; so the overhead is a single char-value, and the "static" code here is again solved with a comma expression.

See the following code, which demonstrates the approach. Note that you actually do not have to touch class C; member functions/data C::setX,C::print, and int x have been introduced for demonstration purpose only:

class C{
public:
    void ini(){/* some code */}

    // the following methods and data members are actually not necessary; they have been introduced just for demonstration purpose:
    void setX(int _x) { x = _x;  };
    void print() { cout << x << endl; }
    int x;
};

class D{
protected:
    std::vector<C> regis = std::vector<C>(3); //will ini(); later
public:
    C &field0 = (regis[0].setX(5),printAllRegis("at field0:"),regis[0]);
    C &field1 = (regis[1].setX(7),printAllRegis("at field1:"),regis[1]);
    C &field2 = (regis[2].setX(regis[0].x + regis[1].x),printAllRegis("at field2:"),regis[2]);
    char dummy = (cout << "after field2: ", field2.print(), '0');
    D(){ }

    void ini(){
        for(auto ele:regis){ ele.ini(); }
    }

    void printAllRegis(string header) {
        int i=0;
        cout << header << endl;
        for(auto ele:regis){ cout << "  field" << i++ << ":"; ele.print(); }
    }
};

int main() {

    D d;
    /* Output:
     at field0:
       field0:5
       field1:0
       field2:0
     at field1:
       field0:5
       field1:7
       field2:0
     at field2:
       field0:5
       field1:7
       field2:12
     after field2: 12
     */

    return 0;
}
like image 175
Stephan Lechner Avatar answered Oct 24 '22 14:10

Stephan Lechner


Maybe you are looking for a dummy fields initialized by lambda capturing this pointer invoked immediately after declaration:

#include <iostream>

struct C { };

class D{
    int field1=5;
    int dummy1{[&]{ std::cout << field1 << std::endl; return 0;}()};
    int field2=7;
    int field3=8;
    int dummy2{[&]{ std::cout << (field1 + field2 + field3) << std::endl; return 0;}()};
};

int main() {
    D d;
}

Output:

5  
20

[live demo]

like image 23
W.F. Avatar answered Oct 24 '22 13:10

W.F.


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(); }
like image 38
skypjack Avatar answered Oct 24 '22 12:10

skypjack


Looking at this question, it reminds me to my own "Object with properties" concept. I consider it rather as pragmatical than elegant but I dare to present it:

To establish the dependency between Field and Object, I introduced a back-reference to Object in Field. At the cost of an additional member, this has certain conveniencies.

class Field – the base class required to store fields:

class Object; // forward reference

// super class of all object fields
class Field {
  // variables:
  private:
    // parent object
    Object &_obj;
  // methods:
  protected:
    // constructor.
    Field(Object &obj);
    // destructor.
    ~Field();
    // disabled:
    Field(const Field&) = delete;
    Field operator=(const Field&) = delete;
}

The corresponding class Object – the field container:

#include <vector>

// super class of objects which may contain fields
class Object {
  // friends:
  friend class Field;
  // variables:
  private:
    // table of all registered fields
    std::vector<Field*> _pFields;
  // methods:
  protected:
    // constructor.
    Object() = default;
    // destructor.
    virtual ~Object() = default;
    // disabled:
    Object(const Object&) = delete;
    Object& operator=(const Object&) = delete;
};

The implementation of class Field has to "know" both classes:

#include <algorithm>

// implementation of class Field

Field::Field(Object &obj): _obj(obj)
{
  _obj._pFields.push_back(this);
}

Field::~Field()
{
  _obj.erase(
    std::find(
      _obj._pField.begin(), _obj._pField.end(), this));
}

The template for field instances:

// a class template for derived fields
template <typename VALUE>
class FieldT: public Field {
  // variables:
  private:
    // the value
    VALUE _value;
  // methods:
  public:
    // constructor.
    FieldT(Object &obj, const VALUE &value):
      Field(obj), _value(value)
    { }
    // copy constructor.
    FieldT(Object &obj, const FieldT &field):
      Field(obj), _value(field._value)
    { }
    // disabled:
    FieldT(const FieldT&) = delete;
    FieldT& operator=(const FieldT&) = delete;
};

The advantages and disadvantages as considered by me:

Pros:

  1. The object would never register non-fields.

  2. You may never forget to construct fields explicitly because the C++ compiler will remind you in this case. (Fields are not default constructable.)

  3. You may decide in a copy constructor of a derived Object whether to copy the field or initialize it with a constant (or alternative) value instead.

Cons:

  1. It requires explicitly written code in every derived Object which might be considered as tedious. (I, personally, like explicit code to a certain amount to make maintenance easier.)

  2. It is not foolproof. E.g. fields created with new are "begging" for memory leaks. (Although... I never considered to simply delete the operator new(). I've to check out this soon.)

So, how could this look in real-life:

// storage of a 2d vector
struct Vec2f {
  float x, y;
  Vec2f(float x, float y): x(x), y(y) { }
};

// storage of a 2d circle
class Circle: public Object {
  // fields:
  public:
    // center
    FieldT<Vec2f> center;
    // radius
    FieldT<float> radius;
 // methods:
 public:
   // constructor.
   explicit Circle(float x, float y, float r):
     Object(),
     center(*this, Vec2f(x, y)),
     radius(*this, r)
   { }
   // copy constructor.
   Circle(const Circle &circle):
     Object(),
     center(*this, center),
     radius(*this, radius)
  { }
};

Note:

You may wonder why the fields are public – this is intentional. In my real-life incarnation, these fields have a lot more i.e. getter and setter as well as (optional) signal slots to install guardians and modification notifier. My intention was to free me from the tedious writing of access methods in the objects.

In real-life this concept has even more potential:

  • It's sufficient for generic I/O classes which may write and read XML files or other formats.

  • It can be obtained in a second concept of generic GUI classes to simplify the creation of user interfaces in a generic way.

Worth of the costs for administrational extra data:

Some extra bytes seems often enough worth to me if they promise a lot of safed development time. Of course, there are counter examples where I consider every single bit e.g. the vertex data when loading geometry for visual simulation. (For the vertex data example, I never considered a property concept to access every single vertex. Instead, I used the properties to access arrays of vertices as once.)

Concerning the additional requirement of additional construction details:

class D{ int field1=5; { do something very custom; } //^ have to be executed after "field1=5;" but before "field2=7" int field2=7; int field3=8; { do something very custom ; }//have to be executed after "field3=8;" };

I would choose a simple solution which has nothing to do with the above concept:

class D {
  int field1 = 5;
  struct InitBefore {
    InitBefore() {
      // do something very custom;
    }
  } initBefore;
  int field2 = 7;
  int field3 = 8;
  struct InitAfter {
    InitAfter() {
      // do something very custom;
    }
  } initAfter;
  // the rest...
};

Unfortunately, this doesn't fulfill the extra requirement:

without wasting 1 char for each block.

(The reason can be found in SO: sizeof empty structure is 0 in C and 1 in C++ why?.)

May be, it could be solved exploiting the sequence operator and static methods applied to the constructor of the appropriate member. This even seems so ugly to me that I don't dare to sketch code for this.

May be, it could be solved to make an embedded, private derived class of the respective field where the "very custom code" can be added in the constructor. Due to the fact that fields actually have data the empty struct/class problem should be prevented.

Actually, I never cared about the non-zero structs/classes because I needed this trick itself only in rare cases (where only a few resp. instances are expected).

As there are (at the time of writing) still 7 days until the bounty is over I will keep this in mind...

like image 35
Scheff's Cat Avatar answered Oct 24 '22 13:10

Scheff's Cat