简体   繁体   English

指向struct / class的递归成员的指针的变量序列作为模板参数

[英]Variadic sequence of pointer to recursive member of struct/class as template parameter

I'm struggling with some template programming and I hope you can give me some help. 我正在努力进行一些模板编程,希望你能给我一些帮助。 I coded a C++11 interface that, given some structs like: 我编写了一个C ++ 11接口,给出了一些结构,如:

struct Inner{
  double a;
};
struct Outer{
  double x, y, z, r;
  Inner in;
};

Implements a getter/setter to the real data that is customized to the specified struct members: 对实际数据实现getter / setter,该数据是为指定的struct成员定制的:

MyData<Outer, double, &Outer::x,
                               &Outer::y, 
                               &Outer::z,
                               &Outer::in::a //This one is not working
              > state();

Outer foo = state.get();
//...  
state.set(foo);

I managed to implement this for simple structs in the following way: 我设法通过以下方式为简单结构实现此功能:

template <typename T, typename U, U T::* ... Ms>
class MyData{
   std::vector<U *> var;
  public:
    explicit MyData();
    void set(T const& var_);
    T get() const;
};

template <typename T, typename U, U T::* ... Ms>
MyData<T, U, Ms ... >::Struct():var(sizeof...(Ms))
{
}

template <typename T, typename U, U T::* ... Ms>
void MyData<T, U, Ms ...>::set(T const& var_){
  unsigned i = 0;
  for ( auto&& d : {Ms ...} ){
    *var[i++] = var_.*d;
  }
}

template <typename T, typename U, U T::* ... Ms>
T MyData<T, U, Ms ...>::get() const{
  T var_;
  unsigned i = 0;
  for ( auto&& d : {Ms ...} ){
    var_.*d = *var[i++];
  }
  return var_;
}

But it fails when I pass a member of a nested struct. 但是当我传递一个嵌套结构的成员时它失败了。 Ideally, I'd like to implement a generic pointer to member type that allows me to be compatible with several levels of scope resolutions. 理想情况下,我想实现一个指向成员类型的通用指针,它允许我与几个级别的范围分辨率兼容。 I found this approach , but I'm not sure if this should be applied to my problem or if there exists some implementation ready to use. 我找到了这种方法 ,但我不确定这是否应该应用于我的问题或者是否存在一些可以使用的实现。 Thanks in advance! 提前致谢!

Related posts: 相关文章:

Implicit template parameters 隐式模板参数

Pointer to inner struct 指向内部结构的指针

You might wrap member pointer into struct to allow easier chaining: 您可以将成员指针包装到struct中以便更容易链接:

template <typename...> struct Accessor;

template <typename T, typename C, T (C::*m)>
struct Accessor<std::integral_constant<T (C::*), m>>
{
    const T& get(const C& c) { return c.*m; }
    T& get(C& c) { return c.*m; }
};

template <typename T, typename C, T (C::*m), typename ...Ts>
struct Accessor<std::integral_constant<T (C::*), m>, Ts...>
{
    auto get(const C& c) -> decltype(Accessor<Ts...>().get(c.*m))
    { return Accessor<Ts...>().get(c.*m); }

    auto get(C& c) -> decltype(Accessor<Ts...>().get(c.*m))
    { return Accessor<Ts...>().get(c.*m); }
};

template <typename T, typename U, typename ...Ts>
class MyData
{
    std::vector<U> vars{sizeof...(Ts)};

    template <std::size_t ... Is>
    T get(std::index_sequence<Is...>) const
    {
        T res;
        ((Ts{}.get(res) = vars[Is]), ...); // Fold expression C++17
        return res;
    }
    template <std::size_t ... Is>
    void set(std::index_sequence<Is...>, T const& t)
    {
        ((vars[Is] = Ts{}.get(t)), ...); // Fold expression C++17
    }

public:
    MyData() = default;

    T get() const { return get(std::index_sequence_for<Ts...>()); }
    void set(const T& t) { return set(std::index_sequence_for<Ts...>(), t); }

};

With usage similar to 与使用类似

template <auto ...ms> // C++17 too
using Member = Accessor<std::integral_constant<decltype(ms), ms>...>;

MyData<Outer, double, Member<&Outer::x>,
                           Member<&Outer::y>,
                           Member<&Outer::z>,
                           Member<&Outer::in, &Inner::a>
       > state;

std::index_sequence is C++14 but can be implemented in C++11. std::index_sequence是C ++ 14,但可以在C ++ 11中实现。
Folding expression from C++17 can be simulated too in C++11. 在C ++ 11中也可以模拟来自C ++ 17的折叠表达式。
typename <auto> (C++17) should be replaced by typename <typename T, T value> . typename <auto> (C ++ 17)应替换为typename <typename T, T value>

Demo 演示

A generalization of a member pointer is a function that can map T to X& at compile time. 成员指针的泛化是可以在编译时将T映射到X&的函数。

In it isn't hard to wire things up thanks to auto . ,由于auto起来并不困难。 In it gets harder. 它变得更难。 But the basic idea is that you don't actually pass member pointers, you pass types, and those types know how to take your class and get a reference out of them. 但基本的想法是你实际上并没有传递成员指针,传递类型,而且那些类型知道如何获取你的类并从中获取引用。

