Assigning a template-generated class to a C struct with the same layout

心不动则不痛 提交于 2019-12-25 14:13:37

问题


If I understand correctly, the object ’A’ defined thus:

typedef struct {
    int n;
    float *p;
} myStruct;
myStruct A;

is an aggregate with exactly the same layout in memory as the object ‘B’ defined as:

template <typename T> class myTemplateClass
{
public:
    int n;
    T*  p;
};
myTemplateClass<float> B;

So, is there a more elegant way of assigning

A = B;

than having to write

A = *(reinterpret_cast< myStruct *>(&B));

every time?

My reason for asking is that I have to call a library function which exposes an interface with arguments of the form ‘myStruct’, from code where holding my data in the form of myTemplateClass is a great deal more natural.


回答1:


This requires a bit of boilerplate. Two functions per myStruct, and two functions per template.

In the namespace of myStruct inject these two functions:

auto members( myStruct& s ) {
  return std::tie(s.n, s.p);
}
auto members( myStruct const& s ) {
  return std::tie(s.n, s.p);
}

in C++11 you have to add a decltype clause to give the return value explicitly. Basically tie the members in the exact order declared.

In the body of the myTemplateClass, declare a friend function members that does something similar:

template <typename T>
class myTemplateClass {
public:
  int n;
  T*  p;
  friend auto members( myTemplateClass<T>& self ) {
    return std::tie( self.n, self.p );
  }
  friend auto members( myTemplateClass<T> const& self ) {
    return std::tie( self.n, self.p );
  }
};    

finally, write assign:

template<class Lhs, class Rhs>
void assign_by_members( Lhs& lhs, Rhs const& rhs ) {
  members(lhs) = members(rhs);
}

and we are done.

Anyone that declares a free function members that returns something assignable to the other members works. tie does element-wise assign on references, so all is good.

Note that only the one being assigned from needs the const& overload of members and only the one being assigned to needs the & overload of members. So if assignment always goes from template class to C-struct, you can halve that boilerplate.

If you don't like the assign_by_members syntax, you can override operator O as follows:

template <typename T>
class myTemplateClass {
public:
// ... see above for code that goes here
  template<class O,class=decltype(members(std::declval<O>())>
  operator O() const {
    O retval;
    assign_by_members( retval, *this );
    return retval;
  }
};

which also does a test to determine if the type converted to supports members. You could go a step further and test if the members return value can be assigned to from the return value of members(*this), but that adds more boilerplate.




回答2:


You could make myTemplateClass<T> derive from the correct myStruct depending on the type parameter. You can use template specialization for this:

template <typename T> class myTemplateClass;

// Specialization for float
template <> class myTemplateClass<float> : public myStruct {};

// Specialization for int
template <> class myTemplateClass<int> : public myOtherStruct {};

// And so on for other types...

This way, you can assign instances of myTemplateClass<float> to myStruct, and myTemplateClass<int> to myOtherStruct. (Bonus: You don't have to rely on the "same memory layout" guess.)




回答3:


If you derive from mystruct, then, u can use a static_cast on it. As suggested above, template specialization would work too. I would take static_cast over reinterpret_cast any day.

Below is the working code.

typedef struct {
    int n;
    float *p;
} myStruct;
myStruct A;



template <typename T> class myTemplateClass:public myStruct
{
public:
    int n;
    T*  p;
};
//myTemplateClass<float> B;

typedef myTemplateClass<float> temp;


temp B;

int main()
{


    A = *(static_cast< myStruct *>(&B));


来源:https://stackoverflow.com/questions/28157895/assigning-a-template-generated-class-to-a-c-struct-with-the-same-layout

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!