Provide AoS access to SoA - c++

I have data laid out in memory in a Structure of Arrays (SoA) or Sturcture of Pointers (SoP) form, and have a way to access that data as though it were laid out in Array of Structure (AoS) form -- code given below.
However, I am not too happy about use of struct AoS_4_SoP -- although this struct appears to use templates, it is not really generic since, for example, foo and bar are hard-coded inside it.
Two questions/requests:
1) For read-write performance, is AoS access provided as good as the direct SoA access?
2) What would a more generic scheme be? (I have seen quamrana's code here, but it hasn't helped.)
struct SoP{ // Structure of Pointers
int *foo{ nullptr };
double *bar{ nullptr };
SoP( int *xi, double *xd ):foo(xi), bar(xd){};
};
struct SoR{ // Structure of References
int &foo;
double &bar;
SoR( int &xi, double &xd ):foo(xi), bar(xd){};
};
template< typename T, typename S >
struct AoS_4_SoP {
AoS_4_SoP( T *x ) : p( x ){};
T *p;
S operator[](std::size_t idx) const { return { p->foo[idx], p->bar[idx] }; }
const S operator[](std::size_t idx) const { return { p->foo[idx], p->bar[idx] }; }
};
Here's a main() showing the use of the above:
int main()
{
std::vector< int > ibuf{ 11, 22, 33, 44 };
std::vector< double > dbuf{ 0.11, 0.22, 0.33, 0.44 };;
SoP x_sop( ibuf.data(), dbuf.data() );
ibuf.at(2) = 333;
std::cout << "Access via SoP syntax:\n "
<< x_sop.foo[2]
<< " "
<< x_sop.bar[2] << std::endl;
AoS_4_SoP<SoP, SoR> xacc( &x_sop );
std::cout << "Access via AoS syntax:\n "
<< xacc[2].foo
<< " "
<< xacc[2].bar << std::endl;
// show write access via SoA syntax
ibuf.at(2) = 3333;
dbuf.at( 2 ) = 0.333333; // will get overwritten below
xacc[2].bar = 0.3333;
std::cout << "Values written via SoP, read via SoP:\n "
<< x_sop.foo[2]
<< " "
<< x_sop.bar[2] << std::endl;
// show write access via AoS syntax
xacc[2].foo = 333333;
dbuf.at( 2 ) = 0.3333333333; // will get overwritten below
xacc[2].bar = 0.333333;
std::cout << "Values written via AoS, read via AoS:\n "
<< xacc[2].foo
<< " "
<< xacc[2].bar << std::endl;
}
Above code can be compiled via:
// x86_64-w64-mingw32-g++.exe -D_WIN64 -Wall -Wextra -Werror -std=c++11 -O3 -static-libgcc -static-libstdc++ aossoa.cc -o aossoa.exe
and results in the following output:
Access via SoP syntax:
333 0.33
Access via AoS syntax:
333 0.33
Values written via SoP, read via SoP:
3333 0.3333
Values written via AoS, read via AoS:
333333 0.333333

I think this template will work.
template<class T, class U, class D, class S>
struct Accessor {
T* p;
U* (T::*pFirst);
D* (T::*pSecond);
S operator[](size_t index) {
return {(p->*pFirst)[index], (p->*pSecond)[index]};
}
Accessor(T* p_, U * (T::*pF), D * (T::*pS)): p(p_), pFirst(pF), pSecond(pS) {}
};
void main() {
std::vector< int > ibuf{ 11, 22, 33, 44 };
std::vector< double > dbuf{ 0.11, 0.22, 0.33, 0.44 };;
SoP x_sop(ibuf.data(), dbuf.data());
Accessor<SoP, int, double, SoR> aos(&x_sop, &SoP::foo, &SoP::bar);
aos[0].foo;
}
Now the template Accessor knows nothing about the names of the members of T.
At least it compiles under VS2015

Here's my adaptation of quamrana's solution for the "inverse" use case (SoA access for AoS):
template<class T, class S, class M0, class M1>
struct Accessor2{
T* p;
M0 m0;
M1 m1;
S operator[](std::size_t idx) { return { m0[idx], m1[idx] }; }
const S operator[](std::size_t idx) const { return { m0[idx], m1[idx] }; }
Accessor2(T* x, M0 p0, M1 p1): p(x), m0(p0), m1(p1){}
};
template< typename T, typename S >
struct AoS_4_SoP : public Accessor2<T, S, decltype(T::foo), decltype(T::bar)>
{
#ifndef COMPILER_CAN_INFER_PARENT_CLASS_TEMPLATE_SPECIFICATION
AoS_4_SoP(T *x) : Accessor2<T, S, decltype(T::foo), decltype(T::bar)>
(x, x->foo, x->bar ){}
#else
AoS_4_SoP(T *x):Accessor2(x, x->foo, x->bar ){}
#endif
};
Regarding the #ifndef COMPILER_CAN_INFER_PARENT_CLASS_TEMPLATE_SPECIFICATION, the compiler I am using viz, g++ 6.2.0 (x86_64_6.2.0_posix_seh_rt_v5_rev1/mingw64/bin/x86_64-w64-mingw32-g++.exe) is unable to infer the parent class' template specification. But, as Lightness Races in Orbit says on this page: the base's injected-class-name ought to be sufficient information for the compiler.

Related

Pass initializer list to function for initialization of std::array

