tags:

views:

113

answers:

4

I have inherited a bunch of networking code that defined numerous packet types. I have to write a bunch of conversion functions that take structs of a certain type, and copy the values into other structs that have the same fields, but in a different order (as part of a convoluted partial platform bit order conversion thing -- don't ask). Also, I know that there may be better ways of expressing the conversion, etc. below, but I'm not concerned with those at the moment. Particularly, I cannot make convert take its output variable by reference, because I will be passing in bitfields, and that generates a compiler error.

So there are a bunch of structs like this:

struct foo {
   int bar;
   int baz;
};
struct foo_x86 {
   int baz;
   int bar;
};

And a bunch of functions like this, to convert between the two:

foo_x86 convert(const foo& in) { foo_x86 out; out.bar = in.bar; out.baz = in.baz; return out; }

This is all no big deal. The problem I have is this: there is also a template struct that looks something like this:

template <class T>
struct Packet {
   HeaderType head;
   T          data;
};

There are a number of instantiations of this template, using packet types above as the template parameters, for example:

struct superfoo {
   Packet<foo> quux;
};
struct superfoo_x86 {
   Packet<foo_x86> quux;
};

Now, assuming that there exists a function foo_x86 convert(const foo&); is there any way to create a template function for handling Packet objects that calls this convert function?

For example, I want something that looks sort of like this:

template <class type_1, class type_2>
Packet<type_2> convert(const Packet<type_1>& in) {
   Packet<type_2> out;
   out.head = in.head;
   out.data = convert(in.data);
   return out;
}

That would work in a function like:

superfoo_x86 convert(const superfoo& in) {
   superfoo_x86 out;
   out.quux = convert(in.quux);
   return out;
}

I want to be able to convert Packet objects without caring what type they are instantiated with, and I want to avoid having to declare separate convert functions for every possible Packet instantiation.

Is there anyway to do with with templates in C++?

+2  A: 

I am not sure i understand your question correctly.

I suggest you modify your last "convert" function to :

template<typename Type1, typename Type2>
void convert(const Type1& in, Type2& out) 
{
   convert(in.quux, out.quux);
}

and the one before that :

template <class type_1, class type_2>
void convert(const Packet<type_1>& in, Packet<type_2>& out) 
{
   out.head = in.head;
   convert(in.data, out.data);
}

And at last, define specializations for fundamental types.

template<>
void convert( const int& in, int& out )
{
  out = in;
}

To be tested, but it should work fine.

Benoît
I should clarify that my convert functions cannot be pass by reference for the out parameter, because some of the fields are bit-fields.
Schamp
A: 

How about calling the conversion like this ?

out.data = convert<type_1, type_2>(in.data);
da_m_n
This conversion works, but it means my auto-generating scripts require a special case for the template conversion functions. I'm hoping to avoid this.
Schamp
A: 

Out of curiosity, would conversion constructors be at all helpful?

struct foo_x86;
struct superfoo_x86;

struct foo {
   int bar;
   int baz;
   explicit foo(const foo_x86& from);
};
struct foo_x86 {
   int baz;
   int bar;
   explicit foo_x86(const foo& from) : bar(from.bar), baz(from.baz) {}
};
foo::foo(const struct foo_x86& from) : bar(from.bar), baz(from.baz) {}

template <class T>
struct Packet {
   HeaderType head;
   T          data;
   template <class S>
     explicit Packet(const Packet<S>& from) : head(from.head), data(from.data) {}
};
struct superfoo {
   Packet<foo> quux;
   explicit superfoo(const superfoo_x86& from);
};
struct superfoo_x86 {
   Packet<foo_x86> quux;
   explicit superfoo_x86(const superfoo& from) : quux(from.quux) {}
};
superfoo::superfoo(const superfoo_x86& from) : quux(from.quux) {}

It seems they'd solve the problem with automatically deducing template arguments. It might make sense for you to skip the explicit specifiers.

outis
That's a good idea, I hadn't thought of it. However, I am trying to avoid modifying the original structs as much as I can. I ended up with a different solution, see my own answer.
Schamp
+1  A: 

I ended up doing something like what outis suggested in the comment on the original question. First, I created an unspecialized conversion class template, like so:

template <class type_1>
struct conversion {};

I then threw in a macro for specializing this class (which I believe is like the traits template outis suggested, right?), like so:

#define GEN_CON(type_1, type_2) \
    template <> struct conversion<type_1> { typedef type_2 type; }

Then, for every conversion function from foo to foo_x86, I added the following macro call in the header after the convert function definition::

foo_x86 convert (const foo&);
GEN_CON(foo, foo_x86);

This instantiates a conversion template for foo and foo_x86, like so:

template <>
struct conversion<foo> { typedef foo_x86 type; }

Now, I can refer to conversion::type to get foo_x86.
So, finally, I can create my Packet conversion function, referring to the conversion class, like so:

template <class type_1>
Packet< typename conversion<type_1>::type > convert(const Packet<type_1>& in) {
   Packet< typename conversion<type_1>::type > out;
   out.head = convert(in.head);
   out.data = convert(in.data);
   return out;
}

This allowed the compiler to resolve references to Packet convert(const Packet& in) just fine, without having pass the template parameters into the call (like da_m_n suggested).

Thanks for all your suggestions!

Schamp