Improved conversion functions

I have various classes in C ++ 0x framework and I would like to write functions to convert between some of them. For example:

struct Foo { float v; };
struct Bar { int i; };
struct Tar { float p, q; };

void Convert(const Foo& x, Bar& y) { y.i = static_cast<int>(x.v); }
void Convert(const Tar& x, Foo& y) { y.v = x.p + x.q; }

      

This is just an example. There are quite a few "small" classes. And not all conversion functions make sense.

In addition, there are some classes that essentially behave like STL containers and must "inherit" these transform functions.

void Convert(const std::vector<Foo>& cx, std::vector<Bar>& cy) { ... }
void Convert(const std::vector<Tar>& cx, std::vector<Bar>& cy) { ... }

      

Not. I am looking for an easy way to define theses functions. I tried:

template<typename X, typename Y>
void Convert(const std::vector<X>& cx, std::vector<Y>& cy) {
   cy.resize(cx.size());
   for(std::size_t i=0; i<cx.size(); i++) {
     Convert(cx[i], cy[i]);
   }
}

      

and it works great.

However, with this setup, you need to write

std::vector<X> cx = { ... };
std::vector<Y> cy;
Convert(cx, cy);
// when not specifying the type, one needs to use this form f(X, &Y)

      

With type setting

template<typename X, typename Y>
std::vector<Y> Convert(const std::vector<X>& cx) {
   std::vector<Y> cy(cx.size());
   for(std::size_t i=0; i<cx.size(); i++) {
     cy[i] = Convert(cx[i]);
   }
   return cy;
}

      

need to write

std::vector<X> cx = { ... };
std::vector<Y> cy = Convert<X,Y>(cx);
// can I avoid specifying the source type with this form?

      

Of course, at some point you need to specify the target type, but the source type is determined by the function parameter. I do not want to mention this over and over again.

Is there an elegant general way to handle conversion functions like this?

Edited clarification question

+3


source to share


4 answers


Actually, I found out how to do it. Reordering template arguments

template<typename Y, typename X>
std::vector<Y> ConvertTo(const std::vector<X>& cx) {
   std::vector<Y> cy(cx.size());
   for(std::size_t i=0; i<cx.size(); i++) {
     cy[i] = Convert(cx[i]);
   }
   return cy;
}

      

can write



std::vector<Y> cy = ConvertTo<Y>(cx);

      

and the compiler automatically passes the second type of template for input. Nice feature!

0


source


Do you consider using ctors:

struct Bar {
   int i;

   explicit Bar(Foo const &f) : i(static_cast<int>(f.v)) {}
};

      

Then you can do:

Bar x = static_cast<Bar>(some_foo);

      



The question may arise as to whether you really want to make the ctor explicit. If you eliminate it (allow implicit conversion) the vector conversion becomes trivial:

std::vector<foo> foos;

// code to populate foos ...

std::vector<Bar> bars((foos.begin()), foos.end());

      

As for whether to make it explicit or not, the question is whether you could accidentally get a conversion from Foo to Bar, allowing something you don't really need. This is a fairly common problem with things like converting to bool

, but much less common with converting from one custom type to another. In this case, leaving explicit

off can be convenient and reasonably safe.

+1


source


Why not just use constructors for this purpose?

explicit Bar(const Foo& x) : i(static_cast<int>(x.v)) {}

      

+1


source


Because of the Return Value Optimization, you don't need to use the out parameter, just use the return value:

void Convert(const X& x, Y& y);

      

becomes:

Y Convert(const X& x);

      

now you can use it in an expression.

Also consider converting constructors or conversion operators instead :

Y::Y(const X&);
X::operator Y();

      

They will allow implicit conversion between types (if that's what you want - otherwise you can declare them explicitly).

Ranges of iterators are used to handle STL containers:

template<class FwdIterator>
Y Convert(FwdIterator begin, FwdIterator end);

      

In this style, it will not only be compatible with all STL containers, but it will also be compatible with raw pointer ranges.

Also note if you are declaring implicit conversion constructors or conversion operators than you can use std :: copy:

vector<X> xs = ...;
vector<Y> ys;
copy(xs.begin(), xs.end(), back_inserter(ys.begin()));

      

This will trigger your user defined conversions when you explicitly translate elements from xs to ys.

0


source







All Articles