Reputation: 43
So i have something like that:
template<unsigned int W,unsigned int H>
class Class
{
int data[W][H];
Class(const (&_data)[W][H])
{
for (int x=0;x<W;x++)
for (int y=0;y<H;y++)
data[x][y] = _data[x][y];
}
template<class... args>
Class()
{
/// black magic
}
}
What could i replace the "black magic", so the second constructor will accept W*H ints? Example:
Class<3,2> class1(1,2,3,4,5,6);
Upvotes: 4
Views: 802
Reputation: 7904
There are alternative answers which might be more practical and simpler to implement, but I'll show how you could actually do this with a compile-time for-loop for purposes of demonstrating black-magic.
Here is a compile-time for-loop.
/* Compile-time for-loop up to N. */
template <std::size_t N>
struct For {
/* Call f<I>(args...) N times. */
template <typename F, typename... Args>
void operator()(F &&f, Args &&... args) const {
Impl<0, N>()(std::forward<F>(f), std::forward<Args>(args)...);
}
private:
/* Forward declaration. */
template <std::size_t I, std::size_t End>
struct Impl;
/* Base case. Do nothing. */
template <std::size_t End>
struct Impl<End, End> {
template <typename F, typename... Args>
void operator()(F &&, Args &&...) const { /* Do nothing. */ }
}; // Impl<End, End>
/* Recursive case. Call f<I>(args...), then recurse into next step. */
template <std::size_t I, std::size_t End>
struct Impl {
template <typename F, typename... Args>
void operator()(F &&f, Args &&... args) const {
std::forward<F>(f).template operator()<I>(std::forward<Args>(args)...);
Impl<I + 1, End>()(std::forward<F>(f), std::forward<Args>(args)...);
}
}; // Impl<I, End>
}; // For<N>
Here is a simple use case of it.
struct Print {
template <std::size_t I>
void operator()(int x, int y) const {
std::cout << "Iteration " << I << ": " << x << ' ' << y << std::endl;
}
}; // Print
For<3>()(Print(), 1, 2);
Outputs
Iteration 0: 1 2
Iteration 1: 1 2
Iteration 2: 1 2
Now with this we can nest this compile-time for-loop just like how we could nest a run-time for-loop. Here is the Matrix class using this For<> template.
/* Defines an M by N Matrix, (Row by Col). */
template <std::size_t M, std::size_t N>
class Matrix {
public:
/* Our underlying M by N matrix. */
using Data = std::array<std::array<int, N>, M>;
/* Construct off of M * N arguments. */
template <typename... Args>
Matrix(Args &&... args) {
static_assert(sizeof...(Args) == M * N,
"The number of arguments provided must be M * N.");
ForEach(AssignImpl(),
data_,
std::forward_as_tuple(std::forward<Args>(args)...));
}
/* Print each element out to std::cout. */
void Write(std::ostream &strm) const {
ForEach(WriteImpl(), strm, data_);
}
private:
/* Our outer for loop. Call InnerFor() M times.
Resembles: 'for (std::size_t i = 0 ; i < M; ++i) {' */
template <typename F, typename... Args>
void ForEach(F &&f, Args &&... args) const {
For<M>()(InnerFor(), std::forward<F>(f), std::forward<Args>(args)...);
}
/* Our inner for loop. Call ForBody() N times.
Resembles: 'for (std::size_t j = 0; j < N; ++j) {' */
struct InnerFor {
template <std::size_t I, typename F, typename... Args>
void operator()(F &&f, Args &&... args) const {
For<N>()(ForBody<I>(),
std::forward<F>(f),
std::forward<Args>(args)...);
}
}; // InnerFor
/* The body of our for loop. Call f<I, J>(args...); */
template <std::size_t I>
struct ForBody {
template <std::size_t J, typename F, typename... Args>
void operator()(F &&f, Args &&... args) const {
std::forward<F>(f)
.template operator()<I, J>(std::forward<Args>(args)...);
}
}; // ForBody<I>
/* Given the M by N array and a tuple of length M * N, assign the array. */
struct AssignImpl {
template <std::size_t I, std::size_t J, typename Arg>
void operator()(Data &data, Arg &&arg) const {
data[I][J] = std::get<I * N + J>(std::forward<Arg>(arg));
}
}; // AssignImpl
/* Given an output stream and our data, print the data at (I, J). */
struct WriteImpl {
template <std::size_t I, std::size_t J>
void operator()(std::ostream &strm, const Data &data) const {
strm << data[I][J] << std::endl;
}
}; // WriteImpl
/* Our underlying M by N array. */
Data data_;
}; // Matrix
Here is a quick demonstration of construction and writing to std::cout
.
int main() {
Matrix<3, 2> matrix{101, 102,
201, 202,
301, 302};
matrix.Write(std::cout);
}
Upvotes: 3
Reputation: 2176
you could use std::initializer_list as a constructor parameter, since your array only is of type int[][]
.
Class(std::initializer_list<int> il)
{
if(il.size() < W*H)
throw string("Insufficient elements");//if lesser no. of elements are given.
auto it = il.begin();// iterator to the first element of the list.
for(int i =0;i< W;++i)
{
for(int j =0; j < H;++j)
{
data[i][j] = *it++;// increment the iterator
}
}
}
call site will look like this:
Class<3,2> c{1,2,3,4,5,6};
or
Class<3,2> c = {1,2,3,4,5,6};
if more elements are given then extras are discarded. initializer_list
can give type safety, and will give you a diagnostic if narrowing is found.
Upvotes: 0
Reputation: 275330
This works:
#include <array>
#include <utility>
#include <iostream>
template<unsigned W,unsigned H>
struct Foo
{
std::array<std::array<int, H>, W> data;
template<typename... Args>
Foo(Args&&... args):
data{ std::forward<Args>(args)... }
{}
};
int main()
{
Foo<2,2> x(1,2,3,4);
for( auto&& a : x.data ) {
for( unsigned z : a ) {
std::cout << z << ",";
}
std::cout << "\n";
}
but exposes the order of storage in your inner array
.
std::array
is a syntactic sugar wrapped around raw C arrays.
Upvotes: 2
Reputation: 15524
First of all, there's some syntax errors in your example as missing semicolon after class declaration and the constructors being private.
Apart from that, if you want to store the numbers in row-major order, then you should declare your matrix/2d-array as int data[H][W]
(height first, then width).
To store the values from a variadic pack you can simply expand them inside the ctor of a container, e.g. std::array
, and make use of list-initialization.
template <typename... Args>
Class(Args&&... args) {
const std::array<int, H * W> temp{std::forward<Args>(args)...};
/* ... */
};
I've also used universal references and perfect forwarding to preserve the reference type of the pack.
To populate your 2d-array you simply have to iterate over all elements in temp
and store them in the member array.
for (std::size_t h = 0; h != H; ++h)
for (std::size_t w = 0; w != W; ++w)
data[h][w] = temp[h * W + w];
Upvotes: 2