2

Consider the following code.

struct Data {
  int a = 100;
  char* b = nullptr;
};

struct Data2 {
  int c = 100;
  char* d = nullptr;
};

template<typename T>
struct Test {
  T field;
  T field2;
};

int main()
{
  Test<Data> test;
  test.field.a = 500;
  test.field2.a = 500;

  Test<Data2> test2;
  test2.field.c = 1000;
  test2.field2.c = 1000;
}

What I'd like to have is a method or operator to do something like

test.set<a> = {500, 500}
test2.set<c> = {1000, 1000}

Basically I'd like to somehow specify field name, go through all the struct fields and set the values from a given list of values.

I suspect the problem is insolvable with standard C++ (any standard) due to the absence of reflection mechanisms. Am I right with that assumption?

Stack Danny
  • 5,590
  • 1
  • 17
  • 49
Dmitry
  • 671
  • 5
  • 12
  • There is [`magic_get`](https://github.com/apolukhin/magic_get), but otherwise you're right, there's no reflection in C++. – HolyBlackCat Apr 13 '21 at 10:47
  • First thing I thought of was a method like `Test::foreach` that takes a lambda function and passes each field to that lambda on by one. You can then use it with a lambda like `[](auto& field){ field.a = 500; }` – perivesta Apr 13 '21 at 10:49
  • 1
    If you have a bunch of values of the same type, and you need to do the same thing to all of them, you may want to use an array. `T fields[2]` should work. – n. 1.8e9-where's-my-share m. Apr 13 '21 at 10:58
  • @Dmitry do you actually need that instead of a getA() and getB()? – Moia Apr 13 '21 at 11:02
  • @n.'pronouns'm. The actual reason I need this is a bit complicated, but basically it must be two different instances. – Dmitry Apr 13 '21 at 11:02

3 Answers3

2

How about something like this? (utilizing data-member-pointers)

struct Data
{
    int a;
};

template<class T>
struct test
{
    T field1, field2;
    template <class U, U (T::* p)>
    void set(U value)
    {
        field1.*p = value;
        field2.*p = value;
    }

};

int main()
{
    test<Data> t;
    t.set<int, &Data::a>(7);
}

It's a mouthful to write, I agree, but seems to more or less achieve (albeit in a little verbose and reduntant way) what you want. An obvious drawback, one that I can't think off the top of my head how to overcome, is that you need to specify both int and Data:: in your set template arguments, whereas both in principle are known to the compiler.

Update: Here's an alternative, cleaner solution:

struct Data
{
    int a;
};

template<class T>
struct test
{
    T field1, field2;
    template <class U>
    void set(U(T::* p), const U* values)
    {
        field1.*p = *values++;
        field2.*p = *values++;
        //...
    }

};

int main()
{
    test<Data> t;
    int values[] = { 7, 8 };

    t.set(&Data::a, values);
}
Armen Tsirunyan
  • 125,569
  • 56
  • 315
  • 427
0

You can use a member function to set the data, and then specialize it:

template<typename T>
struct Test
{
    T field;
    T field2;

    void set(int value);
};

template<>
void Test<Data>::set(int value)
{
    field.a = value;
    field2.a = value;
}

template<>
void Test<Data2>::set(int value)
{
    field.c = value;
    field2.c = value;
}
int main()
{
    Test<Data> test;
    Test<Data2> test2;

    test.set(500);
    test2.set(1000);
}

While it doesn't really cut down on the code, it kind of make it simpler using the Test structure.

Some programmer dude
  • 380,411
  • 33
  • 383
  • 585
0

You can do something like this:

#include <initializer_list>
#include <iostream>

struct Data {
  int a = 100;
  char* b = nullptr;
};

struct Data2 {
  int c = 100;
  char* d = nullptr;
};

template<typename T>
struct Test {
  T field;
  T field2;
private:
  template<typename Type>
  class Proxy {
  public:
    Proxy(Type T::* ptr, Test *owner) : ptr_(ptr), owner_(owner) {}
    Proxy& operator =(std::initializer_list<Type> list) {
        auto it = list.begin();
        owner_->field.*ptr_ = *it++;
        owner_->field2.*ptr_ = *it;
    }
  private:
    Type T::* ptr_;
    Test *owner_;
  };
public:
  template<typename Type>
  Proxy<Type> set(Type T::* ptr)
  {
    return Proxy<Type>(ptr, this);
  }
};

int main()
{
  Test<Data> test;
  test.field.a = 500;
  test.field2.a = 500;

  Test<Data2> test2;
  test2.field.c = 1000;
  test2.field2.c = 1000;
  ///.....

  // What I'd like to have is a method or operator to do smth like
  // test.set<a> = {500, 500}
  // test2.set<c> = {1000, 1000}
  // ....
  // Basically I'd like to somehow specify filed name,
  // go through all the struct fields
  // and set the values from a given list of values

    test.set(&Data::a) = {600, 600};
    test2.set(&Data2::c) = {1001, 1001};

    std::cout << test.field.a << ", " << test.field2.a << ", " << test2.field.c << ", " << test2.field2.c << "\n";
}
sklott
  • 1,468
  • 2
  • 13