Related
I've just started using Eigen and have read from their documentation that the best performance comes from their lazy evaluation of matrix expressions. Hence, expressions like this are very efficient once evaluated:
Eigen::Matrix<float, 3, 1> a;
a << 0, 1, 2;
Eigen::Matrix<float, 3, 1> b;
b << 3, 4, 5;
Eigen::Matrix<float, 3, 1> c;
c << (a + b).sum(),
(a - b).sum(),
a.sum();
std::cout << c << std::endl;
I ran into a problem when constructing matrices whose column count depends on a template parameter. For example:
template <std::size_t w>
auto buildGradient() {
Eigen::Matrix<float, 3, w> matrix;
matrix << /* ? */;
return matrix;
}
My first inclination was to use a recursive c++ template do do this.
template <std::size_t w, typename Functor>
auto buildGradientExpr(Functor functor) {
if constexpr (w == 0) {
return;
} else if constexpr (w == 1) {
return functor();
} else {
return functor(), buildGradientExpr<w - 1, Functor>(functor);
}
}
But using this leads to runtime errors issued by Eigen, since the expression only has one initializer.
template <std::size_t w>
auto buildGradient() {
Eigen::Matrix<float, 3, w> gradient;
/* Emits an error about too few coefficients being passed to the initializer. */
gradient << buildGradientExpr<w>([]() { /* Return 3x1 matrix */ });
return gradient;
}
Here's a complete runnable example.
#include <Eigen/Dense>
#include <iostream>
#include <cstddef>
namespace {
template <std::size_t w, typename Functor>
auto buildGradientExpr(Functor functor) {
if constexpr (w == 0) {
return;
} else if constexpr (w == 1) {
return functor(w);
} else {
return functor(w), buildGradientExpr<w - 1, Functor>(functor);
}
}
template <std::size_t w, typename Functor>
auto buildGradient(Functor functor) {
Eigen::Matrix<float, 3, w> gradient;
gradient << buildGradientExpr<w>(functor);
return gradient;
}
} // namespace
int main() {
constexpr std::size_t gradient_width = 10;
auto gradient_functor = [](std::size_t w) {
return Eigen::Matrix<float, 3, 1>::Constant(float(w) / gradient_width);
};
auto gradient = buildGradient<gradient_width>(gradient_functor);
std::cout << gradient << std::endl;
return 0;
}
Is there a way of constructing matrices with sizes that depend on template parameters, without resorting to a for loop? Nothing against for loops, that's what I'm using in the mean time. I'd just like to know if there's a way to initialize a matrix expression using template loops.
Edit:* I updated the example, because the gradient functor was suppose to be returning a vector and not a scalar. The sample problem occurs, though.
If I understand correctly your issue here, and you can use c++11, a possible solution could be the following:
(a) extend the Eigen::Matrix class as explained here to include the constructor from std::initializer_list<T>:
// File "Eigen_plugin.h"
template<class T>
Matrix(std::initializer_list<T> elems) : Base()
{
Base::_check_template_params();
int idx = 0;
for(auto el : elems)
coeffRef(idx++) = el;
}
(b) define your buildGradient function using variadic templates:
// File main.cpp
#include <iostream>
#include <eigen3/Eigen/Dense>
template<class... Args>
Eigen::Matrix<double, 3, sizeof...(Args)/3> buildGradient(Args&&... args)
{
static_assert(sizeof...(Args) % 3 == 0, "Wrong number of elements");
return Eigen::Matrix<double, 3, sizeof...(Args)/3>{ args... };
}
int main()
{
std::cout << buildGradient(1.0,2.0,3.0) << std::endl;
std::cout << buildGradient(1.0,2.0,3.0,4.0,5.0,6.0) << std::endl;
}
Note
As explained in the first link, to make this compile you need to define the preprocessor variable EIGEN_MATRIX_PLUGIN to point at the absolute path of your plugin file:
g++ -std=c++11 main.cpp -D EIGEN_MATRIX_PLUGIN=\"path-to-Eigen_plugin.h\"
Is it a better idea to use boost::join to access and change the values of different arrays?
I have defined a member array inside class element.
class element
{
public:
element();
int* get_arr();
private:
int m_arr[4];
}
At different place, I'm accessing these arrays and joined together using boost::join and changing the array values.
//std::vector<element> elem;
auto temp1 = boost::join(elem[0].get_arr(),elem[1].get_arr());
auto joined_arr = boost::join(temp1,elem[2].get_arr());
//now going to change the values of the sub array
for(auto& it:joined_arr)
{
it+= sample[i];
i++;
}
Is this a good idea to modify the values of array in the class as above?
In your code you probably want to join the 4-elements arrays. To do that change the signature of get_arr to:
typedef int array[4];
array& get_arr() { return m_arr; }
So that the array size does not get lost.
Performance-wise there is a non-zero cost for accessing elements through the joined view. A double for loop is going to be most efficient, and easily readable too, e.g.:
for(auto& e : elem)
for(auto& a : e.get_arr())
a += sample[i++];
boost::join returns a more complicated type every composition step. At some point you might exceed the compiler's limits on inlining so that you're going to have a runtime cost¹.
Thinking outside the box, it really looks like you are creating a buffer abstraction that allows you to do scatter/gather like IO with few allocations.
As it happens, Boost Asio has nice abstractions for this², and you could use that: http://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/MutableBufferSequence.html
As I found out in an earlier iteration of this answer code that abstraction sadly only works for buffers accessed through native char-type elements. That was no good.
So, in this rewrite I present a similar abstraction, which consists of nothing than an "hierarchical iterator" that knows how to iterate a sequence of "buffers" (in this implementation, any range will do).
You can choose to operate on a sequence of ranges directly, e.g.:
std::vector<element> seq(3); // tie 3 elements together as buffer sequence
element& b = seq[1];
Or, without any further change, by reference:
element a, b, c;
std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence
The C++ version presented at the bottom demonstrates this approach Live On Coliru
The Iterator Implementation
I've used Boost Range and Boost Iterator:
template <typename Seq,
typename WR = typename Seq::value_type,
typename R = typename detail::unwrap<WR>::type,
typename V = typename boost::range_value<R>::type
>
struct sequence_iterator : boost::iterator_facade<sequence_iterator<Seq,WR,R,V>, V, boost::forward_traversal_tag> {
using OuterIt = typename boost::range_iterator<Seq>::type;
using InnerIt = typename boost::range_iterator<R>::type;
// state
Seq& _seq;
OuterIt _ocur, _oend;
InnerIt _icur, _iend;
static sequence_iterator begin(Seq& seq) { return {seq, boost::begin(seq), boost::end(seq)}; }
static sequence_iterator end(Seq& seq) { return {seq, boost::end(seq), boost::end(seq)}; }
// the 3 facade operations
bool equal(sequence_iterator const& rhs) const {
return ((_ocur==_oend) && (rhs._ocur==rhs._oend))
|| (std::addressof(_seq) == std::addressof(rhs._seq) &&
_ocur == rhs._ocur && _oend == rhs._oend &&
_icur == rhs._icur && _iend == rhs._iend);
}
void increment() {
if (++_icur == _iend) {
++_ocur;
setup();
}
}
V& dereference() const {
assert(_ocur != _oend);
assert(_icur != _iend);
return *_icur;
}
private:
void setup() { // to be called after entering a new sub-range in the sequence
while (_ocur != _oend) {
_icur = boost::begin(detail::get(*_ocur));
_iend = boost::end(detail::get(*_ocur));
if (_icur != _iend)
break;
++_ocur; // skid over, this enables simple increment() logic
}
}
sequence_iterator(Seq& seq, OuterIt cur, OuterIt end)
: _seq(seq), _ocur(cur), _oend(end) { setup(); }
};
That's basically the same kind of iterator as boost::asio::buffers_iterator but it doesn't assume an element type. Now, creating sequence_iterators for any sequence of ranges is as simple as:
template <typename Seq> auto buffers_begin(Seq& seq) { return sequence_iterator<Seq>::begin(seq); }
template <typename Seq> auto buffers_end(Seq& seq) { return sequence_iterator<Seq>::end(seq); }
Implementing Your Test Program
Live On Coliru
// DEMO
struct element {
int peek_first() const { return m_arr[0]; }
auto begin() const { return std::begin(m_arr); }
auto end() const { return std::end(m_arr); }
auto begin() { return std::begin(m_arr); }
auto end() { return std::end(m_arr); }
private:
int m_arr[4] { };
};
namespace boost { // range adapt
template <> struct range_iterator<element> { using type = int*; };
// not used, but for completeness:
template <> struct range_iterator<element const> { using type = int const*; };
template <> struct range_const_iterator<element> : range_iterator<element const> {};
}
#include <algorithm>
#include <iostream>
#include <vector>
template <typename Output, typename Input, typename Operation>
size_t process(Output& output, Input const& input, Operation op) {
auto ib = boost::begin(input), ie = boost::end(input);
auto ob = boost::begin(output), oe = boost::end(output);
size_t n = 0;
for (;ib!=ie && ob!=oe; ++n) {
op(*ob++, *ib++);
}
return n;
}
int main() {
element a, b, c;
std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence
//// Also supported, container of range objects directly:
// std::list<element> seq(3); // tie 3 elements together as buffer sequence
// element& b = seq[1];
std::vector<int> const samples {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32
};
using boost::make_iterator_range;
auto input = make_iterator_range(samples);
auto output = make_iterator_range(buffers_begin(seq), buffers_end(seq));
while (auto n = process(output, input, [](int& el, int sample) { el += sample; })) {
std::cout << "Copied " << n << " samples, b starts with " << b.peek_first() << "\n";
input.advance_begin(n);
}
}
Prints
Copied 12 samples, b starts with 5
Copied 12 samples, b starts with 22
Copied 8 samples, b starts with 51
Full Listing, C++11 Compatible
Live On Coliru
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <functional> // std::reference_wrapper
namespace detail {
template<typename T> constexpr T& get(T &t) { return t; }
template<typename T> constexpr T const& get(T const &t) { return t; }
template<typename T> constexpr T& get(std::reference_wrapper<T> rt) { return rt; }
template <typename T> struct unwrap { using type = T; };
template <typename T> struct unwrap<std::reference_wrapper<T> > { using type = T; };
}
template <typename Seq,
typename WR = typename Seq::value_type,
typename R = typename detail::unwrap<WR>::type,
typename V = typename boost::range_value<R>::type
>
struct sequence_iterator : boost::iterator_facade<sequence_iterator<Seq,WR,R,V>, V, boost::forward_traversal_tag> {
using OuterIt = typename boost::range_iterator<Seq>::type;
using InnerIt = typename boost::range_iterator<R>::type;
// state
Seq& _seq;
OuterIt _ocur, _oend;
InnerIt _icur, _iend;
static sequence_iterator begin(Seq& seq) { return {seq, boost::begin(seq), boost::end(seq)}; }
static sequence_iterator end(Seq& seq) { return {seq, boost::end(seq), boost::end(seq)}; }
// the 3 facade operations
bool equal(sequence_iterator const& rhs) const {
return ((_ocur==_oend) && (rhs._ocur==rhs._oend))
|| (std::addressof(_seq) == std::addressof(rhs._seq) &&
_ocur == rhs._ocur && _oend == rhs._oend &&
_icur == rhs._icur && _iend == rhs._iend);
}
void increment() {
if (++_icur == _iend) {
++_ocur;
setup();
}
}
V& dereference() const {
assert(_ocur != _oend);
assert(_icur != _iend);
return *_icur;
}
private:
void setup() { // to be called after entering a new sub-range in the sequence
while (_ocur != _oend) {
_icur = boost::begin(detail::get(*_ocur));
_iend = boost::end(detail::get(*_ocur));
if (_icur != _iend)
break;
++_ocur; // skid over, this enables simple increment() logic
}
}
sequence_iterator(Seq& seq, OuterIt cur, OuterIt end)
: _seq(seq), _ocur(cur), _oend(end) { setup(); }
};
template <typename Seq> auto buffers_begin(Seq& seq) { return sequence_iterator<Seq>::begin(seq); }
template <typename Seq> auto buffers_end(Seq& seq) { return sequence_iterator<Seq>::end(seq); }
// DEMO
struct element {
int peek_first() const { return m_arr[0]; }
auto begin() const { return std::begin(m_arr); }
auto end() const { return std::end(m_arr); }
auto begin() { return std::begin(m_arr); }
auto end() { return std::end(m_arr); }
private:
int m_arr[4] { };
};
namespace boost { // range adapt
template <> struct range_iterator<element> { using type = int*; };
// not used, but for completeness:
template <> struct range_iterator<element const> { using type = int const*; };
template <> struct range_const_iterator<element> : range_iterator<element const> {};
}
#include <algorithm>
#include <iostream>
#include <vector>
template <typename Output, typename Input, typename Operation>
size_t process(Output& output, Input const& input, Operation op) {
auto ib = boost::begin(input), ie = boost::end(input);
auto ob = boost::begin(output), oe = boost::end(output);
size_t n = 0;
for (;ib!=ie && ob!=oe; ++n) {
op(*ob++, *ib++);
}
return n;
}
int main() {
element a, b, c;
std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence
//// Also supported, container of range objects directly:
// std::list<element> seq(3); // tie 3 elements together as buffer sequence
// element& b = seq[1];
std::vector<int> const samples {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32
};
using boost::make_iterator_range;
auto input = make_iterator_range(samples);
auto output = make_iterator_range(buffers_begin(seq), buffers_end(seq));
while (auto n = process(output, input, [](int& el, int sample) { el += sample; })) {
std::cout << "Copied " << n << " samples, b starts with " << b.peek_first() << "\n";
input.advance_begin(n);
}
}
¹ I'm ignoring the compile-time cost and the lurking dangers with the pattern of auto x = complicated_range_composition when that complicated range composition contains references to temporaries: this is a frequent source of UB bugs
² which have been adopted by various other libraries, like Boost Beast, Boost Process and seem to have found their way into the Networking TS for C++20: Header <experimental/buffer> synopsis (PDF)
My task is to modify Sergiu Dotenco's Well Equidistributed Long-period Linear (WELL) algorithm code to not use boost (not saying boost is bad, but due to some company's policy i have to remove it).
now, Sergiu's WELL is using boost's mpl library, there are quite some logic behind it. So one way is to read up all those, then naturally i would be able to finish the task. The other way is, replacing bit by bit with some best guess.
I'm on the 2nd way to hope this try-and-error approach would be faster. So far I've successfully replaced boost::mpl::if_ and if_c with std::conditional, but hit error when try to update IsPowerOfTwo and Power2Modulo etc, that's why i'm seeking help there.
Below is the code, how to rewrite it without boost, but only c++17?
/**
* Conditional expression of type (r & (r - 1)) == 0 which allows to check
* whether a number #f$r#f$ is of type #f$2^n#f$.
*/
typedef boost::mpl::equal_to<
boost::mpl::bitand_<
boost::mpl::_,
boost::mpl::minus<boost::mpl::_, boost::mpl::int_<1>
>
>,
boost::mpl::int_<0>
> IsPowerOfTwo;
template<class UIntType, UIntType r>
struct Power2Modulo
{
typedef typename boost::mpl::apply<
IsPowerOfTwo,
boost::mpl::integral_c<UIntType, r>
>::type type;
BOOST_STATIC_ASSERT(type::value);
template<class T>
static T calc(T value)
{
return value & (r - 1);
}
};
If possible, pls give a short example on how to call it? I tried to instantiate IsPowerOfTwo or Power2Modulo in main with
Detail::IsPowerOfTwo p0;
or
Detail::Power2Modulo<int, 3> p1;
but got compilation error.
I asked a relevant question before and got some suggestion. However, not familiar to metaprogramming and boost, I don't quite get it.
So, I looked at that library, and created a no-boost fork adapting the WELL pseudo-random-number-generator to pure c++11.
See here on my github: https://github.com/sehe/well-random (the default branch is no-boost).
What is well-random?
well-random is a c++11 fork from
random, a collection of various
pseudo-random number generators and distributions that were intended
to accompany the Boost Random Number Library.
This fork currently only adopted the WELL generator and its tests.
Getting started
The no-boost branch no longer requires any boost library. Instead it
requires c++11. To compile the tests make sure first CMake 2.8 is
installed, then enter :
$ cmake . -DCMAKE_BUILD_TYPE=Release
in your terminal or command prompt on Windows inside project's
directory to generate the appropriate configuration that can be used
to compile the tests using make/nmake or inside an IDE.
What Was Refactored
BOOST_STATIC_ASSERT to STATIC_ASSERT (this becomes obsolete with c++17: http://en.cppreference.com/w/cpp/language/static_assert)
BOOST_STATIC_CONSTANT to static constexpr
BOOST_PREVENT_MACRO_SUBSTITUTION -> PREVENT_MACRO_SUBSTITUTION (trivial macro)
BOOST_THROW_EXCEPTION dropped. NOTE This implies the code cannot be compiled with exception support disabled.
All things related to Boost Test
BOOST_CHECK -> CHECK
#define MESSAGE_PREAMBLE() (std::cerr << __FILE__ << ":" << __LINE__ << " ")
#define CHECK(test) do { if (!(test)) MESSAGE_PREAMBLE() << #test << "\n"; } while (0)
BOOST_CHECK_EQUAL -> CHECK_EQUAL
#define CHECK_EQUAL(expected,actual) do { \
auto&& _e = expected; \
auto&& _a = actual; \
if (_e != _a) \
MESSAGE_PREAMBLE() << "expected:" << #expected << " = " << _e << "\n" \
<< "\tactual:" << #actual << " = " << _a << "\n"; \
} while (0)
BOOST_AUTO_TEST_CASE - dropped. The test driver is main now:
int main() {
//CHECK_EQUAL(16, Detail::shift<2>(64));
//CHECK_EQUAL(64, Detail::shift<-2>(16));
//CHECK_EQUAL(32, Detail::shift<0>(32));
//CHECK(Detail::is_powerof2(512u));
//CHECK(not Detail::is_powerof2(0u));
WellTestCase<Well512a, 0x2b3fe99e>::run();
WellTestCase<Well521a, 0xc9878363>::run();
WellTestCase<Well521b, 0xb75867f6>::run();
WellTestCase<Well607a, 0x7b5043ea>::run();
WellTestCase<Well607b, 0xaedee7da>::run();
WellTestCase<Well800a, 0x2bfe686f>::run();
WellTestCase<Well800b, 0xf009e1bd>::run();
WellTestCase<Well1024a, 0xd07f528c>::run();
WellTestCase<Well1024b, 0x867f7993>::run();
WellTestCase<Well19937a, 0xb33a2cd5>::run();
WellTestCase<Well19937b, 0x191de86a>::run();
WellTestCase<Well19937c, 0x243eaed5>::run();
WellTestCase<Well21701a, 0x7365a269>::run();
WellTestCase<Well23209a, 0x807dacb >::run();
WellTestCase<Well23209b, 0xf1a77751>::run();
WellTestCase<Well44497a, 0xfdd7c07b>::run();
WellTestCase<Well44497b, 0x9406547b>::run();
}
boost::ref -> std::ref (from <functional>)
Boost Range helpers replaced by standard c++ (boost::size, boost::end for arrays)
using ulong_long_type = unsigned long long;
Conditional operators shift and mod have been re-implemented with straight-up SFINAE based on std::enable_if instead of using MPL meta-programming:
template<class UIntType, unsigned N>
struct Left
{
static UIntType shift(UIntType a)
{
return a << N;
}
};
template<class UIntType, unsigned N>
struct Right
{
static UIntType shift(UIntType a)
{
return a >> N;
}
};
template<int N, class UIntType>
inline UIntType shift(UIntType a)
{
return boost::mpl::if_c<(N < 0),
Left<UIntType, -N>,
Right<UIntType, N>
>::type::shift(a);
}
became:
template <typename UIntType, signed N, typename Enable = void> struct Shift;
template <typename UIntType, signed N>
struct Shift<UIntType, N, typename std::enable_if<(N>=0)>::type> {
static UIntType apply(UIntType a) { return a >> N; }
};
template <typename UIntType, signed N>
struct Shift<UIntType, N, typename std::enable_if<(N<0)>::type> {
static UIntType apply(UIntType a) { return a << -N; }
};
template<int N, class UIntType>
inline UIntType shift(UIntType a) { return Shift<UIntType, N>::apply(a); }
Likewise, the Modulo switch (Power2Modulo and GenericModulo) that looked like this:
/**
* Conditional expression of type (r & (r - 1)) == 0 which allows to check
* whether a number #f$r#f$ is of type #f$2^n#f$.
*/
typedef boost::mpl::equal_to<
boost::mpl::bitand_<
boost::mpl::_,
boost::mpl::minus<boost::mpl::_, boost::mpl::int_<1>
>
>,
boost::mpl::int_<0>
> IsPowerOfTwo;
template<class UIntType, UIntType r>
struct Power2Modulo
{
typedef typename boost::mpl::apply<
IsPowerOfTwo,
boost::mpl::integral_c<UIntType, r>
>::type type;
BOOST_STATIC_ASSERT(type::value);
template<class T>
static T calc(T value)
{
return value & (r - 1);
}
};
template<class UIntType, UIntType r>
struct GenericModulo
{
/**
* #brief Determines #a value modulo #a r.
*
* #pre value >= 0 and value < 2 * r
* #post value >= 0 and value < r
*/
template<class T>
static T calc(T value)
{
BOOST_STATIC_ASSERT(!std::numeric_limits<UIntType>::is_signed);
assert(value < 2 * r);
if (value >= r)
value -= r;
return value;
}
};
template<class UIntType, UIntType r>
struct Modulo
{
typedef typename boost::mpl::apply<
IsPowerOfTwo,
boost::mpl::integral_c<UIntType, r>
>::type rIsPowerOfTwo;
static UIntType calc(UIntType value)
{
// Use the bitwise AND for power 2 modulo arithmetic, or subtraction
// otherwise. Subtraction is about two times faster than direct modulo
// calculation.
return boost::mpl::if_<
rIsPowerOfTwo,
Power2Modulo<UIntType, r>,
GenericModulo<UIntType, r>
>::type::calc(value);
}
};
became much simpler with a little bit of c++11 (constexpr!) goodness:
template <typename T, typename = typename std::enable_if<!std::is_signed<T>()>::type>
constexpr static bool is_powerof2(T v) { return v && ((v & (v - 1)) == 0); }
template<class UIntType, UIntType r>
struct Modulo {
template<class T> static T calc(T value) { return calc(value, std::integral_constant<bool, is_powerof2(r)>{}); }
/**
* #brief Determines #a value modulo #a r.
*
* #pre value >= 0 and value < 2 * r
* #post value >= 0 and value < r
*/
template<class T> static T calc(T value, std::true_type) { return value & (r - 1); }
template<class T> static T calc(T value, std::false_type) {
STATIC_ASSERT(!std::numeric_limits<UIntType>::is_signed);
assert(value < 2 * r);
if (value >= r)
value -= r;
return value;
}
};
<boost/cstdint.hpp> -> <cstdint> (replacing ::boost by ::std for uint_least32_t and uint32_t)
Well_quoted type function replaced by an alias template (template<...> using T = ... see http://en.cppreference.com/w/cpp/language/type_alias ad 2)
typedefs rewritten as type aliases.
Full Listing
Live On Coliru
// Copyright (c) Sergiu Dotenco 2010, 2011, 2012
// Copyright (c) Seth Heeren - made independent of BOOST using C++11 - 2017
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
/**
* #brief Implementation of the Well Equidistributed Long-period Linear (WELL)
* pseudo-random number generator.
* #file well.hpp
*/
#ifndef WELL_HPP
#define WELL_HPP
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <iomanip>
#include <istream>
#include <limits>
#include <ostream>
#include <functional>
#include <stdexcept>
#define STATIC_ASSERT(x) static_assert(x, #x)
#define PREVENT_MACRO_SUBSTITUTION
//! #cond hide_private
namespace Detail {
using ulong_long_type = unsigned long long;
template <typename UIntType, signed N, typename Enable = void> struct Shift;
template <typename UIntType, signed N>
struct Shift<UIntType, N, typename std::enable_if<(N>=0)>::type> {
static UIntType apply(UIntType a) { return a >> N; }
};
template <typename UIntType, signed N>
struct Shift<UIntType, N, typename std::enable_if<(N<0)>::type> {
static UIntType apply(UIntType a) { return a << -N; }
};
template<int N, class UIntType>
inline UIntType shift(UIntType a) {
return Shift<UIntType, N>::apply(a);
}
/**
* #name Transformation matrices #f$M0,\dotsc,M6#f$ from Table I
* #{
*/
struct M0
{
template<class T>
static T transform(T)
{
return T(0);
}
};
struct M1
{
template<class T>
static T transform(T x)
{
return x;
}
};
template<int N>
struct M2
{
template<class T>
static T transform(T x)
{
return shift<N>(x);
}
};
template<int N>
struct M3
{
template<class T>
static T transform(T x)
{
return x ^ shift<N>(x);
}
};
template<std::uint_least32_t a>
struct M4
{
template<class T>
static T transform(T x)
{
T result = x >> 1;
if ((x & 1) == 1)
result ^= a;
return result;
}
};
template<int N, std::uint_least32_t b>
struct M5
{
template<class T>
static T transform(T x)
{
return x ^ (shift<N>(x) & b);
}
};
template
<
std::size_t w,
std::uint_least32_t q,
std::uint_least32_t a,
std::uint_least32_t ds,
std::uint_least32_t dt
>
struct M6
{
template<class T>
static T transform(T x)
{
T result = ((x << q) ^ (x >> (w - q))) & ds;
if ((x & dt) != 0)
result ^= a;
return result;
}
};
//! #}
template <typename T, typename = typename std::enable_if<!std::is_signed<T>()>::type>
constexpr static bool is_powerof2(T v) { return v && ((v & (v - 1)) == 0); }
template<class UIntType, UIntType r>
struct Modulo {
template<class T> static T calc(T value) { return calc(value, std::integral_constant<bool, is_powerof2(r)>{}); }
/**
* #brief Determines #a value modulo #a r.
*
* #pre value >= 0 and value < 2 * r
* #post value >= 0 and value < r
*/
template<class T> static T calc(T value, std::true_type) { return value & (r - 1); }
template<class T> static T calc(T value, std::false_type) {
STATIC_ASSERT(!std::numeric_limits<UIntType>::is_signed);
assert(value < 2 * r);
if (value >= r)
value -= r;
return value;
}
};
template<std::uint_least32_t b, std::uint_least32_t c>
struct MatsumotoKuritaTempering
{
template<std::size_t r, class UIntType, std::size_t N>
static UIntType apply(UIntType x, UIntType (&)[N], std::size_t)
{
x ^= (x << 7) & b;
x ^= (x << 15) & c;
return x;
}
};
template<std::uint_least32_t mask>
struct HaraseTempering
{
template<std::size_t r, class UIntType, std::size_t N>
static UIntType apply(UIntType x, UIntType (&s)[N], std::size_t m2)
{
return x ^ (s[Modulo<UIntType, r>::calc(m2 + 1)] & mask);
}
};
struct NoTempering
{
template<std::size_t r, class UIntType, std::size_t N>
static UIntType apply(UIntType x, UIntType (&)[N], std::size_t)
{
return x;
}
};
} // namespace Detail
//! #endcond
/**
* #brief Well Equidistributed Long-period Linear (WELL) pseudo-random number
* generator.
*
* The implementation is based on the "Improved Long-Period Generators Based on
* Linear Recurrences Modulo 2" paper by Francois Panneton, Pierre L'Ecuyer and
* Makoto Matsumoto from ACM Transactions on Mathematical Software, 32 (1,
* March) 2006, pp. 1-16.
*
* #tparam UIntType The unsigned integer type.
* #tparam w Word size.
* #tparam r State size.
*/
template
<
class UIntType,
std::size_t w,
std::size_t r,
std::size_t p,
std::size_t m1,
std::size_t m2,
std::size_t m3,
class T0,
class T1,
class T2,
class T3,
class T4,
class T5,
class T6,
class T7,
class Tempering // mpl pluggable
>
class Well
{
STATIC_ASSERT(!std::numeric_limits<UIntType>::is_signed);
STATIC_ASSERT(w <= static_cast<std::size_t>(std::numeric_limits<UIntType>::digits));
STATIC_ASSERT(r > 0 && p < w);
STATIC_ASSERT(m1 > 0 && m1 < r);
STATIC_ASSERT(m2 > 0 && m2 < r);
STATIC_ASSERT(m3 > 0 && m3 < r);
public:
//! The unsigned integer type.
typedef UIntType result_type;
//! Word size.
static constexpr std::size_t word_size = w;
//! State size.
static constexpr std::size_t state_size = r;
//! Number of mask bits.
static constexpr std::size_t mask_bits = p;
//! Default seed value.
static constexpr UIntType default_seed = 5489U;
/**
* #brief Initializes the class using the specified seed #a value.
*
* #param value The seed value to be used for state initialization.
*/
explicit Well(result_type value = default_seed)
{
seed(value);
}
template<class InputIterator>
Well(InputIterator& first, InputIterator last)
{
seed(first, last);
}
template<class Generator>
explicit Well(Generator& g)
{
seed(g);
}
template<class Generator>
void seed(Generator& g)
{
// Ensure std::generate_n doesn't copy the generator g by using
// std::reference_wrapper
std::generate_n(state_, state_size, std::ref(g));
}
void seed(result_type value = default_seed)
{
if (value == 0U)
value = default_seed;
state_[0] = value;
std::size_t i = 1;
UIntType *const s = state_;
// Same generator used to seed Mersenne twister
for ( ; i != state_size; ++i)
s[i] = (1812433253U * (s[i - 1] ^ (s[i - 1] >> (w - 2))) + i);
index_ = i;
}
template<class InputIterator>
void seed(InputIterator& first, InputIterator last)
{
index_ = 0;
std::size_t i = 0;
for ( ; i != state_size && first != last; ++i, ++first)
state_[i] = *first;
if (first == last && i != state_size)
throw std::invalid_argument("Seed sequence too short");
}
/**
* #brief Generates a random number.
*/
result_type operator()()
{
const UIntType upper_mask = ~0U << p;
const UIntType lower_mask = ~upper_mask;
// v[i,j] = state[(r-i+j) mod r]
std::size_t i = index_;
// Equivalent to r-i but allows to avoid negative values in the
// following two expressions
std::size_t j = i + r;
std::size_t k = mod(j - 1); // [i,r-1]
std::size_t l = mod(j - 2); // [i,r-2]
std::size_t im1 = i + m1;
std::size_t im2 = i + m2;
std::size_t im3 = i + m3;
UIntType z0, z1, z2, z3, z4;
z0 = (state_[k] & upper_mask) | (state_[l] & lower_mask);
z1 = T0::transform(state_[i]) ^
T1::transform(state(im1));
z2 = T2::transform(state(im2)) ^
T3::transform(state(im3));
z3 = z1 ^ z2;
z4 = T4::transform(z0) ^ T5::transform(z1) ^
T6::transform(z2) ^ T7::transform(z3);
state_[i] = z3; // v[i+1,1]
state_[k] = z4; // v[i+1,0]
index_ = k;
return Tempering::template apply<r>(z4, state_, im2);
}
result_type min PREVENT_MACRO_SUBSTITUTION () const
{
return 0U;
}
result_type max PREVENT_MACRO_SUBSTITUTION () const
{
return ~0U >> (std::numeric_limits<UIntType>::digits - w);
}
void discard(Detail::ulong_long_type z)
{
while (z-- > 0) {
operator()();
}
}
/**
* #brief Compares the state of two generators for equality.
*/
friend bool operator==(const Well& lhs, const Well& rhs)
{
for (std::size_t i = 0; i != state_size; ++i)
if (lhs.compute(i) != rhs.compute(i))
return false;
return true;
}
/**
* #brief Compares the state of two generators for inequality.
*/
friend bool operator!=(const Well& lhs, const Well& rhs)
{
return !(lhs == rhs);
}
/**
* #brief Writes the state to the specified stream.
*/
template<class E, class T>
friend std::basic_ostream<E, T>&
operator<<(std::basic_ostream<E, T>& out, const Well& well)
{
E space = out.widen(' ');
for (std::size_t i = 0; i != state_size; ++i)
out << well.compute(i) << space;
return out;
}
/**
* #brief Reads the generator state from the specified input stream.
*/
template<class E, class T>
friend std::basic_istream<E, T>&
operator>>(std::basic_istream<E, T>& in, Well& well)
{
for (std::size_t i = 0; i != state_size; ++i)
in >> well.state_[i] >> std::ws;
well.index_ = state_size;
return in;
}
private:
template<class T>
static T mod(T value)
{
return Detail::Modulo<T, r>::calc(value);
}
UIntType state(std::size_t index) const
{
return state_[mod(index)];
}
UIntType compute(std::size_t index) const
{
return state_[(index_ + index + r) % r];
}
UIntType state_[r];
std::size_t index_;
};
namespace Detail {
/**
* #name Base definitions with pluggable tempering method
* #{
*/
template <typename Tempering>
using Well512a_base = Well<
std::uint32_t, 32, 16, 0, 13, 9, 5, M3<-16>, M3<-15>, M3<11>, M0, M3<-2>, M3<-18>, M2<-28>,
M5<-5, 0xda442d24>, Tempering>;
template <typename Tempering>
using Well521a_base = Well<
std::uint32_t, 32, 17, 23, 13, 11, 10, M3<-13>, M3<-15>, M1, M2<-21>,
M3<-13>, M2<1>, M0, M3<11>, Tempering>;
template <typename Tempering>
using Well521b_base = Well<
std::uint32_t, 32, 17, 23, 11, 10, 7, M3<-21>, M3<6>, M0, M3<-13>, M3<13>,
M2<-10>, M2<-5>, M3<13>, Tempering>;
template <typename Tempering>
using Well607a_base = Well<
std::uint32_t, 32, 19, 1, 16, 15, 14, M3<19>, M3<11>, M3<-14>, M1, M3<18>,
M1, M0, M3<-5>, Tempering>;
template <typename Tempering>
using Well607b_base = Well<
std::uint32_t, 32, 19, 1, 16, 18, 13, M3<-18>, M3<-14>, M0, M3<18>,
M3<-24>, M3<5>, M3<-1>, M0, Tempering>;
template <typename Tempering>
using Well800a_base = Well<
std::uint32_t, 32, 25, 0, 14, 18, 17, M1, M3<-15>, M3<10>, M3<-11>, M3<16>,
M2<20>, M1, M3<-28>, Tempering>;
template <typename Tempering>
using Well800b_base = Well<
std::uint32_t, 32, 25, 0, 9, 4, 22, M3<-29>, M2<-14>, M1, M2<19>, M1,
M3<10>, M4<0xd3e43ffd>, M3<-25>, Tempering>;
template <typename Tempering>
using Well1024a_base = Well<
std::uint32_t, 32, 32, 0, 3, 24, 10, M1, M3<8>, M3<-19>, M3<-14>, M3<-11>,
M3<-7>, M3<-13>, M0, Tempering>;
template <typename Tempering>
using Well1024b_base = Well<
std::uint32_t, 32, 32, 0, 22, 25, 26, M3<-21>, M3<17>, M4<0x8bdcb91e>,
M3<15>, M3<-14>, M3<-21>, M1, M0, Tempering>;
template <typename Tempering>
using Well19937a_base = Well<
std::uint32_t, 32, 624, 31, 70, 179, 449, M3<-25>, M3<27>, M2<9>, M3<1>,
M1, M3<-9>, M3<-21>, M3<21>, Tempering>;
template <typename Tempering>
using Well19937b_base = Well<
std::uint32_t, 32, 624, 31, 203, 613, 123, M3<7>, M1, M3<12>, M3<-10>,
M3<-19>, M2<-11>, M3<4>, M3<-10>, Tempering>;
template <typename Tempering>
using Well21701a_base = Well<
std::uint32_t, 32, 679, 27, 151, 327, 84, M1, M3<-26>, M3<19>, M0, M3<27>,
M3<-11>, M6<32, 15, 0x86a9d87e, 0xffffffef, 0x00200000>, M3<-16>,
Tempering>;
template <typename Tempering>
using Well23209a_base = Well<
std::uint32_t, 32, 726, 23, 667, 43, 462, M3<28>, M1, M3<18>, M3<3>,
M3<21>, M3<-17>, M3<-28>, M3<-1>, Tempering>;
template <typename Tempering>
using Well23209b_base = Well<
std::uint32_t, 32, 726, 23, 610, 175, 662, M4<0xa8c296d1>, M1, M6<32, 15,
0x5d6b45cc, 0xfffeffff, 0x00000002>, M3<-24>, M3<-26>, M1, M0, M3<16>,
Tempering>;
template <typename Tempering>
using Well44497a_base = Well<
std::uint32_t, 32, 1391, 15, 23, 481, 229, M3<-24>, M3<30>, M3<-10>,
M2<-26>, M1, M3<20>, M6<32, 9, 0xb729fcec, 0xfbffffff, 0x00020000>, M1, Tempering>;
//! #}
} // namespace Detail
using Well512a = Detail::Well512a_base<Detail::NoTempering>;
using Well521a = Detail::Well521a_base<Detail::NoTempering>;
using Well521b = Detail::Well521b_base<Detail::NoTempering>;
using Well607a = Detail::Well607a_base<Detail::NoTempering>;
using Well607b = Detail::Well607b_base<Detail::NoTempering>;
using Well800a = Detail::Well800a_base<Detail::NoTempering>;
using Well800b = Detail::Well800b_base<Detail::NoTempering>;
using Well1024a = Detail::Well1024a_base<Detail::NoTempering>;
using Well1024b = Detail::Well1024b_base<Detail::NoTempering>;
using Well19937a = Detail::Well19937a_base<Detail::NoTempering>;
using Well19937b = Detail::Well19937b_base<Detail::NoTempering>;
using Well19937c = Detail::Well19937a_base<Detail::MatsumotoKuritaTempering<0xe46e1700, 0x9b868000>>;
using Well21701a = Detail::Well21701a_base<Detail::NoTempering>;
using Well23209a = Detail::Well23209a_base<Detail::NoTempering>;
using Well23209b = Detail::Well23209b_base<Detail::NoTempering>;
using Well44497a = Detail::Well44497a_base<Detail::NoTempering>;
using Well44497b = Detail::Well44497a_base<Detail::MatsumotoKuritaTempering<0x93dd1400, 0xfa118000>>;
/**
* #name Maximally equidistributed versions using Harase's tempering method
* #{
*/
using Well800a_ME = Detail::Well800a_base<Detail::HaraseTempering<0x4880>>;
using Well800b_ME = Detail::Well800b_base<Detail::HaraseTempering<0x17030806>>;
using Well19937a_ME = Detail::Well19937a_base<Detail::HaraseTempering<0x4118000>>;
using Well19937b_ME = Detail::Well19937b_base<Detail::HaraseTempering<0x30200010>>;
using Well21701a_ME = Detail::Well21701a_base<Detail::HaraseTempering<0x1002>>;
using Well23209a_ME = Detail::Well23209a_base<Detail::HaraseTempering<0x5100000>>;
using Well23209b_ME = Detail::Well23209b_base<Detail::HaraseTempering<0x34000300>>;
using Well44497a_ME = Detail::Well44497a_base<Detail::HaraseTempering<0x48000000>>;
//! #}
#endif // WELL_HPP
// Copyright (c) Sergiu Dotenco 2010
// Copyright (c) Seth Heeren - made independent of BOOST using C++11 - 2017
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
/**
* #brief WELL PRNG implementation unit test.
* #file welltest.cpp
*/
#include <algorithm>
#include <memory>
#include <iostream>
// #include "well.hpp"
#define MESSAGE_PREAMBLE() (std::cerr << __FILE__ << ":" << __LINE__ << " ")
#define CHECK_EQUAL(expected,actual) do { \
auto&& _e = expected; \
auto&& _a = actual; \
if (_e != _a) \
MESSAGE_PREAMBLE() << "expected:" << #expected << " = " << _e << "\n" \
<< "\tactual:" << #actual << " = " << _a << "\n"; \
} while (0)
#define CHECK(test) do { if (!(test)) MESSAGE_PREAMBLE() << #test << "\n"; } while (0)
/**
* #brief Generic WELL test case.
*
* The test case performs the following checks:
* -# The last generated value is equal to the value generate by the reference
* implementation after #f$10^9#f$ iterations. The generator is seeded using
* an array filled with 1s.
* -# The #c min and #c max methods of the #ref Well generator return 0 and
* #f$2^{32}-1#f$ respectively.
*
* #tparam RandomNumberGenerator WELL PRNG implementation type.
* #tparam Expected The expected result after #f$10^9#f$ iterations.
*/
template
<
class RandomNumberGenerator,
typename RandomNumberGenerator::result_type Expected
>
class WellTestCase
{
RandomNumberGenerator rng;
typedef typename RandomNumberGenerator::result_type result_type;
result_type generate()
{
unsigned state[RandomNumberGenerator::state_size];
std::uninitialized_fill_n(state, RandomNumberGenerator::state_size, 1);
unsigned* p = state;
rng.seed(p, p + RandomNumberGenerator::state_size);
result_type x = 0;
int iterations = 1000000000;
while (iterations-- > 0)
x = rng();
return x;
}
public:
static void run()
{
WellTestCase c;
CHECK_EQUAL(c.generate(), Expected);
CHECK_EQUAL(c.rng.min(), 0U);
CHECK_EQUAL(c.rng.max(), ~0U);
CHECK_EQUAL(c.rng, c.rng);
CHECK(c.rng == c.rng);
}
};
/**
* #brief Defines the actual test case.
*
* #param name The name of the test case.
* #param type WELL pseudo-random generator type.
* #param expected The expected result after #f$10^9#f$ iterations.
*
* #hideinitializer
*/
int main() {
CHECK_EQUAL(16, Detail::shift<2>(64));
CHECK_EQUAL(64, Detail::shift<-2>(16));
CHECK_EQUAL(32, Detail::shift<0>(32));
CHECK(Detail::is_powerof2(512u));
CHECK(not Detail::is_powerof2(0u));
WellTestCase<Well512a, 0x2b3fe99e>::run();
#ifndef COLIRU // stay in execution time limits
WellTestCase<Well521a, 0xc9878363>::run();
WellTestCase<Well521b, 0xb75867f6>::run();
WellTestCase<Well607a, 0x7b5043ea>::run();
WellTestCase<Well607b, 0xaedee7da>::run();
WellTestCase<Well800a, 0x2bfe686f>::run();
WellTestCase<Well800b, 0xf009e1bd>::run();
WellTestCase<Well1024a, 0xd07f528c>::run();
WellTestCase<Well1024b, 0x867f7993>::run();
WellTestCase<Well19937a, 0xb33a2cd5>::run();
WellTestCase<Well19937b, 0x191de86a>::run();
WellTestCase<Well19937c, 0x243eaed5>::run();
WellTestCase<Well21701a, 0x7365a269>::run();
WellTestCase<Well23209a, 0x807dacb >::run();
WellTestCase<Well23209b, 0xf1a77751>::run();
WellTestCase<Well44497a, 0xfdd7c07b>::run();
WellTestCase<Well44497b, 0x9406547b>::run();
#endif
}
Using C++17, this code becomes way simpler and error messages are friendlier on the eye.
This is a sample implementation of Power2Modulo:
#include <type_traits>
template<class UIntType, UIntType r>
struct Power2Modulo
{
static_assert(std::is_unsigned_v<UIntType>);
static_assert((r & (r - 1)) == 0,
"The second parameter of this struct is required to be a power of 2");
template<class T>
[[nodiscard]] static constexpr T calc(T value)
{
return value & (r - 1);
}
};
You can use it like this:
int main()
{
/* This code fails to compile with friendly error message
Power2Modulo<unsigned, 12> x;
*/
// Using the static function
using Mod16 = Power2Modulo<unsigned, 16>;
static_assert(Mod16::calc(15) == 15);
static_assert(Mod16::calc(16) == 0);
static_assert(Mod16::calc(17) == 1);
// Using it like a member function
Power2Modulo<unsigned, 4> mod4;
static_assert(mod4.calc(15) == 3);
static_assert(mod4.calc(16) == 0);
static_assert(mod4.calc(17) == 1);
}
Tested with clang-6 and gcc-8 and VisualC++ (via http://webcompiler.cloudapp.net/).
I have a couple of const C++-Arrays which I'd like to initialize with different data, but the prefix will always be the same. This example compiles:
const int array_1[] = { 1, 2, 3, 5, 5 };
const int array_2[] = { 1, 2, 3, 8, 7, 6 };
// ...
Is it possible to not specify the prefix (1, 2, 3) everytime? This compiles and illustrates it, with the drawback of using a macro:
#define prefix 1, 2, 3
const int array_1[] = { prefix, 5, 5 };
const int array_2[] = { prefix, 8, 7, 6 };
Requirements:
No macros.
You can use C++11, or if needed, C++14. Please specify what version is necessary.
std::array instead of C-style arrays is allowed.
C++11:
#include <array>
#include <utility>
template<int... Is> struct seq {};
template<int N, int... Is> struct gen_seq : gen_seq<N-1, N-1, Is...> {};
template<int... Is> struct gen_seq<0, Is...> : seq<Is...> {};
template<class T, int N, class... Rs, int... Is>
constexpr auto append(seq<Is...>, T (&lhs)[N], Rs&&... rhs)
-> std::array<T, N+sizeof...(Rs)>
{
return {{lhs[Is]..., std::forward<Rs>(rhs)...}};
}
template<class T, int N, class... Rs>
constexpr auto append(T (&lhs)[N], Rs&&... rhs)
-> decltype( append(gen_seq<N>{}, lhs, std::forward<Rs>(rhs)...) )
{
return append(gen_seq<N>{}, lhs, std::forward<Rs>(rhs)...);
}
constexpr int prefix[] = {1,2,3};
constexpr auto array_1 = append(prefix, 5, 5);
constexpr auto array_2 = append(prefix, 8, 7, 6);
#include <iostream>
int main()
{
std::cout << "array_1: ";
for(auto const& e : array_1) std::cout << e << ", ";
std::cout << "\n";
std::cout << "array_2: ";
for(auto const& e : array_2) std::cout << e << ", ";
}
As for me I have no other idea except to use a macro. For example
#define COMMON_PART 1, 2, 3
const int array_1[] = { COMMON_PART, 5, 5 };
const int array_2[] = { COMMON_PART, 8, 7, 6 };
Imagine you have a simple matrix class
template <typename T = double>
class Matrix {
T* data;
size_t row, col;
public:
Matrix(size_t m, size_t n) : row(m), col(n), data(new T[m*n]) {}
//...
friend std::ostream& operator<<(std::ostream& os, const Matrix& m) {
for (int i=0; i<m.row; ++i) {
for (int j=0; j<m.col; ++j)
os<<" "<<m.data[i + j*m.row];
os<<endl;
}
return os;
}
};
Is there a way that I can initialize this matrix with an initializer list? I mean to obtain the sizes of the matrix and the elements from an initializer list. Something like the following code:
Matrix m = { {1., 3., 4.}, {2., 6, 2.}};
would print
1 3 4
2 6 2
Looking forward to your answers. Thank you all.
aa
EDIT
So I worked on your suggestions to craft a somewhat generic array that initializes elements using initializer lists. But this is the most generic I could obtain.
I would appreciate if any of you have any suggestions as to make it a more generic class.
Also, a couple of questions:
Is it fine that a derived class initializes the state of the base class? I'm not calling the base constructor because of this, but should I call it anyways?
I defined the destructor a the Generic_base class as protected, is this the right way to do it?
Is there any foreseeable way to carry out the code that belongs to the constructor of the initializer in a more generic way? I mean to have one general constructor that takes care of all cases?
I included just the necessary code to illustrate the use of initializer lists in construction. When going to higher dimensions it gets messy, but I did one just to check the code.
#include <iostream>
#include <cassert>
using std::cout;
using std::endl;
template <int d, typename T>
class Generic_base {
protected:
typedef T value_type;
Generic_base() : n_(), data_(nullptr){}
size_t n_[d] = {0};
value_type* data_;
};
template <int d, typename T>
class Generic_traits;
template <typename T>
class Generic_traits<1,T> : public Generic_base<1,T> {
protected:
typedef T value_type;
typedef Generic_base<1,T> base_type;
typedef std::initializer_list<T> initializer_type;
using base_type::n_;
using base_type::data_;
public:
Generic_traits(initializer_type l) {
assert(l.size() > 0);
n_[0] = l.size();
data_ = new T[n_[0]];
int i = 0;
for (const auto& v : l)
data_[i++] = v;
}
};
template <typename T>
class Generic_traits<2,T> : public Generic_base<2,T> {
protected:
typedef T value_type;
typedef Generic_base<2,T> base_type;
typedef std::initializer_list<T> list_type;
typedef std::initializer_list<list_type> initializer_type;
using base_type::n_;
using base_type::data_;
public:
Generic_traits(initializer_type l) {
assert(l.size() > 0);
n_[0] = l.size();
n_[1] = l.begin()->size();
data_ = new T[n_[0]*n_[1]];
int i = 0, j = 0;
for (const auto& r : l) {
assert(r.size() == n_[1]);
for (const auto& v : r) {
data_[i + j*n_[0]] = v;
++j;
}
j = 0;
++i;
}
}
};
template <typename T>
class Generic_traits<4,T> : public Generic_base<4,T> {
protected:
typedef T value_type;
typedef Generic_base<4,T> base_type;
typedef std::initializer_list<T> list_type;
typedef std::initializer_list<list_type> llist_type;
typedef std::initializer_list<llist_type> lllist_type;
typedef std::initializer_list<lllist_type> initializer_type;
using base_type::n_;
using base_type::data_;
public:
Generic_traits(initializer_type l) {
assert(l.size() > 0);
assert(l.begin()->size() > 0);
assert(l.begin()->begin()->size() > 0);
assert(l.begin()->begin()->begin()->size() > 0);
size_t m = n_[0] = l.size();
size_t n = n_[1] = l.begin()->size();
size_t o = n_[2] = l.begin()->begin()->size();
n_[3] = l.begin()->begin()->begin()->size();
data_ = new T[m*n*o*n_[3]];
int i=0, j=0, k=0, p=0;
for (const auto& u : l) {
assert(u.size() == n_[1]);
for (const auto& v : u) {
assert(v.size() == n_[2]);
for (const auto& x : v) {
assert(x.size() == n_[3]);
for (const auto& y : x) {
data_[i + m*j + m*n*k + m*n*o*p] = y;
++p;
}
p = 0;
++k;
}
k = 0;
++j;
}
j = 0;
++i;
}
}
};
template <int d, typename T>
class Generic : public Generic_traits<d,T> {
public:
typedef Generic_traits<d,T> traits_type;
typedef typename traits_type::base_type base_type;
using base_type::n_;
using base_type::data_;
typedef typename traits_type::initializer_type initializer_type;
// initializer list constructor
Generic(initializer_type l) : traits_type(l) {}
size_t size() const {
size_t n = 1;
for (size_t i=0; i<d; ++i)
n *= n_[i];
return n;
}
friend std::ostream& operator<<(std::ostream& os, const Generic& a) {
for (int i=0; i<a.size(); ++i)
os<<" "<<a.data_[i];
return os<<endl;
}
};
int main()
{
// constructors for initializer lists
Generic<1, double> y = { 1., 2., 3., 4.};
cout<<"y -> "<<y<<endl;
Generic<2, double> C = { {1., 2., 3.}, {4., 5., 6.} };
cout<<"C -> "<<C<<endl;
Generic<4, double> TT = { {{{1.}, {7.}, {13.}, {19}}, {{2}, {8}, {14}, {20}}, {{3}, {9}, {15}, {21}}}, {{{4.}, {10}, {16}, {22}}, {{5}, {11}, {17}, {23}}, {{6}, {12}, {18}, {24}}} };
cout<<"TT -> "<<TT<<endl;
return 0;
}
Which prints as expected:
y -> 1 2 3 4
C -> 1 4 2 5 3 6
TT -> 1 4 2 5 3 6 7 10 8 11 9 12 13 16 14 17 15 18 19 22 20 23 21 24
Why not?
Matrix(std::initializer_list<std::initializer_list<T>> lst) :
Matrix(lst.size(), lst.size() ? lst.begin()->size() : 0)
{
int i = 0, j = 0;
for (const auto& l : lst)
{
for (const auto& v : l)
{
data[i + j * row] = v;
++j;
}
j = 0;
++i;
}
}
And as stardust_ suggests - you should use vectors, not arrays here.
The main issue with using initializer lists to tackle this problem, is that their size is not easily accessible at compile time. It looks like this particular class is for dynamic matrices, but if you wanted to do this on the stack (usually for speed/locality reasons), here is a hint at what you need (C++17):
template<typename elem_t, std::size_t ... dim>
struct matrix
{
template<std::size_t ... n>
constexpr matrix(const elem_t (&...list)[n]) : data{}
{
auto pos = &data[0];
((pos = std::copy(list, list + n, pos)), ...);
}
elem_t data[(dim * ... * 1)];
};
template<typename ... elem_t, std::size_t ... n>
matrix(const elem_t (&...list)[n]) -> matrix<std::common_type_t<elem_t...>, sizeof...(n), (n * ... * 1) / sizeof...(n)>;
I had to tackle this same problem in my linear algebra library, so I understand how unintuitive this is at first. But if you instead pass a C-array into your constructor, you will have both type and size information of the values you've passed in. Also take note of the constuctor template argument deduction (CTAD) to abstract away the template arguments.
You can then create constexpr matrix objects like this (or, leave out constexpr to simply do this at runtime on the stack):
constexpr matrix mat{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12} };
Which will initialize an object at compile time of type:
const matrix<int, 4, 3>
If C++20 is supported by your compiler, I would recommend adding a "requires" clause to the CTAD to ensure that all sub-arrays are the same size (mathematically-speaking, n1 == n2 == n3 == n4, etc).
Using std::vector::emplace_back() (longer)
Using std::vector, instead of plain old array, you can use std::vector::emplace_back() to fill the vector:
template <typename T = double>
class Matrix {
std::vector<T> data;
size_t row{}, col{}; // Non-static member initialization
public:
Matrix(size_t m, size_t n) : data(std::vector<T>(m * n)), row(m), col(n)
{ // ^ Keep the order in which the members are declared
}
Matrix(std::initializer_list<std::initializer_list<T>> lst)
: row(lst.size())
, col(lst.size() ? lst.begin()->size() : 0) // Minimal validation
{
// Eliminate reallocations as we already know the size of matrix
data.reserve(row * col);
for (auto const& r : lst) {
for (auto const &c : r) {
data.emplace_back(c);
}
}
}
};
int main() {
Matrix<double> d = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
}
Using std::vector::insert() (better and shorter)
As #Bob mentioned in a comment, you can use std::vector::insert() member function, instead of the inner emplace_back loop:
template <typename T = double>
class Matrix {
std::vector<T> data;
size_t row{}, col{}; // Non-static member initialization
public:
Matrix(size_t m, size_t n) : data(std::vector<T>(m * n)), row(m), col(n)
{ // ^ Keep the order in which the members are declared
}
Matrix(std::initializer_list<std::initializer_list<T>> lst)
: row{lst.size()}
, col{lst.size() ? lst.begin()->size() : 0} // Minimal validation
{
// Eliminate reallocations as we already know the size of the matrix
data.reserve(row * col);
for (auto const& r : lst) {
data.insert(data.end(), r.begin(), r.end());
}
}
};
int main() {
Matrix<double> d = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
}
So, we're saying: For each row (r) in the lst, insert the content of the row from the beginning (r.begin()) to the end (r.end()) into the end of the empty vector, data, (in an empty vector semantically we have: empty_vec.begin() == empty_vec.end()).
i might be a bit late but here is code for generally initializing tensors, regardless if they are matricies or vectors or whatever tensor.You could restrict it by throwing runtime errors when its not a matrix. Below is the source code to extract the data from the initilizer_list its a bit hacky. The whole trick is that the constructor are implicitly called with the correct type.
#include <initializer_list>
#include <iostream>
using namespace std;
class ShapeElem{
public:
ShapeElem* next;
int len;
ShapeElem(int _len,ShapeElem* _next): next(_next),len(_len){}
void print_shape(){
if (next != nullptr){
cout <<" "<< len;
next->print_shape();
}else{
cout << " " << len << "\n";
}
}
int array_len(){
if (next != nullptr){
return len*next->array_len();
}else{
return len;
}
}
};
template<class value_type>
class ArrayInit{
public:
void* data = nullptr;
size_t len;
bool is_final;
ArrayInit(std::initializer_list<value_type> init) : data((void*)init.begin()), len(init.size()),is_final(true){}
ArrayInit(std::initializer_list<ArrayInit<value_type>> init): data((void*)init.begin()), len(init.size()),is_final(false){}
ShapeElem* shape(){
if(is_final){
ShapeElem* out = new ShapeElem(len,nullptr);
}else{
ArrayInit<value_type>* first = (ArrayInit<value_type>*)data;
ShapeElem* out = new ShapeElem(len,first->shape());
}
}
void assign(value_type** pointer){
if(is_final){
for(size_t k = 0; k < len;k ++ ){
(*pointer)[k] = ( ((value_type*)data)[k]);
}
(*pointer) = (*pointer) + len;
}else{
ArrayInit<value_type>* data_array = (ArrayInit<value_type>*)data;
for(int k = 0;k < len;k++){
data_array[k].assign(pointer);
}
}
}
};
int main(){
auto x = ArrayInit<int>({{1,2,3},{92,1,3}});
auto shape = x.shape();
shape->print_shape();
int* data = new int[shape->array_len()];
int* running_pointer = data;
x.assign(&running_pointer);
for(int i = 0;i < shape->array_len();i++){
cout << " " << data[i];
}
cout << "\n";
}
outputs
2 3
1 2 3 92 1 3
The shape() function will return you the shape of the tensor at each dimension. The array is exactly saved as it is written down. It's really import to create something like shape since this will give you the ordering in which the elements are.
If you want a specific index out of the tensor lets say a[1][2][3]
the correct position is in 1*a.shape[1]a.shape[2] + 2a.shape[2] + 3
Some minor details and tricks can be found in: https://github.com/martinpflaum/multidimensional_array_cpp