Question

I have few small structs and one big struct that contains them:

struct A
{
...
};
struct B
{
...
};

struct AB
{
A a;
B b;
};

In different place at the code, I get an AB variable and do stuff with AB.a and AB.b. I want to make sure that if someone add a struct to AB he also updated the code that uses AB.

So my question is how can I check that?

I know that I can't trust sizeof and do something like:

 sizeof(AB)==sizeof(a)+sizeof(b)

I should mention that I don't really care what the small structs are, only how many are there in AB.

Any suggestions?

Thanks!

EDIT: if I assuming that A and B contains only primitives, what if I check:

offset(last member of B)+sizeof(last member of B)==sizeof(AB)

What do you think?

Was it helpful?

Solution 3

The solution I found and currently in use is the following check:

if !(sizeof(A+B) + 3 >= sizeof(AB)){
  return error;
}

I have some relaxation assumption which makes this condition a solution for my question:

  1. word size=4
  2. All my small structs (i.e A and B) are big enough and contain a member with size>=4- which ensure eligning of 4 to the big struct
  3. Struct AB contains only structs

In that case, if someone will add to AB a third struct, C, without changing this condition, he'll get an error (even if sizeof(c)<3 because of the aligning). Which is exactly what I wanted.

OTHER TIPS

With C++ a possible option would be to use templates and Boost.Fusion.

We use and an boost::fusion::vector as the aggregate class:

typename boost::fusion::vector<A,B> Aggregate;

Let's assume we have an operation which should be executed on all parts:

struct Operation {
    void operator()(A& a) { ... } // operation for A
    void operator()(B& b) { ... } // operation for B
};

Boost Fusion provides a function for simple iteration over all parts of a type container:

Aggregate x;
boost::fusion::for_each(x, Operation());   

If you now extend the aggregate to boost::fusion::vector<A,B,C> you will get a compiler error when you do not provide an overload of Operation::operator() for type C.


Working example:

#include <iostream>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/at_c.hpp>

namespace bf = boost::fusion;

struct A { int a; };
struct B { float q; };

typedef bf::vector<A,B> MyClass;

struct IncrementOp {
    void operator()(A& x) const { x.a ++; }
    void operator()(B& x) const { x.q += 1.0f; }
};

int main() {
    MyClass c;
    bf::at_c<0>(c).a = 0;
    bf::at_c<1>(c).q = 4.2f;
    bf::for_each(c, IncrementOp());
    std::cout << bf::at_c<0>(c).a << std::endl;
    std::cout << bf::at_c<1>(c).q << std::endl;
}

This does not answer your question directly, but illustrates how change to a struct can be managed without concern due to adding members over time.

(Note, these ideas do NOT apply when REMOVING members)

A substantial benefit of using the struct construct (over using discrete types) is precisely what you are asking about: ability to add members without breaking your code The struct type can insulate legacy revisions of code from the need to add additional required variables. That is, you can add members to a struct that is defined globally, without forcing change to the prototype of any function that includes it in its argument list.

For example: given your scenario with the following structs:

(Note, I am using typedef so that the declarations will be shorter in argument list, and added real members)

Here is your original scenario:

typedef struct 
{
    int a1;
    int a2;
}A;

typedef struct 
{
    int b1;
    int b2;

}B;

typedef struct   //small change for less text in prototypes
{
    A a;
    B b;
}AB;  

Here are two functions that use the struct AB

This one was written for use before any modifications to original AB: (and does not currently need new member)

void func1(AB var_old)
{
    var_old.a.a1 = 3;
    var_old.a.a2 = 3;
    var_old.b.b1 = 4;
    var_old.b.b2 = 4;
    //Note:  if var_old.b.b3 is ever needed here, it is available without changing prototype
}

sometime later a function is created (or modified) that requires a new variable

Add member to struct B:

typedef struct 
{
    int b1;
    int b2;
    int b3;

}B;  

Use it here:

void func2(AB var_new)
{
     var_new.a.a2 = 10;//pre-existing member
     var_new.b.b3 = 4; //new member
}

The prototype argument list did not change, avoiding the need to edit every function that uses AB.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top