template<class T, class D, class...Fs>
struct MyData {
  std::array<D*, sizeof...(Fs)> var = {};
  explicit MyData()=default;
  void set(T const& var_) {
    var = {{ Fs{}(std::addressof(var_))... }};
  }
  T get() {
    T var_;
    std::size_t index = 0;
    using discard=int[];
    (void)discard{ 0, (void(
      *Fs{}(std::addressof(var_)) = *var[index++]
    ),0)... };
    return var_;
  }
};

it remains to write a utility that makes writing the Fs... easy for the member pointer case 它仍然是写一个实用程序,使得编写Fs...容易成员指针案例

template<class X, X M>
struct get_ptr_to_member_t;
template<class T, class D, D T::* M>
struct get_ptr_to_member_t< D T::*, M > {
  D const* operator()( T const* t )const{
    return std::addressof( t->*M );
  }
};
#define TYPE_N_VAL(...) \
  decltype(__VA_ARGS__), __VA_ARGS__
#define MEM_PTR(...) get_ptr_to_member_t< TYPE_N_VAL(__VA_ARGS__) >

now the basic case is 现在的基本情况是

MyData< Outer, double, MEM_PTR(&Outer::x), MEM_PTR(&Outer::y) >

The more complex case can now be handled. 现在可以处理更复杂的情况。

An approach would be to teach get_ptr_to_member to compose. 一种方法是教get_ptr_to_member撰写。 This is annoying work, but nothing fundamental. 这是烦人的工作,但没什么根本的。 Arrange is so that decltype(ptr_to_member_t * ptr_to_member_t) returns a type that instances right, applies it, then takes that pointer and runs the left hand side on it. 排列是这样的,以便decltype(ptr_to_member_t * ptr_to_member_t)返回一个实例正确的类型,应用它,然后获取该指针并在其上运行左侧。

template<class First, class Second>
struct composed;

template<class D>
struct composes {};

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
  decltype(__VA_ARGS__) \
  { return __VA_ARGS__; }

template<class First, class Second>
struct composed:composes<composed<First, Second>> {
  template<class In>
  auto operator()(In&& in) const
  RETURNS( Second{}( First{}( std::forward<In>(in) ) ) )
};

template<class First, class Second>
composed<First, Second> operator*( composes<Second> const&, composes<First> const& ) {
  return {};
}

then we upgrade: 然后我们升级:

template<class X, X M>
struct get_ptr_to_member_t;
template<class T, class D, D T::* M>
struct get_ptr_to_member_t< D T::*, M >:
  composes<get_ptr_to_member_t< D T::*, M >>
{
  D const* operator()( T const* t )const{
    return std::addressof( t->*M );
  }
};

and now * composes them. 现在*组成他们。

MyData<TestStruct, double, MEM_PTR(&Outer::x),
                           MEM_PTR(&Outer::y), 
                           MEM_PTR(&Outer::z),
                           decltype(MEM_PTR(&Inner::a){} * MEM_PTR(&Outer::in){})
          > state();

answre probably contains many typos, but design is sound. 回答可能包含许多错别字,但设计是合理的。

In most of the garbage evaporates, like the macros. 大多数垃圾都会像宏一样蒸发掉。

I would use lambda approach to implement similar functionalities in C++17(C++14 is also ok, just change the fold expression): 我将使用lambda方法在C ++ 17中实现类似的功能(C ++ 14也可以,只需更改fold表达式):

auto access_by() {
    return [] (auto &&t) -> decltype(auto) {
        return decltype(t)(t);
    };
}

template<class Ptr0, class... Ptrs>
auto access_by(Ptr0 ptr0, Ptrs... ptrs) {
    return [=] (auto &&t) -> decltype(auto) {
        return access_by(ptrs...)(decltype(t)(t).*ptr0);
    };
}

auto data_assigner_from = [] (auto... accessors) {
    return [=] (auto... data) {
        return [accessors..., data...] (auto &&t) {
            ((accessors(decltype(t)(t)) = data), ...);
        };
    };
};

Let's see how to use these lambdas: 让我们看看如何使用这些lambdas:

struct A {
    int x, y;
};

struct B {
    A a;
    int z;
};

access_by function can be used like: access_by函数可以像:

auto bax_accessor = access_by(&B::a, &A::x);
auto bz_accessor = access_by(&B::z);

Then for B b; 然后是B b; , bax_accessor(b) is bax ; bax_accessor(b)bax ; bz_accessor(b) is bz . bz_accessor(b)bz Value category is also preserved, so you can assign: bax_accessor(b) = 4 . 值类别也会保留,因此您可以指定: bax_accessor(b) = 4

data_assigner_from() will construct an assigner to assign a B instance with given accessors: data_assigner_from()将构造一个赋值器来为给定的访问器分配B实例:

auto data_assigner = data_assigner_from(
        access_by(&B::a, &A::x),
        access_by(&B::z)
     );

data_assigner(12, 3)(b);

assert(b.z == 3 && b.a.x == 12);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM