Suppose I have types A
, B
with constructors A(int a, double b, std::string c)
, B(double a, int b)
.
I know how to define a function that instantiates either A
or B
via variadic templates.
Is there any way to design a function/macro/type that for a type T
and a series of vectors of possibilities for T
's constructor arguments it provides me with all possible objects?
For example, if I use this magical construct for <A, {2, 5, 6}, {2.44, 3.14}, {"yes", "no"}>
it should provide the objects:
A(2, 2.44, "yes")
A(2, 2.44, "no")
A(2, 3.14, "yes")
...
A(6, 3.14, "no")
The same should work for B
or any other type without having to rework the magical construct.
This is super easy in Python for example, but I don't know if it's possible in C++.
In Java, a constructor is a block of codes similar to the method. It is called when an instance of the class is created. At the time of calling the constructor, memory for the object is allocated in the memory. It is a special type of method which is used to initialize the object.
When you create an object, you are creating an instance of a class, therefore "instantiating" a class. The new operator requires a single, postfix argument: a call to a constructor. The name of the constructor provides the name of the class to instantiate. The constructor initializes the new object.
A copy constructor is a member function that initializes an object using another object of the same class.
This uses std::experimental::array_view
for efficiency. You can replace it with std::vector
at some runtime cost, or a pair of iterators/pointers at some clarity cost.
template<class T>
using array_view = std::experimental::array_view<T>;
using indexes = array_view<std::size_t>;
This iterates over the cross product of each element <
the respective index in indexes. So {3,3,2}
as is
iterates over {0,0,0}
then {0,0,1}
all the way to {2,2,1}
.
template<class F>
void for_each_cartesian_product(
indexes is,
F&& f,
std::vector<std::size_t>& result
) {
if (is.empty()) {
f(result);
return;
}
auto max_index = is.front();
for (std::size_t i = 0; i < max_index; ++i) {
result.push_back(i);
for_each_cartesian_product( {is.begin()+1, is.end()}, f, result );
result.pop_back();
}
}
template<class F>
void for_each_cartesian_product(
indexes is,
F&& f
) {
std::vector<size_t> buffer;
for_each_cartesian_product( is, f, buffer );
}
then we just populate our indexes:
template<class...Ts>
std::vector<std::size_t> get_indexes( std::vector<Ts> const&... vs ) {
return {vs.size()...};
}
Next, we can just have to take our arguments, put them in a vector, and then use the indexes to get elements from each vector and pass them to A
to be constructed.
template<class T, std::size_t...Is, class...Args>
std::vector<T> make_stuff( std::index_sequence<Is...>, std::vector<Args>const&... args ) {
std::vector<T> retval;
for_each_cartesian_product(
get_indexes(args...),
[&](auto&& index){
retval.emplace_back( args[ index[Is] ]... );
}
);
return retval;
}
template<class T, class...Args>
std::vector<T> make_stuff( std::vector<Args>const&... args ) {
return make_stuff<T>( std::index_sequence_for<Args...>{}, args... );
}
and bob is your uncle.
The A
s generated may be moved.
Doing this at compile time with compile time known arrays can also be done.
index_sequence_for
and index_sequence
are C++14, but easy to implement in C++11. There are many examples on stack overflow.
The above code has not been compiled.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With