Compile time array indexing using expression templates -- constexpr? - c++

I want to generate a function like this:
double apply_stencil(const double *u, const int i, const width,
const int *offset, const double *weight)
{
double t=0;
for(int j=0; j<width; j++)
t += u[i+offset[j]] * weight[j];
return t;
}
But I want to make sure that the width, the offsets, and possibly even the weights are compile/time constants.
That is possible to achieve by defining a type:
template <typename S>
double apply_stencil(const double *u, const int i)
{
double t=0;
for(int j=0; j<S::width; j++)
t += u[i+S::offset[j]] * S::weight[j];
return t;
}
// then:
struct Stencil {
const static double weight[];
const static int offset[];
const static unsigned int width = 3;
};
const double Stencil::weight[] = {1.0, 2.0, 1.0};
const int Stencil::offset[] = {-1, 0, 1};
However, this is not very pretty. I want a user to be able to specify the Stencil in their application code, and then call my apply_stencil function from my header file (this is really a simplification of something much more complicated).
Ideally, I would like to have the thing specified using expression templates, like so:
const Symbol u;
const Stencil<3> s (1*u[-1] + 2*u[0] + 1*u[1]);
Which uses this infrastructure:
struct Accessor {
int offset;
};
struct Symbol
{
Accessor operator[](int i) const {
return Accessor{i};
}
};
struct WeightedTerm
{
double weight;
int offset;
WeightedTerm()
: weight(1), offset(0) {}
WeightedTerm(double a, Accessor u)
: weight(a), offset(u.offset) {}
};
WeightedTerm operator*(double a, Accessor u) {
return WeightedTerm(a,u);
}
template <int n>
struct Sum
{
WeightedTerm terms[n];
Sum(WeightedTerm x, WeightedTerm y) {
terms[0] = x;
terms[1] = y;
}
Sum(Sum<n-1> x, WeightedTerm y) {
for(int i = 0; i < n-1; ++i)
terms[i] = x.terms[i];
terms[n-1] = y;
}
};
Sum<2> operator+(WeightedTerm x, WeightedTerm y) {
return Sum<2>(x,y);
}
template <int n>
Sum<n+1> operator+(Sum<n> x, WeightedTerm y) {
return Sum<n+1>(x,y);
}
template <int width>
struct Stencil
{
double weights[width];
int offsets[width];
Stencil(const Sum<width> s) {
for(int j = 0; j < width; ++j) {
weights[j] = s.terms[j].weight;
offsets[j] = s.terms[j].offset;
}
};
};
This looks nice, but now, the arrays are not necessarily compile time known. If I write it like I do above with literals in the expression, I have verified that the compiler can make the correct optimizations. But I want to make find a way to guarantee that they are always compile time constants.
I presume I could encode the offset as a template parameter in Accessor and WeightedTerm, but I can't see how I could do that and keep the desired expression syntax since operator() takes the offset as a regular argument.
So, the question is if there is a way to achieve this? I have a feeling that constexpr could be of use here which I am a bit unfamiliar with.

Use a std::integral_constant alias. If you want ease of use, use user defined literals.
constexpr int int_from_chars(){return 0;}
constexpr int k_pow(unsigned int x, unsigned int b=10){
return (x==0)?1:(k_pow(x-1,b)*b);
}
template<class...Chars>
constexpr int int_from_chars(char x, Chars...chars){
return k_pow(sizeof...(Chars))*(x-'0') + int_from_chars(chars...);
}
template<char...Cs>
constexpr auto operator""_kint(){
return std::integral_constant<int, int_from_chars(Cs...)>{};
}
or somesuch. Needs polish.
Now 7_kint is a compile time type encoding 7.
You can take the integral_constant<int, K> as a function argument, where K is a template non type argument.
Extending to octal/hex/binary left as an exercise.
template<int width>
double apply_stencil(const double *u, const int i, std::integral_constant<int,width>,
const int *offset, const double *weight)
{
double t=0;
for(int j=0; j<width; j++)
t += u[i+offset[j]] * weight[j];
return t;
}
Called via
apply_stencil( ptr, i, 7_kint, poffsets, pweights);
Similar (but more complex) techniques can be used to pass packs of offsets. Weights are a mess, as there is little compile time double support.

