I have the following vector class (vector as in spatial, not array):
template<typename T0, size_t S, typename = typename std::enable_if<std::is_arithmetic<T0>::value && (S > 1 && S < 5)>::type>
struct Vec
{
using value_type = T0;
using vector_type = Vec<T0, S>;
using array_type = std::array<T0, S>;
using index_type = size_t;
using size_type = size_t;
enum { num_components = S };
array_type v;
};
, such that I can make a vector type with 2, 3 or 4 elements:
template<typename T0>
using Vec2 = Vec<T0, 2>;
template<typename T0>
using Vec3 = Vec<T0, 3>;
template<typename T0>
using Vec4 = Vec<T0, 4>;
Access is of the form v[0], v[1], etc. (for brevity I don't include the [] operator overloads). Sometimes I prefer x, y and so on but don't want the extra "." from naming the structs in the union. So using a non-standard "feature" of Visual Studio 2013, tried to use an anonymous union, only enabling the value if S (dimension) is 2, 3 or 4, as follows:
template<typename T0, size_t S, typename = typename std::enable_if<std::is_arithmetic<T0>::value && (S > 1 && S < 5)>::type>
struct Vec
{
using value_type = T0;
using vector_type = Vec<T0, S>;
using array_type = std::array<T0, S>;
using index_type = size_t;
using size_type = size_t;
enum { num_components = S };
union
{
array_type v;
template<typename = typename std::enable_if<S == 2>::type>
struct
{
value_type x, y;
};
template<typename = typename std::enable_if<S == 3>::type>
struct
{
value_type x, y, z;
};
template<typename = typename std::enable_if<S == 4>::type>
struct
{
value_type x, y, z, w;
};
};
};
Unfortunately this gives me the following error:
**error C2332: 'struct' : missing tag name**
And in a way I suppose it is. Is there any way to achieve what I'm trying here? I'm sure enable/disable of an anoymous struct almost certainly gives the compiler a migrane. I can use anonymous union like this if I give the struct a name of course.
Why do you decide that memory layout of std::array and struct{ T x, T y, ... T} will be identical? It can be reached only if you reset alignment setting to 1 for your class with #pragma pack. For others the alignment is unpredictable.
You want to have such class that
provides access via data member selectors such as .x, .y and so on
provides direct access to data member via operator[]
does not break default data member alignment (std::array is linear, it breaks compiler optimization alignment for your class data member)
The following code meets the above requirements without any non-standard features:
template<typename T, size_t S>
struct Vec;
template<typename T>
struct Vec<T, 1> {
enum {count = 1};
T x;
T& operator[](size_t i) {
assert(i == 0);
return x;
}
const T& operator[](size_t i) const {
assert(i == 0);
return x;
}
};
template<typename T>
struct Vec<T, 2> {
enum { count = 2 };
T x;
T y;
T& operator[](size_t i) {
assert(0 <= i && i < count);
return this->*(pointer(i));
}
const T& operator[](size_t i) const {
assert(0 <= i && i < count);
return this->*(pointer(i));
}
static T Vec::* pointer(size_t i) {
static T Vec::* a[count] = { &Vec::x, &Vec::y };
return a[i];
}
};
template<typename T>
struct Vec<T, 3> {
enum { count = 3 };
T x;
T y;
T z;
T& operator[](size_t i) {
assert(0 <= i && i < count);
return this->*(pointer(i));
}
const T& operator[](size_t i) const {
assert(0 <= i && i < count);
return this->*(pointer(i));
}
static T Vec::* pointer(size_t i) {
static T Vec::* a[count] = { &Vec::x, &Vec::y, &Vec::z };
return a[i];
}
};
int main() {
Vec<int, 2> v1{ 1, 2 };
assert(v1[0] == v1.x);
assert(v1[1] == v1.y);
Vec<unsigned char, 3> v2{ 4, 5, 6 };
assert(v2[0] == v2.x);
assert(v2[1] == v2.y);
assert(v2[2] == v2.z);
return 0;
}
Related
I'm still rather new to TMP so forgive me if this is a poorly worded question.
I'm trying to make a very generic mathematical Vector class to store any number of components, but defaults to 3 and using float as it's base representation. So if you default construct one of these vectors it will hold (0.0f,0.0f,0.0f)
The values themselves are stored in a std::array and I would like to create accessor function for ease of use. I currently have this:
std::array<Type,SIZE> e;
Type x() const {return e.at(0);};
Type y() const {return e.at(1);};
Type z() const {return e.at(2);};
What I am trying to do now is also have one for the 4th component, w but only enable it if the size of this array is >= 4. so something like this:
template<class Type, std::enable_if<.......>>
Type w() const {return e.at(3);};
This is just a vague idea of what I think it should look like. I'm aware concept exists, but I'm also struggling to write one for this situation.
With concepts you can simply do
Type w() const requires (SIZE >= 4) {return e.at(3);};
Prior to C++20 concepts, you can do this:
template<typename Type = float, size_t SIZE = 3>
class Vector {
std::array<Type, SIZE> e;
public:
template<typename Dummy = void, std::enable_if_t<SIZE >= 3, Dummy>* = nullptr>
Type x() const { return e[0]; };
template<typename Dummy = void, std::enable_if_t<SIZE >= 3, Dummy>* = nullptr>
Type y() const { return e[1]; };
template<typename Dummy = void, std::enable_if_t<SIZE >= 3, Dummy>* = nullptr>
Type z() const { return e[2]; };
template<typename Dummy = void, std::enable_if_t<SIZE >= 4, Dummy>* = nullptr>
Type w() const { return e[3]; };
};
Vector v1;
v1.x();
v1.y();
v1.z();
//v1.w(); // ERROR
Vector<int, 4> v2;
v2.x();
v2.y();
v2.z();
v2.w(); // OK
Online Demo
Alternatively, you can also do this, like #Jarod42 suggested:
template<typename Type = float, size_t SIZE = 3>
class Vector {
std::array<Type, SIZE> e;
public:
template<size_t N = SIZE, std::enable_if_t<N >= 3>* = nullptr>
Type x() const { return e[0]; };
template<size_t N = SIZE, std::enable_if_t<N >= 3>* = nullptr>
Type y() const { return e[1]; };
template<size_t N = SIZE, std::enable_if_t<N >= 3>* = nullptr>
Type z() const { return e[2]; };
template<size_t N = SIZE, std::enable_if_t<N >= 4>* = nullptr>
Type w() const { return e[3]; };
};
Online Demo
However, that would allow the user to explicitly make w() accessible to read from an invalid index, eg:
Vector v1; // only has indexes 0..2
v1.w<5>(); // NO ERROR, but accesses index 3
I am currently struggling with writing a function for multidimensional spline interpolation on an N-dimensional grid of points. I have written the following to handle multidimensional splines:
template <class integral_constant_M, class integral_constant_K>
struct spline_MK_t {
integral_constant_M M;
integral_constant_K K;
};
template <class V, class C, class MK, class... MKs> class spline {
public:
constexpr static int M = decltype(MK::M)();
constexpr static int K = decltype(MK::K)();
typedef std::integral_constant<int, M + K> M_plus_K;
typedef typename spline<V, C, MKs...>::coefs_array_t Vs;
typedef my_array<Vs, M_plus_K> coefs_array_t;
spline() = default;
spline(const coefs_array_t & my_coefs): coefs(my_coefs) {}
coefs_array_t get_coefs() const { return(coefs); }
V operator()(const my_array<C, std::integral_constant<int, 1 + sizeof...(MKs)>> & x) const;
private:
coefs_array_t coefs;
};
template <class V, class C, class MK> class spline<V, C, MK> {
public:
constexpr static int M = decltype(MK::M)();
constexpr static int K = decltype(MK::K)();
typedef std::integral_constant<int, M + K> M_plus_K;
typedef my_array<V, M_plus_K> coefs_array_t;
spline() = default;
spline(const coefs_array_t & my_coefs): coefs(my_coefs) {}
coefs_array_t get_coefs() const { return(coefs); }
V operator()(const my_array<C, std::integral_constant<int, 1>> & x) const;
V operator()(const C & x) const;
private:
coefs_array_t coefs;
};
The struct spline_MK_t is used to specify pairs of integers from which extended knot vectors are calculated (I am not worried about calculating the knot vectors right now). The n-th pair of integers would correspond to the n-th dimension (hence, the number of pairs would also define the dimensionality of the spline). My original hope was to use std::pair in a variadic template, but I learned pretty fast of the restrictions on non-type template parameters, and I resorted to the last suggestion provided here.
The class my_array allows to define multidimensional arrays, and it is as follows:
template <class T, class N, class... Ns> class my_array {
public:
typedef typename my_array<T, Ns...>::type Ts;
typedef Ts type[N()];
Ts & operator [](std::size_t n) { return(data[n]); }
const Ts & operator [](std::size_t n) const { return(data[n]); }
Ts * begin() { return(& data[0]); }
Ts * end() { return(& data[N()]); }
const Ts * begin() const { return(& data[0]); }
const Ts * end() const { return(& data[N()]); }
Ts data[N()];
};
template <class T, class N> class my_array<T, N> {
public:
typedef T type[N()];
T & operator [](std::size_t n) { return(data[n]); }
const T & operator [](std::size_t n) const { return(data[n]); }
T * begin() { return(& data[0]); }
T * end() { return(& data[N()]); }
const T * begin() const { return(& data[0]); }
const T * end() const { return(& data[N()]); }
T data[N()];
};
I would like to be able to calculate an N-dimensional interpolating spline from either an N-dimensional object of type my_array and N 1-dimensional arrays (also defined by instantiating my_array) or a nested my_array (with the number of nestings corresponding to the dimensionality N) and N 1-dimensional arrays.
In other words, I am looking for the declaration of spapi so that either block of code below would compile (the same function has to handle any dimensionality greater than 1, not just 2 as shown below):
#define MX 3
#define KX 4
#define MY 2
#define KY 7
int main(int argc, char **argv) {
my_array<float, std::integral_constant<int, MX + KX>, std::integral_constant<int, MY + KY>> v;
my_array<float, std::integral_constant<int, MX + KX>> x;
my_array<float, std::integral_constant<int, MY + KY>> y;
... // initialize v, x, y
spline<float, float,
spline_MK_t<std::integral_constant<int, MX>, std::integral_constant<int, KX>>,
spline_MK_t<std::integral_constant<int, MY>, std::integral_constant<int, KY>>> s =
spapi<float, float,
spline_MK_t<std::integral_constant<int, MX>, std::integral_constant<int, KX>>,
spline_MK_t<std::integral_constant<int, MY>, std::integral_constant<int, KY>>>(v, x, y);
return 0;
}
OR
#define MX 3
#define KX 4
#define MY 2
#define KY 7
int main(int argc, char **argv) {
my_array<my_array<float, std::integral_constant<int, MY + KY>>,
std::integral_constant<int, MX + KX>> v;
my_array<float, std::integral_constant<int, MX + KX>> x;
my_array<float, std::integral_constant<int, MY + KY>> y;
... // initialize v, x, y
spline<float, float,
spline_MK_t<std::integral_constant<int, MX>, std::integral_constant<int, KX>>,
spline_MK_t<std::integral_constant<int, MY>, std::integral_constant<int, KY>>> s =
spapi<float, float,
spline_MK_t<std::integral_constant<int, MX>, std::integral_constant<int, KX>>,
spline_MK_t<std::integral_constant<int, MY>, std::integral_constant<int, KY>>>(v, x, y);
return 0;
}
This question already has answers here:
Naming Array Elements, or Struct And Array Within a Union
(4 answers)
Closed 4 years ago.
I've got this code that I just compiled successfully :
template <typename T, unsigned int N>
struct Vector
{
struct Vec1
{
T x;
};
struct Vec2 : public Vec1
{
T y;
};
struct Vec3 : public Vec2
{
T z;
};
struct Vec4 : public Vec3
{
T w;
};
template <unsigned int N>
union Data
{
std::array<T, N> components;
};
template <>
union Data<1>
{
Vec1 vec;
std::array<T, 1> components;
};
template <>
union Data<2>
{
Vec2 vec;
std::array<T, 2> components;
};
template <>
union Data<3>
{
Vec3 vec;
std::array<T, 3> components;
};
template <>
union Data<4>
{
Vec4 vec;
std::array<T, 4> components;
};
Data<N> data;
};
It works as intended, however I would like the struct Vector to expose the data's variables as its own member variables.
Is it possible?
The solution would allow me to do Vector<int, 3> vec; vec.x ...; vec.components[0] ...;
The purpose of the union is to access easily both the vector's components as an array and individually.
Also, if you happen to know a better way to implement the templated union Data specializations, please say so as I find it kinda hard coded. It would be perfect to recursively add variables without having to add the variables of the previous specialization.
For example, I would only need to declare T x once.
I think you need to bring some clarity to your design and the code.
Use of
template <>
union Data<3>
{
T x;
T y;
T z;
std::array<T, 3> components;
};
does not sound right. You need to have {x, y, z} or components, not x, or y, or z, or components. What you need is something along the lines of
template <>
union Data<3>
{
struct
{
T x;
T y;
T z;
} members;
std::array<T, 3> components;
};
Having said that, the cleanest member variable is just
std::array<T, N> components;
As far as the member variables are concerned, Vector can be defined as:
template <typename T, unsigned int N>
struct Vector
{
std::array<T, N> components;
};
If you need to expose the elements of components through x, y, and z-like abstractions, it will be better to add member functions.
template <typename T, unsigned int N>
struct Vector
{
std::array<T, N> components;
T& x()
{
static_assert(N > 0);
return components[0];
}
T& y()
{
static_assert(N > 1);
return components[1];
}
T& z()
{
static_assert(N > 2);
return components[2];
}
};
with the above definition of Vector, the following main function should work.
int main()
{
Vector<int, 1> v1;
v1.x() = 20;
Vector<int, 2> v2;
v2.x() = 20;
v2.y() = 30;
Vector<int, 3> v3;
v3.x() = 20;
v3.y() = 30;
v3.z() = 40;
}
If you use
Vector<int, 2> v2;
v2.z() = 20;
you should get a compile-time error.
You can add the const versions of the above functions to make the member functions work with const objects too.
template <typename T, unsigned int N>
struct Vector
{
std::array<T, N> components;
T& x()
{
static_assert(N > 0);
return components[0];
}
T const& x() const
{
static_assert(N > 0);
return components[0];
}
T& y()
{
static_assert(N > 1);
return components[1];
}
T const& y() const
{
static_assert(N > 1);
return components[1];
}
T& z()
{
static_assert(N > 2);
return components[2];
}
T const& z() const
{
static_assert(N > 2);
return components[2];
}
};
I would like to implement a STL-like tensor template in C++, which needs overload operator[] to access its elements, which may looks like this:
Tensor<double,2> tensor2
{
{1,2,3},
{4,5,6}
};
std::cout<< tensor2[1][2]<<'\n';
For low dimensions, it may be easy to use some thing like
std::vector<std::vector<int>>
But this would not be easy to use for higher dimensions.
Also I would like to implement product between tensors and contraction between dimensions in one tensor.
I implemented the multi-dimensional array with some very basic operations. Since Boost.MultiArray offers a far better solution, my implementation is just for demonstration.
namespace DataStructures
{
struct ExtentList //Discribe extents of dimensions for MultiArray
{
std::vector<size_t> dimensions;
ExtentList() = default;
template<typename Iterator, typename SFINAE = std::enable_if_t<std::_Is_iterator_v<Iterator>>>
ExtentList(Iterator begin, Iterator end) : dimensions(begin, end) {}
//operator[] used to initialize the extents
ExtentList& operator[](size_t n) { dimensions.push_back(n); return *this; }
ExtentList after_front() const { return ExtentList(++dimensions.cbegin(), dimensions.cend()); }
//at() used to access extents
size_t at(size_t n) const { return dimensions.at(n); }
};
static ExtentList Extents;
template<
typename ElemType, //Underlying Type
size_t Dimension, //Dimension of MultiArray
typename ElementAllocator = std::allocator<ElemType>, //Allocator for elements
template<typename, typename> typename ContainerType = std::vector, //Underlying container type
template<typename> typename ContainerAllocator = std::allocator> //Allocator for container
class MultiArray
{
//Necessary for contructor with ExtentList
friend class MultiArray<ElemType, Dimension + 1U, ElementAllocator, ContainerType, ContainerAllocator>;
using value_type = typename
std::conditional_t<
Dimension == 1U,
ElemType,
MultiArray<ElemType, Dimension - 1U, ElementAllocator, ContainerType, ContainerAllocator>>;
using allocator_type = typename
std::conditional_t<
Dimension == 1U,
ElementAllocator,
ContainerAllocator<value_type>>;
ContainerType<value_type, allocator_type> data;
public:
MultiArray() = default;
MultiArray(size_t n, const value_type& val) : data(n, val) {}
template<typename SFINAE = std::enable_if_t<(Dimension == 1U)>>
MultiArray(ExtentList extents, const ElemType& elem) : data(extents.at(0), elem) {}
template<typename SFINAE = std::enable_if_t<(Dimension >= 2U)>, typename SFINAE2 = SFINAE>
MultiArray(ExtentList extents, const ElemType& elem) : data(extents.at(0), value_type(extents.after_front(), elem)) {}
MultiArray(std::initializer_list<value_type> ilist) : data(ilist) {}
template<typename ... SizeType>
MultiArray(size_t N, SizeType... args) : data(N, value_type(args...)) {}
value_type& operator[](size_t n) { return data[n]; }
void push_back(const value_type& elem) { data.push_back(elem); }
};
}
The idea is to implement the multi-dimensional array with recursive template, so subscription and list initialization operations are allowed.
namespace DS = DataStructures;
DS::MultiArray<int, 2> matrix_2d
{
{ 1,2,3 },
{ 4,5,6 },
{ 7,8,9 }
};
for (size_t i = 0; i != 3; ++i)
for (size_t j = 0; j != 3; ++j)
std::cout << matrix_2d[i][j] << ' ';
DS::MultiArray<int, 3> matrix_3d(DS::Extents[10][10][10], 0);
size_t sum = 0;
for (size_t i = 0; i != DS::Extents.at(0); ++i)
for (size_t j = 0; j != DS::Extents.at(1); ++j)
for (size_t k = 0; k != DS::Extents.at(2); ++k)
sum += (matrix_3d[i][j][k] = i * 100 + j * 10 + k);
std::cout << sum << '\n' << matrix_3d[9][9][9] << '\n';
The idea of ExtentList comes from Boost. It is better than variable function parameter list or variable template.
I'm in the process of creating a vector class and am trying to figure out ways to reuse the maximum amount of code for different size vectors.
Here's a basic example:
template<typename T, unsigned int D>
class Vector
{
public:
union {
T v[D];
struct {
/* T x;
* T y;
* T z;
* T w;
*/
};
};
Vector()
{
for(unsigned int i=0; i<D; ++i)
(*this)[i] = T(0);
}
Vector(T scalar)
{
for(unsigned int i=0; i<D; ++i)
(*this)[i] = scalar;
}
inline T operator[](int i) { return (*this).v[i]; }
};
I want the member variables to be publicly accessible. Ex:
Vector<float,2> vec;
printf("X: %.2f, Y: %.2f\n", vec.x, vec.y);
What I'd like to do is something along the lines of this:
template<typename T>
class Vector2 : public Vector<T,2, struct { T x; T y; }> {};
template<typename T>
class Vector3 : public Vector<T,2, struct { T x; T y; T z; }> {};
and have it override a struct in the union:
template<typename T, unsigned int D, struct C>
class Vector
{
public:
union {
T v[D];
// Place the passed struct here
};
};
Is there any feasible way to do this? I do not want to use anything other than the standard library if possible. Thanks in advance.
EDIT: After reading upon all of the answers, I have understood that the way I am using unions is incorrect! Thank you to #M.M for pointing this out. I have since chosen to go a different route, but I have selected the answer that best fit what I was looking for at the time. Once again, thank you for all of the welcomed responses below!
What you are trying to do is not allowed.
Anyway, you can do this:
template<typename T>
struct S { T x; T y; };
template<typename T>
class Vector2 : public Vector<T,2,S<T>> {};
Or this:
template<typename T>
class Vector2 : public Vector<T,2,S> {};
In the second case, Vector can be defined as:
template<typename T, unsigned int D, template<typename> class S>
class Vector {
using MyStruct = S<T>;
// ...
union {
T v[D];
MyStruct myStruct;
};
};
It doesn't scale very well to a large D, but if you're just after the four to six variants I'm imagining, you could partial-specialize a base class:
#include <iostream>
template<typename T, size_t D>
struct VectorBase;
template<typename T>
struct VectorBase<T, 2>
{
constexpr VectorBase() : v{} {}
union {
T v[2];
struct { T x, y; };
};
};
template<typename T>
struct VectorBase<T, 3>
{
constexpr VectorBase() : v{} {}
union {
T v[3];
struct { T x, y, z; };
};
};
template<typename T>
struct VectorBase<T, 4>
{
constexpr VectorBase() : v{} {}
union {
T v[4];
struct { T x, y, z, w; };
};
};
template<typename T, size_t D>
struct Vector : public VectorBase<T, D>
{
using VectorBase<T, D>::v;
using size_type = decltype(D);
using value_type = T;
constexpr Vector() : VectorBase<T,D>{} {}
constexpr Vector(T scalar) {
std::fill(std::begin(v), std::end(v), scalar);
}
constexpr T& operator[](size_type i) const noexcept { return v[i]; }
constexpr const T& operator[](size_type i) noexcept { return v[i]; }
constexpr size_type size() const noexcept { return D; }
constexpr T* data() noexcept { return &v[0]; }
constexpr const T* data() const noexcept { return &v[0]; }
};
template<typename T>
using Vector2 = Vector<T, 2>;
template<typename T>
using Vector3 = Vector<T, 3>;
template<typename T>
using Vector4 = Vector<T, 4>;
int main() {
Vector3<int> v{1};
std::cout << v[0] << ", " << v.z << "\n";
return 0;
}
Live demo: http://ideone.com/T3QHoq
If I understood you correctly your main purpose was to to declare order of fields that corresponds to the array elements of templated class. You cannot do this directly as templates does not accept inline type as parameter. To workaround the problem you could play with non-type template parameters to bind some labels to given index of an array:
#include <cstdio>
#include <unordered_map>
#include <utility>
struct Label { } x, y, z, w;
template <Label&... labels>
struct Pack { };
template <class, class>
struct VectorParent;
template <Label&... labels, size_t... Is>
struct VectorParent<Pack<labels...>, std::index_sequence<Is...>> {
static std::unordered_map<Label *, size_t> label_map;
};
template <Label&... labels, size_t... Is>
std::unordered_map<Label *, size_t> VectorParent<Pack<labels...>, std::index_sequence<Is...>>::label_map = {{&labels, Is}...};
struct LabelNotFound { };
template <class T, size_t N, Label&... labels>
struct Vector:VectorParent<Pack<labels...>, std::make_index_sequence<sizeof...(labels)>> {
static_assert(N == sizeof...(labels),
"the cound of labels should corespond to the number of elements of the vector");
using VectorParent<Pack<labels...>, std::make_index_sequence<sizeof...(labels)>>::label_map;
T t[N];
T &operator->*(Label& l) {
auto it = label_map.find(&l);
if (it == label_map.end())
throw LabelNotFound{};
return t[it->second];
}
};
int main() {
Vector<float,2,x,y> vec;
vec->*x = 10.0f;
printf("X: %.2f, Y: %.2f\n", vec->*x, vec->*y); // prints: X: 10.00, Y: 0.00
//vec->*w = 10.1f; //would throw an exception LabelNotFound
}