In my Point header I have:
15 template<typename real> class Point
16 {
17 public:
18 // Constructors
19 Point();
20 Point(const std::initializer_list<real>&);
21 Point(const std::initializer_list<real>&, const types::unitTypes);
22 Point(const real, const real, const real);
23 Point(const real, const real, const real, const types::unitTypes);
...
43 private:
44 std::array<real, 3> xyz_;
45 types::unitTypes units_;
46 };
Note that lines 20 and 44 show that the Point to should be able to be initialized with an initializer_list and I have private variable std::array<real, 3> xyz_. Now, I would like for my constructor of this to be something like the following:
31 template<typename T>
32 Point<T>::Point(const std::initializer_list<T>& xyz)
33 : xyz_(xyz)
34 , units_(types::au)
35 {};
However, it doesn't seem like I'm able to construct the array from an initializer and if I try to move that modify that from :xyz_(xyz) to something like
31 template<typename T>
32 Point<T>::Point(std::initializer_list<T> xyz)
33 : units_(types::au)
34 {
35 xyz_ = xyz;
36 };
it is not able to overload = for operands array and initializer. Is there a better way to go about this that I can use invoke Point<real>({x, y, z}); and initialize the xyz_ array internally?
Update:
I had tried to define Point(const std::array<real, 3>&) before but I get the following compilation error (essential parts extracted):
error: call of overloaded ‘Point(<brace-enclosed initializer list>)’ is ambiguous
...
note: candidate: ‘aided::point::Point<real>::Point(const std::array<real, 3>&) [with real = float]’
...
note: candidate: ‘constexpr aided::point::Point<float>::Point(const aided::point::Point<float>&)’
...
note: candidate: ‘constexpr aided::point::Point<float>::Point(aided::point::Point<float>&&)’
The first is candidate is the one I am intending to use. Are the second two somehow copy constructors that are able to be invoked via an initialization with an initializer list?
std::initializer_list and std::array don’t cooperate as well as one would hope. Separate constructor arguments can give you a bit more flexibility, automatic template argument deduction and (also, to some extent) automatic choice of a type that can hold all the values:
#include <array>
#include <iostream>
#include <type_traits>
#include <utility>
template <typename Real, size_t N>
struct Point {
template <typename... R>
Point(R &&...reals) : xyz_{static_cast<Real>(std::forward<R>(reals))...} {}
private:
std::array<Real, N> xyz_;
template <size_t Head, size_t... Tail>
void print(std::ostream &out, std::index_sequence<Head, Tail...>) const {
out << xyz_[Head];
((out << ',' << xyz_[Tail]), ...);
}
friend std::ostream &operator<<(std::ostream &out, const Point &point) {
out << typeid(Real).name() << ' ' << '[';
point.print(out, std::make_index_sequence<N>());
return out << ']';
}
};
template <typename... R>
Point(R &&...) -> Point<std::common_type_t<R...>, sizeof...(R)>;
Now let’s test that↑ a bit and let’s not insist on Real too strongly:
#include <complex>
#include "that_magic_point.h"
int main() {
Point p0{1, 2, 3}; // int
Point p1{4., 5., 6.}; // double
Point p2{7, 8., 9}; // int, double -> double
Point p3{10, 11, 12ll}; // int, long long -> long long
Point p4{1}; // int
Point p5{2., 3.}; // double
Point p6{4, 5., 6u, 7}; // int, double, unsigned -> double
Point p7{std::complex{1, 2}, 3}; // complex<int>, int -> complex<int>
Point p8{4, std::complex{5., 6.}}; // int, complex<double> -> complex<double>
// Caveat: This resolves (incorrectly) to complex<int>:
// Point p9{std::complex{7, 8}, 9.};
// Caveat: This will not compile (cast from complex<int> to complex<double>):
// Point<std::complex<double>, 2> p9{std::complex{7, 8}, 9.};
// Caveat: This is verbose:
Point<std::complex<double>, 2> p9{std::complex<double>{7, 8}, 9.};
std::cout << p0 << '\n'
<< p1 << '\n'
<< p2 << '\n'
<< p3 << '\n'
<< p4 << '\n'
<< p5 << '\n'
<< p6 << '\n'
<< p7 << '\n'
<< p8 << '\n'
<< p9 << '\n';
}
This↑ seems to work and may generate the following output (modulo compilers’ RTTI naming differences):
i [1,2,3]
d [4,5,6]
d [7,8,9]
x [10,11,12]
i [1]
d [2,3]
d [4,5,6,7]
St7complexIiE [(1,2),(3,0)]
St7complexIdE [(4,0),(5,6)]
St7complexIdE [(7,8),(9,0)]
Solving some of the caveats outlined in comments using deep(er) template decomposition and specialization would be a nice exercise, but may not be worth the hassle. 🤪
There is basically no problem to use a std::initializer list for your class. You just need to copy the data manually.
You can also check the number of parameters in the std::initializer_list and act accordingly. In my below example I used assert to check that.
You can also implement the rule of 3 or 5. Although not needed for this simple class. I added it just for demo purposes.
I also recomend to use parameters with default arguments. For this example, it will make life easier.
Maybe best is to show an example:
#include <iostream>
#include <array>
#include <initializer_list>
#include <algorithm>
#include <fstream>
#include <cassert>
enum class types : int { au };
class Point {
std::array<double, 3> coordinate{};
types type{};
public:
// Rule of five. Not needed in this case
Point() {};
Point(const Point& other) { coordinate = other.coordinate; type = other.type; }
Point& operator = (const Point& p) { if (this == &p) return *this; coordinate = p.coordinate; type = p.type; return *this; }
Point(Point&& other) noexcept { coordinate = std::move(other.coordinate); type = other.type; }
Point& operator = (Point&& other) noexcept { coordinate = std::move(other.coordinate); type = other.type; return *this; }
virtual ~Point() {};
// Special constructors
Point(const std::initializer_list<double>& il, types t = types::au) : type(t){
assert((il.size() == 3) && "\nDynamic Assert: Only 3 elements allowed\n");
std::copy_n(il.begin(), 3, coordinate.begin());
}
Point(const double x, const double y, const double z, types t = types::au) : type(t) {
coordinate[0] = x; coordinate[1] = y; coordinate[2] = z;
}
Point(const double(&c)[3], types t = types::au) : type(t) {
std::copy_n(std::begin(c), 3, coordinate.begin());
}
friend std::ostream& operator << (std::ostream& os, const Point& p) {
return os << p.coordinate[0] << '\t' << p.coordinate[1] << '\t' << p.coordinate[2];
}
// Special assignment
Point& operator = (const std::initializer_list<double>& il) {
assert((il.size() == 3) && "\nDynamic Assert: Only 3 elements allowed\n");
std::copy_n(il.begin(), 3, coordinate.begin());
return *this;
}
Point& operator = (const double(&c)[3]) {
std::copy_n(std::begin(c), 3, coordinate.begin());
return *this;
}
};
int main() {
Point p1({ 1,2,3 });
std::cout << "p1:\t" << p1 << '\n';
Point p2({ 4,5,6 }, types::au);
std::cout << "p2:\t" << p2 << '\n';
Point p3( 7,8,9 );
std::cout << "p3:\t" << p3 << '\n';
Point p4( 10,11,12, types::au);
std::cout << "p4:\t" << p4 << '\n';
double d5[3] = { 13,14,15 };
Point p5(d5);
std::cout << "p5:\t" << p5 << '\n';
double d6[3] = { 16,17,18 };
Point p6(d6, types::au);
std::cout << "p6:\t" << p6 << '\n';
Point p7(p6);
std::cout << "p7:\t" << p7 << '\n';
Point p8;
p8 = p7;
std::cout << "p8:\t" << p8 << '\n';
Point p9(19, 20, 21);
Point p10(std::move(p9));
std::cout << "p10:\t" << p10 << '\n';
Point p11(22,23,24);
Point p12{};
p12 = (std::move(p11));
std::cout << "p12:\t" << p12 << '\n';
Point p13{};
p13 = { 25,26,27 };
std::cout << "p13:\t" << p13 << '\n';
Point p14{};
double d14[3] = { 28,29,30 };
p14 = d14;
std::cout << "p14:\t" << p14 << '\n';
}