Related

Template function that returns object with random values

I would like to have a template function that returns an object of whatever class with random values. I tried the function below but it doesn't work, because tab is read as a uint32_t* rather than a sequence of numberOfCalls values of type uint32_t.
std::random_device rd;
template <typename Type>
Type RandomObject(void)
{
constexpr auto numberOfCalls = (sizeof(Type) + sizeof(uint32_t) - 1u)/sizeof(uint32_t);
uin32_t tab[numberOfCalls];
for (auto i = 0u; i < numberOfCalls; ++i)
tab[i] = rd();
return reinterpret_cast<Type>(tab);
}
Here's an example of something I would like to do.
struct Test
{
uint64_t s;
uint64_t t;
uint32_t x;
uint32_t y;
uint32_t z;
};
int32_t main(void)
{
const auto foo = ::RandomObject<Test>();
// do things
return 0;
}
Of course I could simply implement RandomObject<Test> but I'd much prefer having one robust template function that works for everything.

Porting C code to C++, problem with casting void* from malloc to desired pointer

I am currently porting some C code I wrote to C++ for fun. I am struggling with a malloc() call I make in C, with h and w being constants for simplicity reasons, but later exchanged with runtime constants:
double (*g2)[h][w] = malloc(h * w * sizeof(double));
In C, this is an implicit conversion of a void*, and this of course doesn't fly with C++.
I already tried casting this with reinterpret_cast<double[h][w]>, but this is still an invalid cast.
I was wondering, how can I make this work in C++ since this would save me a lot of work?
As an alternative I'll probably use a matrix class with indirection:
struct Matrix : std::vector<double> {
unsigned matSize;
std::vector<double*> indirection;
Matrix() : matSize(0) {}
Matrix(unsigned n) : matSize(n) {
resize(n*n);
indirection.resize(n);
for(unsigned i = 0; i < n; ++i) {
indirection[i] = &(*this)[i*n];
}
}
double& operator()(unsigned i, unsigned j) {
return indirection[i][j];
}
const double& operator()(unsigned i, unsigned j) const {
return indirection[i][j];
}
};
Porting involves more than just making it work, line by line, so:
C:
double (*g2)[h][w] = malloc(h * w * sizeof(double));
...
g2[y][x] = ...;
C++:
std::vector<double> g2(h*w);
...
g2[y+x*h] = ...; // or
g2[y*w+x] = ...;
Using that syntax is inconvenient for accessing elements so you might want to wrap it inside a simple class. Example:
#include <iostream>
#include <iterator>
#include <vector>
class arr2d {
public:
arr2d(size_t h, size_t w) : data_(h * w), w_(w) {}
inline double& operator()(size_t y, size_t x) {
return data_[y * w_ + x];
}
inline double operator()(size_t y, size_t x) const {
return data_[y * w_ + x];
}
// getting pointer to a row
inline double* operator[](size_t y) {
return &data_[y * w_];
}
inline double const* operator[](size_t y) const {
return &data_[y * w_];
}
inline size_t width() const { return w_; }
private:
std::vector<double> data_;
size_t w_;
};
int main() {
arr2d g2(3, 4);
g2(2, 3) = 3.14159;
// alternative access:
g2[1][2] = 1.23456;
std::cout << g2[2][3] << "\n";
double* row = g2[2];
std::copy(row, row + g2.width(), std::ostream_iterator<double>(std::cout, ", "));
std::cout << "\n";
}
Output:
3.14159
0, 0, 0, 3.14159,
A non-initializing version could look like:
class arr2d {
public:
arr2d(size_t h, size_t w) : data_(new double[w * h]), w_(w) {}
inline double& operator()(size_t y, size_t x) { return data_[y * w_ + x]; }
inline double operator()(size_t y, size_t x) const { return data_[y * w_ + x]; }
inline double* operator[](size_t y) { return &data_[y * w_]; }
inline double const* operator[](size_t y) const { return &data_[y * w_]; }
inline size_t width() const { return w_; }
private:
std::unique_ptr<double[]> data_;
size_t w_;
};
But note that the
std::copy(row, row + g2.width(), std::ostream_iterator<double>(std::cout, ", "));
from the first example would lead to undefined behaviour.
Also note that this version will delete the copy constructor and copy assignment operator. You'll have to implement them yourself if you need them.
The creation time for the non-initializing version is of course hard to beat with any initializing version, but for access times, one might think that a lookup table, or indirection as you call it, for the rows would speed up things compared to doing the multiplication and addition in one go.
My results:
8x8 http://quick-bench.com/f8zcnU9P8oKwMUwLRXYKZnLtcLM
1024x1024 http://quick-bench.com/0B2rQeUkl-WoqGeG-iS1hdP4ah8
4096x4096 http://quick-bench.com/c_pGFmB2C9_B3r3aRl7cDK6BlxU
It seems to vary. The lookup version is faster for the 4096x4096 matrix but the naive version is faster for the two smaller ones. You need to compare using sizes close to what you'll be using and also check with different compilers. I sometimes get completely opposite "winners" when changing compiler.
Since you don't mind inheriting from std::vector or keeping extra data for a lookup-table, this could be an option. It seems to outperform the other versions slightly.
class arr2d : protected std::vector<double*> {
public:
using std::vector<double*>::operator[]; // "row" accessor from base class
arr2d(size_t h, size_t w) :
std::vector<double*>(h),
data_(new double[w * h]),
w_(w),
h_(h)
{
for(size_t y = 0; y < h; ++y)
(*this)[y] = &data_[y * w];
}
inline size_t width() const { return w_; }
inline size_t height() const { return h_; }
private:
std::unique_ptr<double[]> data_;
size_t w_, h_;
};
Here are Philipp-P's (OP:s) own measurements for the different 2D-array implementations:
8x8 http://quick-bench.com/vMS6a9F_KrUf97acWltjV5CFhLY
1024x1024 http://quick-bench.com/A8a2UKyHaiGMCrf3uranwOCwmkA
4096x4096 http://quick-bench.com/XmYQc0kAUWU23V3Go0Lucioi_Rg
Results for 5-point stencil code for the same versions:
8x8 http://quick-bench.com/in_ZQTbbhur0I4mu-NIquT4c0ew
1024x1024 http://quick-bench.com/tULLumHZeCmC0HUSfED2K4nEGG8
4096x4096 http://quick-bench.com/_MRNRZ03Favx91-5IXnxGNpRNwQ
In C++ it's not advised to manually allocate memory unless necessary. Let the standard library and templates do the work for you.
They can be very useful and are great to learn if you want to get into C++! You can save a lot of time this way and write some better code.
For example, what is this data type used for? If it fits for your usage, you may instead consider creating a 2D array using std::array:
std::array<std::array<double, w>, h>
If you need to re-size the arrays regularly, std::vector could be used instead. It has effectively the same performance as an array, given that is is all it is under the hood. You can reserve() or resize() as necessary and push_back uses a 1.5x increasing scheme and is good at its job.
EDIT: Since size is known, arrays may be better here. Taken suggestion from comments.
I recommend you using std::vector. Just wrap it if you want 2D array syntax.
#include <iostream>
#include <vector>
class Array2D {
std::vector<double> _v;
size_t _width;
size_t _height;
public:
Array2D(size_t height, size_t width, double initVal = 0.0)
: _v(width * height, initVal),
_width(width),
_height(height)
{}
double* operator[](size_t y) {
return _v.data() + y * _width;
}
};
int main(int, char**) {
size_t rows = 5;
size_t cols = 3;
Array2D a(rows, cols, 0.2);
for (size_t i = 0; i < cols; ++i)
a[4][i] = -0.1 * i;
std::cout << a[4][2] << std::endl; //-0.2
return 0;
}
You can do this, which should work in both C and C++:
double *g2 = (double*) malloc(h * w * sizeof(double));
Though, as others have stated, this is not the way to approach this issue in C++ in general. For instance, you should use a std::vector instead:
#include <vector>
std::vector<double> g2(h * w);
In both cases, you end up with a dynamically allocated 2D array of doubles in a single contiguous block of memory. And as such, you need to then use g2[(row*w)+col] syntax to access the individual elements, where 0 <= row < h and 0 <= col < w.

