Reputation: 863
I was wondering if it was possible to use a vector as the initializer list for a vector. So, if I have
struct somedata{
string str1;
string str2;
}
struct moredata{
string str1;
string str2;
string str3;
}
template<class Dataholder>
Dataholder queryUser(args){
auto vec = get_vector_from_user(args)
Dataholder dat{vec}; // The elements of vec become the structured variables in dat.
return dat;
}
So, the user might input 2 or 3 strings when get_vector_from_user()
is called. However, I know that the programmer will always template queryUser
and that there will be the same number of elements in vec
as strings in the Dataholder
template. Is it possible to initialize a struct with the members of a vector? Thanks!
Upvotes: 2
Views: 353
Reputation: 96246
Yes, it's possible. Enumerating structure members requires complex templates, and possibly code generation because you might need boilerplate code for each specific number of structure members. Luckily there's a library that does it. Behold Boost.PFR, aka magic_get
.
Its interface mimics that of std::tuple
, so it's easy to use.
The two major limitations are:
You will also need a compile-time for
loop:
template <typename Integer, Integer ...I, typename F>
constexpr void constexpr_for_each(std::integer_sequence<Integer, I...>, F &&func)
{
(func(std::integral_constant<Integer, I>{}) , ...);
}
template <auto N, typename F>
constexpr void constexpr_for(F &&func)
{
if constexpr (N > 0)
constexpr_for_each(std::make_integer_sequence<decltype(N), N>{}, std::forward<F>(func));
}
Now you can do this:
struct A
{
std::string x;
std::string y;
std::string z;
};
int main()
{
std::vector<std::string> vec = {"a", "b", "c"};
if (vec.size() != boost::pfr::tuple_size_v<A>)
throw std::runtime_error("Wrong vector size.");
A a;
constexpr_for<boost::pfr::tuple_size_v<A>>([&](auto index)
{
constexpr auto i = index.value;
static_assert(std::is_same_v<std::string, boost::pfr::tuple_element_t<i, A>>);
boost::pfr::get<i>(a) = vec[i];
});
std::cout << a.x << ' ' << a.y << ' ' << a.z << '\n'; // a b c
}
Upvotes: 3
Reputation: 1399
For the class in question, write a constructor which accepts a std::vector
, or include the logic directly in the function template:
struct somedata{
string str1;
string str2;
somedata(const std::vector& vec) : str1(vec[0]), str2(vec[1]){
}
}
struct moredata{
string str1;
string str2;
string str3;
moredata(const std::vector& vec) : str1(vec[0]), str2(vec[1]), str3(vec[2]){
}
}
template<class Dataholder>
Dataholder queryUser(args){
auto vec = get_vector_from_user(args)
Dataholder dat{vec}; // The elements of vec become the structured variables in dat.
return dat;
}
Just make sure you add a check to assert there are correct number of elements in the vector.
Upvotes: 4