Generating floating point limits at compile time via template arguments and constexpr semantics:

I'm working on a set of classes. My Function class will take a Functor class which stores a function pointer to some defined function which has an operator that will invoke the function call from the function pointer. It uses a Limit class that currently takes <int,int> for its upper and lower bounds. It has nothing but static constexpr functions to return the bounds and to calculate the number of elements between those bounds. If the lower bounds = 1 and upper bounds = 5 it will generate 5 for the number of elements to be evaluated for that function...
Here is what I'm doing with these classes:
First I declare a function such as f(x) = x, f(x) = x^2, or f(x) = cos(x), etc.
Then I instantiate a Functor object based on the above function(s) parameter types both for the return and for its parameter-argument types...
Next, I assign the function to my Functor class's member variable.
Then I instantiate a Function object giving it the data-type and the Lower & Upper limits for the range of the function.
The Function class upon construction automatically generates the data points of that function from [lower,upper] and stores the generated values in its internal array.
The Function class also contains an operator that will allow the user to get any value from any given input.
Pseudo Example:
f(x) = x^2;
Functor<T,T> functor;
functor.member = &f(x);
Function<T,Lower,Upper,T> function(functor);
// If T=int, Lower = -4, and Upper = 4 then the internal data set will be
// (-4,16) (-3,9), (-2,4), (-1,1), (0,0), (1,1), (2,4), (3,9), (4,16)
// The user can also use it's operator to call function(9) and it will return 81
Here is my working program that is generating datasets of values from my classes using various functions:
main.cpp
#include <cmath>
#include <exception>
#include <iostream>
#include "Function.h"
int main() {
try {
pipes::Functor<int, int> functor1;
functor1.FuncPtr = &square;
pipes::Function<int, -10, 10, int> func1( functor1 );
auto data1{ func1.data() };
for (auto& p : data1)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func1(25) << "\n\n";
pipes::Functor<int, int> functor2;
functor2.FuncPtr = &linear;
pipes::Function<int, -10, 10, int> func2(functor2);
auto data2{ func2.data() };
for (auto& p : data2)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func2(25) << "\n\n";
pipes::Functor<double, double> functor3;
functor3.FuncPtr = &cosine;
pipes::Function<double, -7, 7, double> func3(functor3);
auto data3{ func3.data() };
for (auto& p : data3)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func3(25) << "\n\n";
}
catch (const std::exception& e) {
std::cerr << e.what() << "\n\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Function.h
#pragma once
#include <array>
namespace pipes {
template<typename Ret, typename... Args>
struct Functor {
Ret(*FuncPtr)(Args...);
Ret operator()(Args... args) { return FuncPtr(args...); }
};
template<int Lower, int Upper>
class Limits {
public:
static constexpr unsigned lower_bound() { return Lower; }
static constexpr unsigned upper_bound() { return Upper; }
static constexpr unsigned element_count() { return (Upper - Lower + 1); }
};
template<typename T, int Lower, int Upper, typename... Args>
class Function {
std::array<std::pair<T, T>, Limits<Lower,Upper>::element_count()> data_points_;
Functor<T,Args...> functor_;
public:
Function(Functor<T,Args...> func) {
functor_ = func;
for (unsigned i = 0; i < Limits<Lower,Upper>::element_count(); i++) {
data_points_[i].first = ((T)i + (T)Lower);
data_points_[i].second = functor_(data_points_[i].first);
}
}
T operator()(Args... args) const {
return functor_.FuncPtr(args...);
}
constexpr auto lower() const { return Lower; }
constexpr auto upper() const { return Upper; }
constexpr auto count() const { return Limits<Lower,Upper>::element_count(); }
constexpr auto data() const { return data_points_; }
};
} // namespace pipes
When I run the program it is generating this output which appears to be correct:
Output
(-10,100)
(-9,81)
(-8,64)
(-7,49)
(-6,36)
(-5,25)
(-4,16)
(-3,9)
(-2,4)
(-1,1)
(0,0)
(1,1)
(2,4)
(3,9)
(4,16)
(5,25)
(6,36)
(7,49)
(8,64)
(9,81)
(10,100)
f(25) = 625
(-10,-10)
(-9,-9)
(-8,-8)
(-7,-7)
(-6,-6)
(-5,-5)
(-4,-4)
(-3,-3)
(-2,-2)
(-1,-1)
(0,0)
(1,1)
(2,2)
(3,3)
(4,4)
(5,5)
(6,6)
(7,7)
(8,8)
(9,9)
(10,10)
f(25) = 25
(-7,0.753902)
(-6,0.96017)
(-5,0.283662)
(-4,-0.653644)
(-3,-0.989992)
(-2,-0.416147)
(-1,0.540302)
(0,1)
(1,0.540302)
(2,-0.416147)
(3,-0.989992)
(4,-0.653644)
(5,0.283662)
(6,0.96017)
(7,0.753902)
f(25) = 0.991203
And now for my question where this becomes the tricky part...
With my code currently the way it is, everything is fine as long as my bounds [-a,b] are of an integral type...
Let's suppose on my last example such as with cos, what if I want to have my bounds from [-2pi,2pi] where the lower and upper limits are of floating-point types...
The Issue:
Currently in C++ this is non-standard and in most cases won't compile:
template<float val> // or template<double>
struct foo() {
constexpr float operator()() {
return val;
}
};
And the above prevents me from doing something like this:
constexpr double PI{ 6.28318531 };
pipes::Functor<double, double> functor3;
functor3.FuncPtr = &cosine;
pipes::Function<double, -PI, PI, double> func3(functor3);
auto data3{ func3.data() };
for (auto& p : data3)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func3(25) << "\n\n";
So if I want to be able to support floating-point types for my intervals of my Limits or Range class... What kind of alternative would there be if such a thing is currently possible in c++? Or would I just have to simply restructure the way my class templates are designed?
If the above is possible in some way during compile time via templates and constexpr semantics, then there is another issue that arises that will have to be taken into consideration and that would be the stepping interval for use with floating-point types to know how many data points there will be within the dataset... (basically calculating dx based on some stepping value which would be defined by the user, for example: (0.1, 0.001, etc...) and the number of data points would be calculated by the number of these divisions between [lower, upper]... However, if the stepping value is known at compile-time, then calculating the divisions should be simple enough... that's not a major concern. The bigger concern is being able to express floating-point constants at compile time for template evaluation...
Currently, with the way my code is with its design, I have hit a limit on its functionality... I'm not sure how to provide a similar interface to support a floating-point range that can be calculated and generated at compile time! Any bit of help or suggestions is welcomed!
I think the closest you can get to a construct like yours is:
#include <iostream>
#include <array>
constexpr const double PI_2{ 6.28318531 };
template<double const &lower, double const &upper>
void foo() {
static_assert(lower<upper, "invalid lower and upper value");
constexpr size_t size = (upper-lower);
std::array<int, size> test;
std::cout << lower << " " << upper << " " << test.size() << std::endl;
}
template<double const &V>
struct neg {
static constexpr double value = -V;
};
int main()
{
foo<neg<PI_2>::value, PI_2>();
return 0;
}
If you can always specify the type as first template argument you could have something like this:
template<typename T, T const &lower, T const &upper>
void foo() {
std::cout << lower << " " << upper << std::endl;
}
I didn't fully think it through, how to get the floating-point part and the other together, but I think it should be possible.
In modern C++ and how templates are currently designed, I had to slightly restructure my code. It's forcing me to have to use std::vector instead of std::array, because we can't use floating-point types as constant template arguments... So I ended up having to change two of my classes... I had to change my Limits class, and my Function class.
My Limits class now accepts a Type instead of constant-integral-type and it stores 3 member variables. It also has a default constructor and a user constructor. The functions are now just constexpr instead of being static.
My Function class now stores a Limits class object and data_points_ is no longer an std::array as it is now std::vector. It's constructor now also takes in a Limits object.
I had also taken into account for the step size for floating-point ranges.
Here is what my modified code looks like with its given output:
main.cpp
#include <cmath>
#include <iostream>
#include <exception>
#include "Function.h"
constexpr int square(int x) {
return x * x;
}
constexpr int linear(int x) {
return x;
}
double cosine(double x) {
return cos(x);
}
//template<float val>
struct foo {
float operator()(float val) { return val; }
};
int main() {
try {
pipes::Functor<int, int> functor1;
pipes::Limits<int> limit1(-10, 10, 1);
functor1.FuncPtr = &square;
pipes::Function<int, int, int> func1( limit1, functor1 );
auto data1{ func1.data() };
for (auto& p : data1)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func1(25) << "\n\n";
pipes::Functor<int,int> functor2;
pipes::Limits<int> limit2(-10, 10, 1);
functor2.FuncPtr = &linear;
pipes::Function<int, int, int> func2(limit2, functor2);
auto data2{ func2.data() };
for (auto& p : data2)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func2(25) << "\n\n";
constexpr double PI{ 6.28318531 };
pipes::Functor<double, double> functor3;
pipes::Limits<double> limits3( (-PI), PI, 0.1);
functor3.FuncPtr = &cosine;
pipes::Function<double, double, double> func3(limits3, functor3);
auto data3{ func3.data() };
for (auto& p : data3)
std::cout << '(' << p.first << ',' << p.second << ")\n";
std::cout << '\n';
std::cout << "f(25) = " << func3(25) << "\n\n";
}
catch (const std::exception& e) {
std::cerr << e.what() << "\n\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Function.h
#pragma once
#include <vector>
namespace pipes {
template<typename Ret, typename... Args>
struct Functor {
Ret(*FuncPtr)(Args...);
Ret operator()(Args... args) { return FuncPtr(args...); }
};
template<typename Ty>
class Limits {
private:
Ty Lower;
Ty Upper;
Ty Step;
public:
Limits() {}
Limits(Ty lower, Ty upper, Ty step) : Lower{ lower }, Upper{ upper }, Step{ step } {}
constexpr Ty lower_bound() { return Lower; }
constexpr Ty upper_bound() { return Upper; }
constexpr Ty step_size() { return Step; }
constexpr unsigned element_count() { return (unsigned)((Upper - Lower + 1)/Step); }
};
template<typename LimT, typename FuncT, typename... Args>
class Function {
Limits<LimT> limits_;
Functor<FuncT, Args...> functor_;
std::vector<std::pair<FuncT, FuncT>> data_points_;
public:
Function(Limits<LimT> limits, Functor<FuncT,Args...> func) {
limits_ = limits;
functor_ = func;
data_points_.resize( limits_.element_count() );
for (unsigned i = 0; i < limits_.element_count(); i++) {
auto x = limits_.lower_bound() + (i * limits_.step_size());
data_points_[i].first = (x);
data_points_[i].second = functor_(x);
}
}
FuncT operator()(Args... args) const {
return functor_.FuncPtr(args...);
}
constexpr auto lower() const { return limits_.lower_bound(); }
constexpr auto upper() const { return limits_.upper_bound(); }
constexpr auto count() const { return limits_.element_count(); }
constexpr auto step() const { return limits_.step_size(); }
constexpr auto data() const { return data_points_; }
};
} // namespace pipes
Output
(-10,100)
(-9,81)
(-8,64)
(-7,49)
(-6,36)
(-5,25)
(-4,16)
(-3,9)
(-2,4)
(-1,1)
(0,0)
(1,1)
(2,4)
(3,9)
(4,16)
(5,25)
(6,36)
(7,49)
(8,64)
(9,81)
(10,100)
f(25) = 625
(-10,-10)
(-9,-9)
(-8,-8)
(-7,-7)
(-6,-6)
(-5,-5)
(-4,-4)
(-3,-3)
(-2,-2)
(-1,-1)
(0,0)
(1,1)
(2,2)
(3,3)
(4,4)
(5,5)
(6,6)
(7,7)
(8,8)
(9,9)
(10,10)
f(25) = 25
(-6.28319,1)
(-6.18319,0.995004)
(-6.08319,0.980067)
(-5.98319,0.955336)
(-5.88319,0.921061)
(-5.78319,0.877583)
(-5.68319,0.825336)
(-5.58319,0.764842)
(-5.48319,0.696707)
(-5.38319,0.62161)
(-5.28319,0.540302)
(-5.18319,0.453596)
(-5.08319,0.362358)
(-4.98319,0.267499)
(-4.88319,0.169967)
(-4.78319,0.0707372)
(-4.68319,-0.0291995)
(-4.58319,-0.128844)
(-4.48319,-0.227202)
(-4.38319,-0.32329)
(-4.28319,-0.416147)
(-4.18319,-0.504846)
(-4.08319,-0.588501)
(-3.98319,-0.666276)
(-3.88319,-0.737394)
(-3.78319,-0.801144)
(-3.68319,-0.856889)
(-3.58319,-0.904072)
(-3.48319,-0.942222)
(-3.38319,-0.970958)
(-3.28319,-0.989992)
(-3.18319,-0.999135)
(-3.08319,-0.998295)
(-2.98319,-0.98748)
(-2.88319,-0.966798)
(-2.78319,-0.936457)
(-2.68319,-0.896758)
(-2.58319,-0.8481)
(-2.48319,-0.790968)
(-2.38319,-0.725932)
(-2.28319,-0.653644)
(-2.18319,-0.574824)
(-2.08319,-0.490261)
(-1.98319,-0.400799)
(-1.88319,-0.307333)
(-1.78319,-0.210796)
(-1.68319,-0.112153)
(-1.58319,-0.0123887)
(-1.48319,0.087499)
(-1.38319,0.186512)
(-1.28319,0.283662)
(-1.18319,0.377978)
(-1.08319,0.468517)
(-0.983185,0.554374)
(-0.883185,0.634693)
(-0.783185,0.70867)
(-0.683185,0.775566)
(-0.583185,0.834713)
(-0.483185,0.88552)
(-0.383185,0.927478)
(-0.283185,0.96017)
(-0.183185,0.983268)
(-0.0831853,0.996542)
(0.0168147,0.999859)
(0.116815,0.993185)
(0.216815,0.976588)
(0.316815,0.950233)
(0.416815,0.914383)
(0.516815,0.869397)
(0.616815,0.815725)
(0.716815,0.753902)
(0.816815,0.684547)
(0.916815,0.608351)
(1.01681,0.526078)
(1.11681,0.438547)
(1.21681,0.346635)
(1.31681,0.25126)
(1.41681,0.153374)
(1.51681,0.0539554)
(1.61681,-0.0460021)
(1.71681,-0.1455)
(1.81681,-0.243544)
(1.91681,-0.339155)
(2.01681,-0.431377)
(2.11681,-0.519289)
(2.21681,-0.602012)
(2.31681,-0.67872)
(2.41681,-0.748647)
(2.51681,-0.811093)
(2.61681,-0.865435)
(2.71681,-0.91113)
(2.81681,-0.947722)
(2.91681,-0.974844)
(3.01681,-0.992225)
(3.11681,-0.999693)
(3.21681,-0.997172)
(3.31681,-0.984688)
(3.41681,-0.962365)
(3.51681,-0.930426)
(3.61681,-0.889191)
(3.71681,-0.839072)
(3.81681,-0.780568)
(3.91681,-0.714266)
(4.01681,-0.640826)
(4.11681,-0.560984)
(4.21681,-0.475537)
(4.31681,-0.385338)
(4.41681,-0.291289)
(4.51681,-0.19433)
(4.61681,-0.0954289)
(4.71681,0.0044257)
(4.81681,0.104236)
(4.91681,0.203005)
(5.01681,0.299745)
(5.11681,0.393491)
(5.21681,0.483305)
(5.31681,0.56829)
(5.41681,0.647596)
(5.51681,0.720432)
(5.61681,0.78607)
(5.71681,0.843854)
(5.81681,0.893206)
(5.91681,0.933634)
(6.01681,0.964733)
(6.11681,0.986192)
(6.21681,0.997798)
(6.31681,0.999435)
(6.41681,0.991085)
(6.51681,0.972833)
(6.61681,0.94486)
(6.71681,0.907447)
(6.81681,0.860967)
(6.91681,0.805884)
(7.01681,0.742749)
(7.11681,0.672193)
f(25) = 0.991203
This is giving me the behavior that I want, however, I was trying to do the same thing using array... I'm guessing until C++ supports floating-point-constants as template arguments I'm going to have to settle with std::vector using heap allocations, instead of std::array and stack-cache friendly containers...

How to have inheritance between template with union?

I have the following two objects. Im wondering if there is a way to have Pixel as a base class of PixelBGR so that any operator (+,-,*,/, [], etc.) could be used without redefining them ?
template<class T, std::size_t N>
struct Pixel
{
T ch[N];
inline T& operator[](const int x)
{
return ch[x];
}
};
template<class T>
struct PixelBGR
{
union
{
struct
{
T b;
T g;
T r;
};
T ch[3];
};
inline T& operator[](const int x)
{
return ch[x];
}
};
EDIT: As suggested by πάντα ῥεῖ, here more details about what Im trying to do.
Im trying to have a generic class Pixel, which will be template to handle any type or size.
The usual are 1,2,3,4,8 or 16. The class with defines some operator such as +,-,*, etc.
Since most of the time, the Pixel<T,3> is a BGR pixel, I would like to define rapid access to r,g and b to avoid confusion, but still store it as BGR.
But the derived class should also provide the Operator which will be generic based on N.
EDIT2: By reading the comment of SergeyA, I forgot to say that the struct Pixel must not change size.
So I think balki answer is the best, by using member function. I was trying to make it with variables to avoid too much char ie: adding the (), but it seems to be too complicated for nothing. I still investigating CRTP, but I dont get it well, Im reading on that.
Answering the question as asked, this should give OP reuse of the operators without any undefined behavior:
#include <cstddef>
template<class T, std::size_t N>
struct Pixel
{
T ch[N];
inline T& operator[](const int x)
{
return ch[x];
}
Pixel& operator+= (const Pixel& ) { return *this;}
};
template<class T, std::size_t N>
Pixel<T, N> operator+ (const Pixel<T, N>& l, const Pixel<T, N>& r);
template<class T>
struct BgrPixel : Pixel<T, 3> {
using base = Pixel<T, 3>;
using base::base;
BgrPixel(const base& b) : base(b) { };
T& b = base::ch[0];
T& g = base::ch[1];
T& r = base::ch[2];
};
BgrPixel<int> a, b;
BgrPixel<int> c = a + b;
Alterinative would be to have b(), g() and r() as a member functions, but this would require you to access them as functions. You would also need const and non-const versions of them.
The benefits, however, would be that the size of the struct will not be increased and copy assignment would work naturally (which, in turn, could be solved by providing custom copy assignment).
The Curiously Recurring Template Pattern (CRTP) would work well in this case. In the CRTP the Derived class is used as a template argument to the Base class. Chapter 16.3 The Curiously Recurring Template Pattern (CRTP), from the C++ Templates - The Complete Guide, by David Vandevoorde and Nicolai M. Josuttis, explains things in more detail.
From the comments below, the usage of a union{struct{...}...} causes undefined behaviour (UB), but there have been some contradicting opinions upon this. As far as I'm aware, it is a gnu extension and supported by almost every compiler. glm for example uses union-structs quite very often.
As an alternative approach, you can use aliases (references) for the r,g,b variables.
#include <iostream>
template<typename T, std::size_t N, template<typename,std::size_t> class B >
struct Pixel
{
B<T,N> *crtp = static_cast<B<T,N>*>(this);
T& operator[](std::size_t x)
{
return crtp->ch[x];
}
Pixel& operator = (const Pixel &t)
{
crtp->ch[0] = t.crtp->ch[0];
crtp->ch[1] = t.crtp->ch[1];
crtp->ch[2] = t.crtp->ch[2];
return *crtp;
}
B<T,N> operator + (const B<T,N> &t)
{
B<T,N> tmp;
tmp[0] = crtp->ch[0] + t.crtp->ch[0];
tmp[1] = crtp->ch[1] + t.crtp->ch[1];
tmp[2] = crtp->ch[2] + t.crtp->ch[2];
return tmp;
}
B<T,N> operator - (const B<T,N> &t)
{
B<T,N> tmp;
tmp[0] = crtp->ch[0] - t.crtp->ch[0];
tmp[1] = crtp->ch[1] - t.crtp->ch[1];
tmp[2] = crtp->ch[2] - t.crtp->ch[2];
return tmp;
}
};
template<typename T, std::size_t N=3>
struct PixelBGR : Pixel<T, N, PixelBGR>
{
T ch[3];
T &r;
T &g;
T &b;
PixelBGR() : ch{},r(ch[0]),g(ch[1]),b(ch[2])
{}
PixelBGR& operator = (const PixelBGR &p)
{
ch[0] = p.ch[0];
ch[1] = p.ch[1];
ch[2] = p.ch[2];
return *this;
}
};
int main()
{
PixelBGR<int> p;
p.r = 25;
p.g = 14;
p.b = 58;
std::cout<< p[0] <<" , "<<p[1]<<" , "<<p[2] <<std::endl;
PixelBGR<int> q;
q = p;
std::cout<< q[0] <<" , "<<q[1]<<" , "<<q[2] <<std::endl;
PixelBGR<int> res1;
res1 = q + p;
std::cout<< res1.r <<" , "<<res1.g<<" , "<<res1.b <<std::endl;
PixelBGR<int> res2;
res2 = q - p;
std::cout<< res2.r <<" , "<<res2.g<<" , "<<res2.b <<std::endl;
}
Result:
25 , 14 , 58
25 , 14 , 58
50 , 28 , 116
0 , 0 , 0
Example using references: https://rextester.com/AZWG4319
Example using union-struct: https://rextester.com/EACC87146
First thanks to all of you for advise, and special thanks to #Constantinos Glynos, #balki and #SergeyA for the example they provide, those help me to achieve a solution that match my need.
I implemented the BGR and BGRA to show that the N works fine, now I just need to implement all the operator, and functions that I require.
Please feel free to edit, or tell me if there is something wrong with this.
Pixel.h
#include <cstdio> // std::size_t
#include <iostream> // std::cout
template<typename T, std::size_t N, template<typename, std::size_t> class B >
struct Pixel
{
T ch[N];
// ==============================================================
// Overload the accessor (so .ch[0] == direct access with [0].
T& operator[](std::size_t x){ return ch[x]; }
// ==============================================================
// Copy-assignement
Pixel& operator=( const Pixel &t )
{
for ( int i = 0; i < N; i++ )
ch[i] = t.ch[i];
return *this;
}
// ==============================================================
// Operator
B<T, N> operator+( const B<T, N> &t )
{
B<T, N> tmp;
for ( int i = 0; i < N; i++ )
tmp[i] = ch[i] + t.ch[i];
return tmp;
}
B<T, N> operator-( const B<T, N> &t )
{
B<T, N> tmp;
for ( int i = 0; i < N; i++ )
tmp[i] = ch[i] - t.ch[i];
return tmp;
}
template<typename T, std::size_t N, template<typename, std::size_t> class B >
friend std::ostream& operator<<( std::ostream& os, const Pixel &t );
};
// To print the vector
template<typename T, std::size_t N, template<typename, std::size_t> class B >
std::ostream& operator<<( std::ostream& os, const B<T, N> &t )
{
os << "Pixel: (" << t.ch[0];
for ( int i = 1; i < N; i++ )
os << ", " << t.ch[i];
os << ")";
return os;
}
template<typename T, std::size_t N = 3>
struct BGR : Pixel<T, N, BGR>
{
T& b() { return ch[0]; }
T& g() { return ch[1]; }
T& r() { return ch[2]; }
};
template<typename T, std::size_t N = 4>
struct BGRA : Pixel<T, N, BGRA>
{
T& b() { return ch[0]; }
T& g() { return ch[1]; }
T& r() { return ch[2]; }
T& a() { return ch[3]; }
};
Main.cpp
int main() {
std::cout << "Sizeof a float BGR: " << sizeof(BGR<float>) << std::endl;
std::cout << "Sizeof a float BGRA: " << sizeof(BGRA<float>) << std::endl;
BGR<int> p;
p.r() = 25;
p.g() = 14;
p.b() = 58;
std::cout << p << std::endl;
std::cout << p[0] << " , " << p[1] << " , " << p[2] << std::endl;
std::cout << p.b() << " , " << p.g() << " , " << p.r() << std::endl;
BGR<int> q;
q = p;
std::cout << q[0] << " , " << q[1] << " , " << q[2] << std::endl;
BGR<int> res1;
res1 = q + p;
std::cout << res1.r() << " , " << res1.g() << " , " << res1.b() << std::endl;
BGR<int> res2;
res2 = q - p;
std::cout << res2.r() << " , " << res2.g() << " , " << res2.b() << std::endl;
BGRA<float> a;
a.r() = 255.0f;
a.g() = 0.0f;
a.b() = 0.0f;
a.a() = 128.5f;
BGRA<float> b = a;
std::cout << a << std::endl;
return 0;
}

How to thrust::make_transform_iterator that dereferences a device_ptr?

New to C++ and CUDA.
Using MSVS 2015 Community and CUDA 9.2.
I tried making a transform_iterator that just dereferences a device_ptr.
I get a compilation error: function "dereference_device_double_functor::operator()" cannot be called with the given argument list
I also made a version that uses a host_vector and just a normal double pointer to make sure my functor usage is right.
#include <iostream>
#include "thrust\device_vector.h"
#include "thrust\host_vector.h"
struct dereference_device_double_functor
{
dereference_device_double_functor() {}
typedef thrust::device_reference<thrust::device_ptr<double>> argument_type;
typedef double result_type;
__host__ __device__
double operator()(thrust::device_reference<thrust::device_ptr<double>> xDpRef) const {
thrust::device_ptr<double> xDp = (thrust::device_ptr<double>)xDpRef;
return *xDp;
}
};
struct dereference_host_double_functor
{
dereference_host_double_functor() {}
typedef double* argument_type;
typedef double result_type;
__host__ __device__
double operator()(double* const& xPtr) const {
return *xPtr;
}
};
int main()
{
// Create double
thrust::device_vector<double> dv(1, 5);
thrust::host_vector<double> hv(1, 6);
// Make sure its there
std::cout << dv[0] << std::endl;
std::cout << hv[0] << std::endl;
// Create pointers to doubles
thrust::device_vector<thrust::device_ptr<double>> dvPtr(1);
thrust::device_vector<double*> hvPtr(1);
// Assign pointers to doubles
dvPtr[0] = &(dv[0]);
hvPtr[0] = &(hv[0]);
// Make sure pointers point correctly
std::cout << *((thrust::device_ptr<double>)dvPtr[0]) << std::endl;
std::cout << *(hvPtr[0]) << std::endl;
// Test functor with iterator
auto dvi = dvPtr.begin();
double dvd = dereference_device_double_functor()(*dvi);
auto hvi = hvPtr.begin();
double hvd = dereference_host_double_functor()(*hvi);
// Make sure it worked with iterator
std::cout << dvd << std::endl;
std::cout << hvd << std::endl;
// Make dereferencing transfom iterators
auto tik = thrust::make_transform_iterator(dvPtr.begin(), dereference_device_double_functor());
auto tij = thrust::make_transform_iterator(hvPtr.begin(), dereference_host_double_functor());
// Check that transform iterators work
//std::cout << *tik << std::endl; // Will cause compile error: function "dereference_device_double_functor::operator()" cannot be called with the given argument list
std::cout << *tij << std::endl;
return 0;
}
Thanks for all your help!
In your question, you state this:
I tried making a transform_iterator that just dereferences a device_ptr.
That's not what I see in your code, however:
__host__ __device__
double operator()(thrust::device_reference<thrust::device_ptr<double>> xDpRef) const {
When I compile your code on linux, I get the following (excerpted from compile error spew):
$ nvcc -std=c++11 -o t351 t351.cu
/usr/local/cuda/bin/..//include/thrust/iterator/transform_iterator.h(312): error: function "dereference_device_double_functor::operator()" cannot be called with the given argument list
argument types are: (thrust::device_ptr<double>)
object type is: dereference_device_double_functor
...
So thrust is passing you a thrust::device_ptr<double>. But your functor operator is configured to take a thrust::device_reference<thrust::device_ptr<double>>
When I modify your code from this:
__host__ __device__
double operator()(thrust::device_reference<thrust::device_ptr<double>> xDpRef) const {
to this:
__host__ __device__
double operator()(thrust::device_ptr<double> xDpRef) const {
It compiles and runs correctly for me (on linux):
$ cat t351.cu
#include <iostream>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>
struct dereference_device_double_functor
{
dereference_device_double_functor() {}
typedef thrust::device_reference<thrust::device_ptr<double>> argument_type;
typedef double result_type;
__host__ __device__
double operator()(thrust::device_ptr<double> xDpRef) const {
thrust::device_ptr<double> xDp = (thrust::device_ptr<double>)xDpRef;
return *xDp;
}
};
struct dereference_host_double_functor
{
dereference_host_double_functor() {}
typedef double* argument_type;
typedef double result_type;
__host__ __device__
double operator()(double* const& xPtr) const {
return *xPtr;
}
};
int main()
{
// Create double
thrust::device_vector<double> dv(1, 5);
thrust::host_vector<double> hv(1, 6);
// Make sure its there
std::cout << dv[0] << std::endl;
std::cout << hv[0] << std::endl;
// Create pointers to doubles
thrust::device_vector<thrust::device_ptr<double>> dvPtr(1);
thrust::device_vector<double*> hvPtr(1);
// Assign pointers to doubles
dvPtr[0] = &(dv[0]);
hvPtr[0] = &(hv[0]);
// Make sure pointers point correctly
std::cout << *((thrust::device_ptr<double>)dvPtr[0]) << std::endl;
std::cout << *(hvPtr[0]) << std::endl;
// Test functor with iterator
auto dvi = dvPtr.begin();
double dvd = dereference_device_double_functor()(*dvi);
auto hvi = hvPtr.begin();
double hvd = dereference_host_double_functor()(*hvi);
// Make sure it worked with iterator
std::cout << dvd << std::endl;
std::cout << hvd << std::endl;
// Make dereferencing transfom iterators
auto tik = thrust::make_transform_iterator(dvPtr.begin(), dereference_device_double_functor());
auto tij = thrust::make_transform_iterator(hvPtr.begin(), dereference_host_double_functor());
// Check that transform iterators work
std::cout << *tik << std::endl; // Will cause compile error: function "dereference_device_double_functor::operator()" cannot be called with the given argument list
std::cout << *tij << std::endl;
return 0;
}
$ nvcc -std=c++11 -o t351 t351.cu
$ cuda-memcheck ./t351
========= CUDA-MEMCHECK
5
6
5
6
5
6
5
6
========= ERROR SUMMARY: 0 errors
$

use enum type to manage storage order

I'm trying to use enum types to indexig some array but I want to allow different ordering of the vector depending on some option. In the class I also want functions that take the enum variable as input and use it as it should.
The solution I found is the following
#include<iostream>
#include<array>
#include<vector>
struct A{
struct XYZ{
enum coord{X=0,Y,Z};
};
struct YZX{
enum coord{Y=0,Z,X};
};
struct ZXY{
enum coord{Z=0,X,Y};
};
std::array<std::vector<float>,3> val;
void resize(int opt, size_t dim){
val[opt].resize(dim);
return;
}
void printsize(){
for(auto & i : val){
std::cout << i.size() << " ";
}
std::cout << std::endl;
return;
}
};
int main(){
A foo1;
A foo2;
A foo3;
foo1.resize(XYZ::X,10);
foo2.resize(YZX::X,10);
foo3.resize(ZXY::X,10);
std::cout << "Size foo1\n";
foo1.printsize();
std::cout << "Size foo2\n";
foo2.printsize();
std::cout << "Size foo3\n";
foo3.printsize();
return 0;
}
What I don't like in this solution is that my function resize takes an integer type as input and there's no type control of the enum.
Is there any other smarter solution? Am I doing something considered as anti-pattern?
Thank you
I suggest you to modify the member function resize (three parameters instead of two) and exploit the type safety of the enum classes:
#include <stdio.h>
#include<iostream>
#include<array>
#include<vector>
struct A{
enum class Coordinate
{
X = 0,
Y = 1,
Z = 2
};
enum class Permutation
{
XYZ = 0,
ZXY = 1,
YZX = 2
};
std::array<std::vector<float>,3> val;
/* resize takes three parameters now */
void resize(Permutation p, Coordinate c, size_t dim)
{
int index = ( static_cast<int>(p) + static_cast<int>(c) ) % 3 ;
val[index].resize(dim);
return;
}
void printsize(){
for(auto & i : val){
std::cout << i.size() << " ";
}
std::cout << std::endl;
return;
}
};
int main()
{
A foo1;
A foo2;
A foo3;
foo1.resize(A::Permutation::XYZ, A::Coordinate::X,10);
foo2.resize(A::Permutation::YZX, A::Coordinate::X,10);
foo3.resize(A::Permutation::ZXY, A::Coordinate::X,10);
std::cout << "Size foo1\n";
foo1.printsize();
std::cout << "Size foo2\n";
foo2.printsize();
std::cout << "Size foo3\n";
foo3.printsize();
return 0;
}
How about an Index class, constructible from several enum classes?
struct A
{
enum class XYZ {X,Y,Z};
enum class YZX {Y,Z,X};
enum class ZXY {Z,X,Y};
struct Index
{
int value;
operator int() const {return value;}
Index(XYZ value) : value(int(value)) {}
Index(YZX value) : value(int(value)) {}
Index(ZXY value) : value(int(value)) {}
};
std::array<std::vector<float>, 3> val;
void resize(Index opt, size_t dim)
{
val[opt].resize(dim);
}
void printsize() const
{
for (const auto &i : val)
std::cout << i.size() << ' ';
std::cout << '\n';
}
};