A lot of non-type template parameters

I have a few functions that are very performance critical. They are quite generic and they depend on about 12 parameters apart from 2 inputs.
These parameters are fixed and I have 4 or 5 set of values (a1..a10) (b1..b10), etc... I could write the function a few times but for efficiency and maintainability I want to use non-type templates.
Imagine something like:
template <int a, int b, int c, int d, ..., int m>
double f(double x, double y)
{
return a*x+a*b*y+c+d+..+a*x*y; // some very complex math code
}
and it is only used in these N ways:
f<1,2,3,...,6>(x,y)
f<4,5,6,...,60>(x,y)
f<10,20,....,50,60>(x,y)
(in another application of the library, the set of parameters might be different but still only a few)
This is all fine but not very elegant...
I am looking for some "nicer" way to group these parameters in a cleaner way.
Ideas:
- create many PARAMS types full of constexpr[s]
- An abstract class with methods to be overriden (not sure if I can mix that with constexpr..)
I was wondering if there is some other nicer way or something available in boost that would be a good match for my problem.
EDIT:
Something similar to this would be perfect! (clearly this is NOT working). And the most important thing is that I need compile time evaluation.
#include <iostream>
struct Params1
{
constexpr static int a = 2;
constexpr static double b = 4;
constexpr static int c = 6;
};
struct Params2
{
constexpr static int a = 1;
constexpr static double b = 4.3;
constexpr static int c = 3;
};
template<P>
double f(double x)
{
return x*P.a*P.b*P.c;
};
int main() {
std::cout << f<Params1>(1.2) << std::endl;
std::cout << f<Params2>(1.2) << std::endl;
return 0;
}
Rewrite f() as follows and your last (post "edit") example should work
template <typename P>
double f(double x)
{
return x * P::a * P::b * P::c;
}
The points are
(a) change template <P> with template <typename P>
(b) and use P::a, P::b and P::c (static values inside a type) instead of P.a, P.b and P.c (values of an object)
Not sure to understand your requirement but...
These parameters are fixed and I have 4 or 5 set of values (a1..a10) (b1..b10), etc..
I suppose you could use std::integer_sequence and define 4 or 5 types
using set1 = std::integer_sequence<int, 1, 2, 3, ....>;
using set2 = std::integer_sequence<int, 2, 4, 6, ....>;
using set3 = std::integer_sequence<int, 10, 20, 30, ....>;
// ...
define the function this way
template <int a, int b, int c, int d, ..., int m>
double f (std::integer_sequence<int, a, ...> const &, double x, double y)
{ .. }
and call it as follows
f(set1{}, x, y);
f(set2{}, x, y);
f(set3{}, x, y);
// ...
The following is a full example (but only with 3 template integer)
#include <utility>
#include <iostream>
using set1 = std::integer_sequence<int, 1, 2, 3>;
using set2 = std::integer_sequence<int, 2, 4, 6>;
using set3 = std::integer_sequence<int, 10, 20, 30>;
template <int a, int b, int c>
double f(std::integer_sequence<int, a, b, c> const &, double x, double y)
{ return a*(x+y)+b*(x-y)+c*(y-x); }
int main ()
{
double x { 1.0 };
double y { 2.0 };
std::cout << f(set1{}, x, y) << std::endl; // print 4
std::cout << f(set2{}, x, y) << std::endl; // print 8
std::cout << f(set3{}, x, y) << std::endl; // print 40
}
Your given example code works quite well with some minor changes. Maybe thats what you are searching for?
struct Params1
{
constexpr static int a = 2;
constexpr static double b = 4;
constexpr static int c = 6;
};
struct Params2
{
constexpr static int a = 2;
constexpr static double b = 4;
constexpr static int c = 6;
};
template<typename P>
double f(double x)
{
return x*P::a*P::b*P::c;
}
int main() {
std::cout << f<Params1>(1.2) << std::endl;
std::cout << f<Params2>(1.2) << std::endl;
return 0;
}

