0

In my code I use the following type of struct that is initialized at program start.

struct Parameters
{
    Parameters()
    {
         bla = false;
         foo = false;
         bar = false;
         // and so on ...    
    }
    bool bla;
    bool foo;
    bool bar;
    // and so on ...
};

The actual struct in my program contains about 100 boolean variables. During runtime the parameters of some variables may changed to true.

I would like to know if there is a simple way to check if at least one of the variables of my struct is true. Does C++ provide the functionality to iterate over a struct so that I can avoid checking about 100 variables manually?

I imagine something like

for (bool item : Parameters)
{
    if (item == true)
    {
        return true;
    }
}
Gilfoyle
  • 3,282
  • 3
  • 47
  • 83
  • 1
    No, there is no such functionality, as it would rely on reflection (which C++ doesn't have). And why do you have so many individual boolean variables instead of storing them in a map or array? – UnholySheep Aug 06 '20 at 07:47
  • Why not `enum member_names{...}; struct Parameters{bool member[100];}`? – user202729 Aug 06 '20 at 07:47
  • You can still loop over them if you list all their names (see pointer-to-member) – user202729 Aug 06 '20 at 07:48
  • Can you decide what the struct looks like? Why not use bit fields? – Carlos Aug 06 '20 at 07:54
  • "The actual struct in my program contains about 100 boolean variables. During runtime the parameters of some variables may changed to true." If setting vars inside the struct is dne by setters, you can have a cache var which counts the current set true elements. This will waste same memory but may speed up your prog a lot. Depends on frequency of actions... – Klaus Aug 06 '20 at 08:06
  • @Carlos I have to use the struct like this. – Gilfoyle Aug 06 '20 at 08:08
  • @Klaus I don't quite understand. Can you elaborate on that a bit more? – Gilfoyle Aug 06 '20 at 08:10
  • If you access your struct by obj.set( PARM6, true ); you can have in the set method a `count_true++` if parm is set to true. After that you have no need to compare each individual bool anymore. – Klaus Aug 06 '20 at 08:12
  • This is a bad design. If you used `std::bitset` instead, you could easily do the check with its `any()` member function. Moreover, if you want to add semantics to individual bits, just provide particular accessor member functions. – Daniel Langr Aug 06 '20 at 08:14
  • @DanielLangr Can you please provide an example? – Gilfoyle Aug 06 '20 at 08:16
  • 1
    @I_told_you_so https://godbolt.org/z/srz86T – Daniel Langr Aug 06 '20 at 08:22
  • Note that if your `struct ` is _trivially-copyable_ and consists only of those `bool` members, you can check the byte representation for nonzeros (see, for example, [this question](https://stackoverflow.com/q/6938219/580083)). However, this might be kind-of a fragile solution, though there should not be any padding issues with `bool`s. – Daniel Langr Aug 06 '20 at 08:32

1 Answers1

-1

Edit: I have completely changed this answer as I have been told the previous answer could lead to problems on some compiler settings.

The way you do this is by creating a pointer to member array of all struct members. This way you can easily loop through them all. Of course this isn't ideal because when you have a lot of members in a struct it can get quite long so I would suggest using a boolean map but if you really want a struct you do it like this.

First create a global pointer to member array of all members:

constexpr bool yourstruct::* arrName[<yourNumberOfElements>] = {
  &yourstruct::elem1,
  &yourstruct::elem2,
  &yourstruct::elem3,
  // continue until you have all your members here
};

Then you can create a function like this:

bool isAtLeastOneTrue(struct yourstruct* p1, int size){
  for(int i = 0; i<size;i++){
    if(p1->*arrName[i])
      return true;
  }
  return false;
}

And then you can call the function to see if any member is true.

isAtLeastOneTrue(&yourstructInstance, <yourStructSize>)

Here is some an example of how to use this

struct test{
  test(){
    a1 = false;
    a2 = false;
    a3 = false;
    a4 = false;
    a5 = false;
    a6 = false;
    a7 = false;
    a8 = false;
    a9 = true;
    a10 = false;
    a11 = false;
  }
  bool a1;
  bool a2;
  bool a3;
  bool a4;
  bool a5;
  bool a6;
  bool a7;
  bool a8;
  bool a9;
  bool a10;
  bool a11;

};

constexpr bool test::* ptma[11] = { // pointer to member array
  &test::a1,
  &test::a2,
  &test::a3,
  &test::a4,
  &test::a5,
  &test::a6,
  &test::a7,
  &test::a8,
  &test::a9,
  &test::a10,
  &test::a11
};



bool isAtLeastOneTrue(struct test* p1, int size){
  for(int i = 0; i<size;i++){
    if(p1->*ptma[i])
      return true;
  }
  return false;
}


int main() {
  test a;
 
  cout << isAtLeastOneTrue(&a, 11) << endl;
}
Marko Borković
  • 1,884
  • 1
  • 7
  • 22
  • Can you please provide an example? – Gilfoyle Aug 06 '20 at 08:17
  • @I_told_you_so I have edited the answer with the example – Marko Borković Aug 06 '20 at 08:18
  • Are there any drawbacks using this approach? – Gilfoyle Aug 06 '20 at 08:19
  • 3
    @I_told_you_so Yes. This will work only if the struct is filled with bools and nothing else. This code is essentially treating the struct as a bool array. – Marko Borković Aug 06 '20 at 08:20
  • Can you please explain why `(bool*)&a` works? Why is a pointer to the struct not enough? – Gilfoyle Aug 06 '20 at 09:10
  • 1
    @I_told_you_so We are casting it to a bool* because we want it to work for any struct that needs to be checked. What if you had two different struct definitions with different number of values. Then you would need to create a function for each of them. If we do it like this tho we just need to cast any struct that we want to a bool* – Marko Borković Aug 06 '20 at 09:13
  • DO NOT DO THIS, pointer arithmetic is *undefined* on non-array objects, see this insightful [question and its answers](https://stackoverflow.com/questions/48463521/aliasing-struct-and-array-the-c-way) for workarounds – kmdreko Aug 06 '20 at 11:32
  • This code might work by accident, but it is not required to behave the way you expect. There is no requirement that adjacent named values be laid out as if they were in an array. – Pete Becker Aug 06 '20 at 12:54
  • @PeteBecker As I said. I'm not sure what the best answer is. altough I have never seen it not work. since from my understanding structs just define how to interpret bytes at a certian location. thus if we define a struct like this and have nothing else in there it should work. – Marko Borković Aug 06 '20 at 12:58
  • @Buffer -- "I have never seen it not work" -- it will fail when you demonstrate your program to your most important customer. All the handwaving in the world ("interpret bytes at a certain location" etc.) doesn't change the fact that this is not required to work. Yes, you could get away with it if you rigorously test it and ensure that the behavior is what you expect. And that means retesting every time you change compiler versions or compiler options. – Pete Becker Aug 06 '20 at 13:02
  • @PeteBecker I really don't understand what can go wrong here even if you change the compiler or options. Structs are structs. If you define a memory structure I don't see what can change here. But that is probably because I don't have that much experience with c++. – Marko Borković Aug 06 '20 at 13:09
  • 1
    There are very few constraints on how compilers can lay out structs. It's up to the compiler to decide whether to put extra space between members, for example. Compilers will typically have an option to adjust padding; you can go for smallest size (i.e., no padding) or fastest execution(padding to put members on hardware boundaries that can be accessed faster by the CPU). The main point is that the C++ standard defines an **abstract machine**; reasoning about what that machine does under the covers (i.e., outside of what the standard explicitly requires) will get you in trouble. – Pete Becker Aug 06 '20 at 13:15
  • @PeteBecker I edited the answer. Is it correct now? – Marko Borković Aug 07 '20 at 06:43