How to generalize conversion from std::array to struct with raw array member in C++?
Asked Answered
T

2

5

In C++ I have this struct from C. This code is very old and cannot be modified:

struct Point {
   double coord[3];
};

On the other hand, I have this modern function which returns modern std::array instead of raw arrays:

std::array<double, 3> ComputePoint();

Currently, to initialize a Point from the returned value, I manually extract each element from the std::array:

std::array<double, 3> ansArray{ComputePoint()};
Point ans{ansArray[0], ansArray[1], ansArray[2]};

This solution is feasible because there are only three coordinates. Could I templatize it for a general length? I would like something similar to the opposite conversion: std::to_array.

Tetramethyldiarsine answered 19/12, 2023 at 8:56 Comment(0)
C
6

You can use std::apply to do it for any length:

#include <array>
#include <tuple>

Point p = std::apply([](auto... args) {
    return Point{args...};
}, computePoint());

If you run into this pattern a lot, it could be worth making a convenience function template:

template <typename T, typename U>
T make_from_apply(U&& from) {
    return std::apply([]<typename... Args>(Args&&... args) {
        return T{std::forward<Args>(args)...};
    }, std::forward<U>(from));
}

Point p = make_from_apply<Point>(computePoint());

See live example at Compiler Explorer

All the std::forwarding isn't strictly necessary if Args is only going to be fundamental types like int and double. In that case, you can omit it.

Crystalloid answered 19/12, 2023 at 9:12 Comment(2)
In case some is asking why to use a lambda function, let me link this question: Why does std::apply fail with function template, but not with a lambda expression with explicit template parameter list?Tetramethyldiarsine
As it may not be obvious to everyone, I'll make it explicit: this solution works also if function from returns a std::tuple with the attributes of the structure T.Tetramethyldiarsine
O
1

Could I templatize it for a general length?

Yes, you can use std::index_sequence to generailze this for any length as shown below:

template<std::size_t N, typename T> constexpr auto make_array(const std::array<T, N> &ref)   
{
    return [&]<std::size_t... Indices>(std::index_sequence<Indices...>)
    {
        return Point{(ref[Indices])...};     
    }(std::make_index_sequence<N>());   
} 
int main()
{
    std::array<double, 3> ansArray{ComputePoint()};
    Point p = make_array(ansArray);       
}

Working demo

Ola answered 19/12, 2023 at 9:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.