0

I have a template class which has a lot of variables, all of the same type

template<typename T>
struct Location
{
    T lat;
    T lon;
    T alt;
    // and roughly 20 variables more of type T
};

In my code T is either float or double, depending on the precision I need. Sometimes I need to cast between these two struct. Thus I want to define a conversion operator

template<typename A, typename B> operator Location<B> {
    Location<B> ret;
    // cast every variable in *this from type A to type B
    return ret;
}

Since there are a lot of variables in Location and it is very likely that I will need to add more variables to the struct in future, I do not want to write each cast by hand in my conversion operator.

Thus, I want to know whether there is some automagically way to cast all variables in the struct?

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
tommsch
  • 582
  • 4
  • 19

4 Answers4

2

I'm afraid there's not such a way. I would probably write templated copy constructor and assign each variable manually. You'll do it just once now, and then everytime you add a new variable - it's not a big deal IMO.

Jiri Volejnik
  • 1,034
  • 6
  • 9
1

You cannot do that natively.

one function that you can write, which might be useful in another contexts (such as comparison) is a tie/as_tuple method:

template<typename T>
struct Location
{
    T lat;
    T lon;
    T alt;
    // and roughly 20 variables more of type T

    auto as_tuple() const { return std::tie(lat, lon, alt /*, ..*/); }

    template <typename T2> operator Location<T2>() const
    {
        return std::make_from_tuple<Location<T2>>(as_tuple());
        // return std::apply([](auto... args){ return Location<T2>{static_cast<T2>(args)...}; },
        //                   as_tuple());
    }
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

Personally i never had this usecase, but if all entries in that struct are of the same type shouldn't something like the following work?

template<typename T>
struct Location {
    T a;
    T b;
    T c;

    template<typename B> operator Location<B>() {
        if constexpr(std::is_same_v<T,B>) {
            return *this;
        }
        Location<B> ret;

        constexpr std::size_t count = sizeof(Location<T>)/sizeof(T);
        static_assert(sizeof(Location<B>)==sizeof(B)*count);
        for (std::size_t i=0;i<count;++i){
            B b;
            T a;
            memcpy(&a, ((char*)this)+i*sizeof(T), sizeof(T));
            b = static_cast<B>(a);
            memcpy(((char*)&ret) + i*sizeof(B), &b, sizeof(B));
        }
        return ret;
    }
};

I'm quite sure that somewhere in that monstrosity is UB or at least issues with memory alignment.

This obviously won't work with pointers or types/structs which contain pointers.

SchweizS
  • 44
  • 4
0

There is no good way to do this... but here is a completely terrible way that should never be used and works only if Location contains only T instances. There may also be alignment issues.

template<typename B>
operator Location<B>() const {
    Location<B> ret;
    for (int i = 0; i <= (sizeof(Location<T>) / sizeof(T)); i++) {
        *(&ret.lat + i) = B(*(&lat + i));
    }
    return ret;
}
Dean Johnson
  • 1,682
  • 7
  • 12
  • the issue here is aliasing, not alignment because those classes are always aligned correctly just like an array – phuclv Apr 01 '21 at 09:13