开发者

Is there a way of applying a function to each member of a struct in c++?

开发者 https://www.devze.com 2023-01-18 01:42 出处:网络
You have a simple struct, say: struct rect { int x; int y; int width; int height; }; And you want to multiply each element by a factor.Is there a m开发者_如何学Core concise way of performing this ope

You have a simple struct, say:

struct rect
{
    int x;
    int y;
    int width;
    int height;
};

And you want to multiply each element by a factor. Is there a m开发者_如何学Core concise way of performing this operation, other than multiplying each member by the value?


Not really. Programatically obtaining a list of the elements of a struct requires reflection, which C++ does not support.

Your two options are to just give the struct a method that does this the long-winded way and then use that method in all other places, or to manually mimic reflection, for example by giving the struct another element which is an array of pointers to all of its other elements (built within the struct's constructor), and then looping over that to perform the scaling function.


No; there is no way to iterate over the members of a struct.

In this particular case, though, you can accomplish your goal by using an array:

struct rect
{
    // actually store the data in an array, not as distinct elements
    int values_[4];

    // use accessor/mutator functions to modify the values; it is often best to
    // provide const-qualified overloads as well.
    int& x()      { return values_[0]; }
    int& y()      { return values_[1]; }
    int& width()  { return values_[2]; }
    int& height() { return values_[3]; }

    void multiply_all_by_two()
    {
        for (int i = 0; i < sizeof(values_) / sizeof(values_[0]); ++i)
            values_[i] *= 2;
    }
};

(note that this example doesn't make much sense (why would you multiply x, y, height and width by two?) but it demonstrates a different way to solve this sort of problem)


Boost.Fusion offers the BOOST_FUSION_ADAPT_STRUCT macro, which can turn any struct into a Fusion sequence, which Fusion can then iterate over (as demoed in the Quick Start).

Another option is to use a third-party C++ reflection library like Reflex to iterate over a struct's members.

However, all of this may be overkill for your purposes; manually listing each of the class's members may be less elegant, but it's much simpler, and simplicity in coding is important.


If all the values fit into a 128-bit vector, then you could use SIMD instructions to multiply them all at once. For example Intel's Integrated Performance Primitives (IPP) has primitives for addition, subtraction, multiplication etc. on vectors.

It's probably overkill unless you are doing a lot of compute intensive operations.


If all are the same type, use a union combined with an anonymous struct, inside of your struct:

struct MyStruct {
  union {
    int data[4];
    struct {
      int x,y,z,w;
    }
  };
};

then:

MyStruct my_struct;
cout << my_struct.x << my_struct.y << my_struct.z << my_struct.w;
for (size_t i = 0; i < 4; ++i)
  some_function(my_struct.data[i]); // access the data via "data" member
cout << my_struct.x << my_struct.y << my_struct.z << my_struct.w; // voila!, stryct data has changed!

If youre using different types have a look at boost::fusion


Like everyone has said, you can't do it directly, but there's nothing stopping you from overloading the * operator:

struct rect 
{ 
   int x; 
   int y; 
   int width; 
   int height; 
}

rect operator*(const rect& r, int f)
{
  rect ret=r;
  ret.x*=f;
  ret.y*=f;
  ret.width*=f;
  ret.height*=f;
  return ret;
}

then you can do this

rect r;
//assign fields here
r=r*5;


Assuming you are using a C++ compiler (not C), make it a class and create a member function to do this. I am suprised at all the awkward solutions presented to a simple problem. What does a reflective solution buy you? Are you continually adding new members to this class, so you want to save yourself the step of modifying the member function in the future? If that is a seriously a potential time-saver, it smells of a class doing too much.

It is inevitable there will be repeated functionality acting on the members of a structure, so why not make it a class from the start?


Well, not directly. But you can achieve that by using an additional temporary "index" array of pointers of pointer-to-member type.

For example

rect a_rect[100];
int factor;
...
// We need to multiply all members of all elements of `a_rect` array by `factor`

// Prepare index array
int rect::*rect_members[4] = { &rect::x, &rect::y, &rect::width, &rect::height };

// And multiply
for (i = 0; i < 100; ++i)
  for (j = 0; j < 4; ++j)
    a_rect[i].*rect_members[j] *= factor;

Of course, if you need to do it often, you can use a permanent index array rect_members initialized at the program startup.

Note that this method does not employ any hacks, as some other methods do. Pointers of pointer-to-member type are rarely used, but this is actually one of the things they were introduced for.

Again, if you need to do it often, the proper thing to do would be to make the pointer array a static const member of the struct

struct rect
{
  int x;
  int y;
  int width;
  int height;

  static int rect::* const members[4];
};

int rect::* const rect::members[4] = { &rect::x, &rect::y, &rect::width, &rect::height };

and when you need to iterate over these members, you'd just access struct elements as s.*rect::members[i].


if all your elements are of the same type:

for (int i = 0; i < sizeof(rect) / sizeof(rect.x) ; ++i) {
    do_something(reinterpret_cast<int *>(adress_of_your_struct) + i);
}

with do_something expecting a pointer to int


horrific hack

int *arr = (int*)&my_rect;
for (int i = 0; i < sizeof(rect)/sizeof(int); i++)
{
     arr[i] *= factor;
}


A solution via a new class:

  • Create a class with a template<class T> class Array generic array, (see how),
  • Fill the array with your properties, one property as an element of the array.
  • Ensure all types can be multiplied by a numeric value (or overload the * operator for a type if necessary / create a specific method for that object)
  • Create a method multiply that performs a loop on the array and multiplies each element by a given value.


If you can re-arrange your struct to take be a union instead, you can do something similar to what Microsoft has done with DirectX.

union Rect
{
    struct
    {
        int x;
        int y;
        int width;
        int height;
    };

    int members[4];
};

void multiply(Rect& rect, int factor)
{
    for(int i = 0; i < 4; i++)
    {
        rect.members[i] *= factor;
    }
}


There are some issues associated with inheriting from standard classes; but if you're willing to accept those issues, you can do something analagous to what others have suggested, but using std::valarray instead. It would look something like:

#include <valarray>

class rect : public std::valarray<int>
{
  public:
    rect() : std::valarray(4, 0) {}

    int& x() { return (*this)[0]; }
    int& y() { return (*this)[1]; }
    int& width() { return (*this)[2]; }
    int& height() { return (*this)[3]; }
};

With this, you inherit the vector operations defined on std::valarray<int>.

I wouldn't necessarily recommend this approach. I merely suggest that it can be done.

0

精彩评论

暂无评论...
验证码 换一张
取 消