How to initialise a floating point array at compile time?

I have found two good approaches to initialise integral arrays at compile times here and here.
Unfortunately, neither can be converted to initialise a float array straightforward; I find that I am not fit enough in template metaprogramming to solve this through trial-and-error.
First let me declare a use-case:
constexpr unsigned int SineLength = 360u;
constexpr unsigned int ArrayLength = SineLength+(SineLength/4u);
constexpr double PI = 3.1415926535;
float array[ArrayLength];
void fillArray(unsigned int length)
{
for(unsigned int i = 0u; i < length; ++i)
array[i] = sin(double(i)*PI/180.*360./double(SineLength));
}
As you can see, as far as the availability of information goes, this array could be declared constexpr.
However, for the first approach linked, the generator function f would have to look like this:
constexpr float f(unsigned int i)
{
return sin(double(i)*PI/180.*360./double(SineLength));
}
And that means that a template argument of type float is needed. Which is not allowed.
Now, the first idea that springs to mind would be to store the float in an int variable - nothing happens to the array indices after their calculation, so pretending that they were of another type than they are (as long as byte-length is equal) is perfectly fine.
But see:
constexpr int f(unsigned int i)
{
float output = sin(double(i)*PI/180.*360./double(SineLength));
return *(int*)&output;
}
is not a valid constexpr, as it contains more than the return statement.
constexpr int f(unsigned int i)
{
return reinterpret_cast<int>(sin(double(i)*PI/180.*360./double(SineLength)));
}
does not work either; even though one might think that reinterpret_cast does exactly what is needed here (namely nothing), it apparently only works on pointers.
Following the second approach, the generator function would look slightly different:
template<size_t index> struct f
{
enum : float{ value = sin(double(index)*PI/180.*360./double(SineLength)) };
};
With what is essentially the same problem: That enum cannot be of type float and the type cannot be masked as int.
Now, even though I have only approached the problem on the path of "pretend the float is an int", I do not actually like that path (aside from it not working). I would much prefer a way that actually handled the float as float (and would just as well handle a double as double), but I see no way to get around the type restriction imposed.
Sadly, there are many questions about this issue, which always refer to integral types, swamping the search for this specialised issue. Similarly, questions about masking one type as the other typically do not consider the restrictions of a constexpr or template parameter environment.
See [1][2][3] and [4][5] etc.
Assuming your actual goal is to have a concise way to initialize an array of floating point numbers and it isn't necessarily spelled float array[N] or double array[N] but rather std::array<float, N> array or std::array<double, N> array this can be done.
The significance of the type of array is that std::array<T, N> can be copied - unlike T[N]. If it can be copied, you can obtain the content of the array from a function call, e.g.:
constexpr std::array<float, ArrayLength> array = fillArray<N>();
How does that help us? Well, when we can call a function taking an integer as an argument, we can use std::make_index_sequence<N> to give use a compile-time sequence of std::size_t from 0 to N-1. If we have that, we can initialize an array easily with a formula based on the index like this:
constexpr double const_sin(double x) { return x * 3.1; } // dummy...
template <std::size_t... I>
constexpr std::array<float, sizeof...(I)> fillArray(std::index_sequence<I...>) {
return std::array<float, sizeof...(I)>{
const_sin(double(I)*M_PI/180.*360./double(SineLength))...
};
}
template <std::size_t N>
constexpr std::array<float, N> fillArray() {
return fillArray(std::make_index_sequence<N>{});
}
Assuming the function used to initialize the array elements is actually a constexpr expression, this approach can generate a constexpr. The function const_sin() which is there just for demonstration purpose does that but it, obviously, doesn't compute a reasonable approximation of sin(x).
The comments indicate that the answer so far doesn't quite explain what's going on. So, let's break it down into digestible parts:
The goal is to produce a constexpr array filled with suitable sequence of values. However, the size of the array should be easily changeable by adjusting just the array size N. That is, conceptually, the objective is to create
constexpr float array[N] = { f(0), f(1), ..., f(N-1) };
Where f() is a suitable function producing a constexpr. For example, f() could be defined as
constexpr float f(int i) {
return const_sin(double(i) * M_PI / 180.0 * 360.0 / double(Length);
}
However, typing in the calls to f(0), f(1), etc. would need to change with every change of N. So, essentially the same as the above declaration should be done but without extra typing.
The first step towards the solution is to replace float[N] by std:array<float, N>: built-in arrays cannot be copied while std::array<float, N> can be copied. That is, the initialization could be delegated to to a function parameterized by N. That is, we'd use
template <std::size_t N>
constexpr std::array<float, N> fillArray() {
// some magic explained below goes here
}
constexpr std::array<float, N> array = fillArray<N>();
Within the function we can't simply loop over the array because the non-const subscript operator isn't a constexpr. Instead, the array needs to be initialized upon creation. If we had a parameter pack std::size_t... I which represented the sequence 0, 1, .., N-1 we could just do
std::array<float, N>{ f(I)... };
as the expansion would effectively become equivalent to typing
std::array<float, N>{ f(0), f(1), .., f(N-1) };
So the question becomes: how to get such a parameter pack? I don't think it can be obtained directly in the function but it can be obtained by calling another function with a suitable parameter.
The using alias std::make_index_sequence<N> is an alias for the type std::index_sequence<0, 1, .., N-1>. The details of the implementation are a bit arcane but std::make_index_sequence<N>, std::index_sequence<...>, and friends are part of C++14 (they were proposed by N3493 based on, e.g., on this answer from me). That is, all we need to do is call an auxiliary function with a parameter of type std::index_sequence<...> and get the parameter pack from there:
template <std::size_t...I>
constexpr std::array<float, sizeof...(I)>
fillArray(std::index_sequence<I...>) {
return std::array<float, sizeof...(I)>{ f(I)... };
}
template <std::size_t N>
constexpr std::array<float, N> fillArray() {
return fillArray(std::make_index_sequence<N>{});
}
The [unnamed] parameter to this function is only used to have the parameter pack std::size_t... I be deduced.
Here's a working example that generates a table of sin values, and that you can easily adapt to logarithm tables by passing a different function object
#include <array> // array
#include <cmath> // sin
#include <cstddef> // size_t
#include <utility> // index_sequence, make_index_sequence
#include <iostream>
namespace detail {
template<class Function, std::size_t... Indices>
constexpr auto make_array_helper(Function f, std::index_sequence<Indices...>)
-> std::array<decltype(f(std::size_t{})), sizeof...(Indices)>
{
return {{ f(Indices)... }};
}
} // namespace detail
template<std::size_t N, class Function>
constexpr auto make_array(Function f)
{
return detail::make_array_helper(f, std::make_index_sequence<N>{});
}
static auto const pi = std::acos(-1);
static auto const make_sin = [](int x) { return std::sin(pi * x / 180.0); };
static auto const sin_table = make_array<360>(make_sin);
int main()
{
for (auto elem : sin_table)
std::cout << elem << "\n";
}
Live Example.
Note that you need to use -stdlib=libc++ because libstdc++ has a pretty inefficient implementation of index_sequence.
Also note that you need a constexpr function object (both pi and std::sin are not constexpr) to initialize the array truly at compile-time rather than at program initialization.
There are a few problems to overcome if you want to initialise a floating point array at compile time:
std::array is a little broken in that the operator[] is not constexpr in the case of a mutable constexpr std::array (I believe this will be fixed in the next release of the standard).
the functions in std::math are not marked constexpr!
I had a similar problem domain recently. I wanted to create an accurate but fast version of sin(x).
I decided to see if it could be done with a constexpr cache with interpolation to get speed without loss of accuracy.
An advantage of making the cache constexpr is that the calculation of sin(x) for a value known at compile-time is that the sin is pre-computed and simply exists in the code as an immediate register load! In the worst case of a runtime argument, it's merely a constant array lookup followed by w weighted average.
This code will need to be compiled with -fconstexpr-steps=2000000 on clang, or the equivalent in windows.
enjoy:
#include <iostream>
#include <cmath>
#include <utility>
#include <cassert>
#include <string>
#include <vector>
namespace cpputil {
// a fully constexpr version of array that allows incomplete
// construction
template<size_t N, class T>
struct array
{
// public constructor defers to internal one for
// conditional handling of missing arguments
constexpr array(std::initializer_list<T> list)
: array(list, std::make_index_sequence<N>())
{
}
constexpr T& operator[](size_t i) noexcept {
assert(i < N);
return _data[i];
}
constexpr const T& operator[](size_t i) const noexcept {
assert(i < N);
return _data[i];
}
constexpr T& at(size_t i) noexcept {
assert(i < N);
return _data[i];
}
constexpr const T& at(size_t i) const noexcept {
assert(i < N);
return _data[i];
}
constexpr T* begin() {
return std::addressof(_data[0]);
}
constexpr const T* begin() const {
return std::addressof(_data[0]);
}
constexpr T* end() {
// todo: maybe use std::addressof and disable compiler warnings
// about array bounds that result
return &_data[N];
}
constexpr const T* end() const {
return &_data[N];
}
constexpr size_t size() const {
return N;
}
private:
T _data[N];
private:
// construct each element from the initialiser list if present
// if not, default-construct
template<size_t...Is>
constexpr array(std::initializer_list<T> list, std::integer_sequence<size_t, Is...>)
: _data {
(
Is >= list.size()
?
T()
:
std::move(*(std::next(list.begin(), Is)))
)...
}
{
}
};
// convenience printer
template<size_t N, class T>
inline std::ostream& operator<<(std::ostream& os, const array<N, T>& a)
{
os << "[";
auto sep = " ";
for (const auto& i : a) {
os << sep << i;
sep = ", ";
}
return os << " ]";
}
}
namespace trig
{
constexpr double pi() {
return M_PI;
}
template<class T>
auto constexpr to_radians(T degs) {
return degs / 180.0 * pi();
}
// compile-time computation of a factorial
constexpr double factorial(size_t x) {
double result = 1.0;
for (int i = 2 ; i <= x ; ++i)
result *= double(i);
return result;
}
// compile-time replacement for std::pow
constexpr double power(double x, size_t n)
{
double result = 1;
while (n--) {
result *= x;
}
return result;
}
// compute a term in a taylor expansion at compile time
constexpr double taylor_term(double x, size_t i)
{
int powers = 1 + (2 * i);
double top = power(x, powers);
double bottom = factorial(powers);
auto term = top / bottom;
if (i % 2 == 1)
term = -term;
return term;
}
// compute the sin(x) using `terms` terms in the taylor expansion
constexpr double taylor_expansion(double x, size_t terms)
{
auto result = x;
for (int term = 1 ; term < terms ; ++term)
{
result += taylor_term(x, term);
}
return result;
}
// compute our interpolatable table as a constexpr
template<size_t N = 1024>
struct sin_table : cpputil::array<N, double>
{
static constexpr size_t cache_size = N;
static constexpr double step_size = (pi() / 2) / cache_size;
static constexpr double _360 = pi() * 2;
static constexpr double _270 = pi() * 1.5;
static constexpr double _180 = pi();
static constexpr double _90 = pi() / 2;
constexpr sin_table()
: cpputil::array<N, double>({})
{
for(int slot = 0 ; slot < cache_size ; ++slot)
{
double val = trig::taylor_expansion(step_size * slot, 20);
(*this)[slot] = val;
}
}
double checked_interp_fw(double rads) const {
size_t slot0 = size_t(rads / step_size);
auto v0 = (slot0 >= this->size()) ? 1.0 : (*this)[slot0];
size_t slot1 = slot0 + 1;
auto v1 = (slot1 >= this->size()) ? 1.0 : (*this)[slot1];
auto ratio = (rads - (slot0 * step_size)) / step_size;
return (v1 * ratio) + (v0 * (1.0-ratio));
}
double interpolate(double rads) const
{
rads = std::fmod(rads, _360);
if (rads < 0)
rads = std::fmod(_360 - rads, _360);
if (rads < _90) {
return checked_interp_fw(rads);
}
else if (rads < _180) {
return checked_interp_fw(_90 - (rads - _90));
}
else if (rads < _270) {
return -checked_interp_fw(rads - _180);
}
else {
return -checked_interp_fw(_90 - (rads - _270));
}
}
};
double sine(double x)
{
if (x < 0) {
return -sine(-x);
}
else {
constexpr sin_table<> table;
return table.interpolate(x);
}
}
}
void check(float degs) {
using namespace std;
cout << "checking : " << degs << endl;
auto mysin = trig::sine(trig::to_radians(degs));
auto stdsin = std::sin(trig::to_radians(degs));
auto error = stdsin - mysin;
cout << "mine=" << mysin << ", std=" << stdsin << ", error=" << error << endl;
cout << endl;
}
auto main() -> int
{
check(0.5);
check(30);
check(45.4);
check(90);
check(151);
check(180);
check(195);
check(89.5);
check(91);
check(270);
check(305);
check(360);
return 0;
}
expected output:
checking : 0.5
mine=0.00872653, std=0.00872654, error=2.15177e-09
checking : 30
mine=0.5, std=0.5, error=1.30766e-07
checking : 45.4
mine=0.712026, std=0.712026, error=2.07233e-07
checking : 90
mine=1, std=1, error=0
checking : 151
mine=0.48481, std=0.48481, error=2.42041e-08
checking : 180
mine=-0, std=1.22465e-16, error=1.22465e-16
checking : 195
mine=-0.258819, std=-0.258819, error=-6.76265e-08
checking : 89.5
mine=0.999962, std=0.999962, error=2.5215e-07
checking : 91
mine=0.999847, std=0.999848, error=2.76519e-07
checking : 270
mine=-1, std=-1, error=0
checking : 305
mine=-0.819152, std=-0.819152, error=-1.66545e-07
checking : 360
mine=0, std=-2.44929e-16, error=-2.44929e-16
I am just keeping this answer for documentation. As the comments say, I was mislead by gcc being permissive. It fails, when f(42) is used e.g. as a template parameter like this:
std::array<int, f(42)> asdf;
sorry, this was not a solution
Separate the calculation of your float and the conversion to an int in two different constexpr functions:
constexpr int floatAsInt(float float_val) {
return *(int*)&float_val;
}
constexpr int f(unsigned int i) {
return floatAsInt(sin(double(i)*PI/180.*360./double(SineLength)));
}

Question on using class member function as template parameter

I'm reading the book by Daoqi Yang "C++ and Object Oriented Numeric Computing for Scientists and Engineers". He has a similar example to what I am showing below, but the exceptions are the class "P" I define and the second to last line (which doesn't work). My question is: why does my compiler generate and error when I supply the function member f.integrand? What can I do to correct this? The errors being generated are C3867, C2440, and C2973.
Here is the code:
class P{
public:
double integrand(double x){
return (exp(-x*x));
}
};
template<double F(double)>
double trapezoidal(double a, double b, int n)
{
double h=(b-a)/n;
double sum=F(a)*0.5;
for(int i=1;i<n;i++)
{
sum+=F(a+i*h);
}
sum+=F(b)*0.5;
return (sum*h);
}
double integrand2(double x){
return (exp(-x*x));
}
int main(){
P f;
cout<< trapezoidal<integrand2>(0,1,100)<<endl; // this works
cout<< trapezoidal<f.integrand>(0,1,100)<<endl; // this doesn't work
}
Template arguments must be compile-time constant expressions or types, and member functions require special handling anyway. Instead of doing this, use boost::function<> as an argument, and boost::bind to create the functor, e.g.
double trapezoidal(double, double, boost::function<double(double)>);
// ...
P f;
trapezoidal(0, 1, 100, integrand2);
trapezoidal(0, 1, 100, boost::bind(&P::integrand, boost::ref(f)));
If you have 0x-capable compiler, you can use std::function and std::bind instead.
Cat Plus Plus is correct - boost::bind is a good way to do this easily. I've also included an alternate solution with the following snippet of code:
class P{
private:
double a;
public:
double integrand(double x){
return (a*exp(-x*x));
}
void setA(double y){
a = y;
}
void getA(){
cout<<a<<endl;
}
struct integrand_caller {
P* p;
integrand_caller(P& aP) : p(&aP) {};
double operator()(double x) const {
return p->integrand(x);
};
};
};
template <typename Evaluator, typename VectorType>
VectorType trapezoidal(Evaluator f, const VectorType& a, const VectorType& b, int n)
{
VectorType h=(b-a)/n;
VectorType sum=f(a)*0.5;
for(int i=1;i<n;i++)
{
sum+=f(a+i*h);
}
sum += f(b)*0.5;
return (sum*h);
}
double integrand2(double x){
return (exp(-x*x));
}
int main(){
P f[5];
for(int i=0;i<5;i++){
f[i].setA(5*i);
f[i].getA();
cout<< trapezoidal(P::integrand_caller(f[i]),(double)0, (double)1, 100) << endl;
cout<<trapezoidal(boost::bind(&P::integrand,f[i],_1), 0.0, 1.0, 100)<<"\n"<<endl;
}
}