C++: Access class member by member-like method

76 views Asked by At

I have template class which looks like this:

template<int n=3> struct Vec{
     double Values[n];
};

Of course I can access elements of this class directly or by method, for example:

double& x(){
    return Values[0];
}

But if I would like to make more intuitive code: instead of

Vec<3> v;
v.x() = 5.2;

I would like to let user do it by: v.x = 5.2. It's not only a whim. If I have existing library which uses simple vector structures of a form like struct{double x,y,z;}, I could by method of duck-typing create my template class to be efficient in this case. Of course I could (probably, I'm not sure) pass to this library predefined structure of referenes - like: struct{double &x=v.Values[0], &y=v.Values[1], &z=v.Values[2];} but I am afraid that's not the simplest way to get a goal.

Ah - of course I could add reference-type parameters in my structure, but this would cause in increase of a size of elements.

1

There are 1 answers

0
Richard Hodges On

Here's one way to get the same syntactic effect:

template<int n=3> struct Vec{
     double Values[n];
};

template<int N> struct xyzAdapter;

template<> struct xyzAdapter<3>
{
  xyzAdapter(Vec<3>& vec) : x(vec.Values[0]), y(vec.Values[1]), z(vec.Values[2]) {}

  double& x;
  double& y;
  double& z;
};

template<int N> auto make_adapter(Vec<N>& vec) 
{
  return xyzAdapter<N>(vec);
}

int main()
{
  Vec<3> point;
  auto adapter = make_adapter(point);
  adapter.x = 6;
}

Going the other way is not quite so pleasant. There is no such thing as an array of references, so one workaround is to resort to an array of std::reference_wrapper:

#include <tuple>
#include <array>
#include <functional>
#include <iostream>

template<int N = 3> struct Vector;

template<> struct Vector<3>
{
    double x, y, z;

    auto as_tuple() {
      return std::tie(x, y, z);
    }
};

template<std::size_t...Is, class Tuple>
auto as_array(std::index_sequence<Is...>, Tuple&& t)
{
  return std::array<std::reference_wrapper<double>, sizeof...(Is)> {
    std::get<Is>(t)...
  };
}

template<int N> auto as_array(Vector<N>& v)
{
  return as_array(std::make_index_sequence<N>(), v.as_tuple());
}

int main2()
{
  Vector<3> point;
  point.x = 6;

  auto arr = as_array(point);

  for (auto ref : arr) {
    std::cout << ref.get() << std::endl;
  }

  return 0;
}