I'm new to SFINAE and I'm trying to write a simple Vector template. What I'm trying to achieve is to enable the z member variable based on the dimensions set for the Vector.
I have tried to achieve this effect using the following code:
template<unsigned int DIMENSIONS>
class Vector {
// The x and y variables (same as z)
template<typename = typename std::enable_if<DIMENSIONS >= 3>::type>
/// <summary>
/// The z coordinate.
/// </summary>
Float& z;
Vector() : x(values[0]), y(values[1]) {
}
// Other member functions and variables
std::vector<float> values;
};
template <>
Vector<3>::Vector() : x(values[0]), y(values[1]), z(values[2]) {
}
This should enable the z variable when there are 3 or more dimensions. This makes the compiler complain with the following error:
'Vector<DIMENSIONS>::z': only static data member templates are allowed
Also, I'm not entirely sure how to use initialization lists with SFINAE in such a situation. Because if the dimensions are smaller than 3, z would not have to be initialized (since it wouldn't exist). So far I've used a specialized constructor for 3D vectors (see the code above).
But intelliisense still reports that Members 'x', 'y', 'z' are not initialized in this constructor.
Any help would be appreciated.
You could do something like this:
struct no_z_var {};
struct z_var
{
float z;
};
template<std::size_t n>
struct my_vector : std::conditional_t<( n >= 3 ), z_var, no_z_var>
{
float x, y;
};
int main()
{
my_vector<3> mv3;
mv3.z = 3.4f;
my_vector<2> mv2;
mv2.z = 3.4f; // error; no 'z'
}
However, is this a good solution/design? I'm not so sure; it can get messy. It will most likely present other difficulties during the implementation...
You simply can't conditionally enable variables like that. Your best bet is just to provide multiple specializations of Vector:
template <>
struct Vector<2> {
float x, y;
Vector(std::vector<float> const& values)
: x(values[0])
, y(values[1])
{ }
};
template <>
struct Vector<3> {
float x, y, z;
Vector(std::vector<float> const& values)
: x(values[0])
, y(values[1])
, z(values[2])
{ }
};
// etc.
Though it might be more straightforward to use an array instead:
template <size_t DIM>
struct Vector {
std::array<float, DIM> values;
Vector(std::vector<float> const& vs)
: Vector(vs, std::make_index_sequence<DIM>{})
{ }
private:
template <size_t... Is>
Vector(std::vector<float> const& vs, std::index_sequence<Is...> )
: values{{vs[Is]...}}
{ }
};
You could use inheritance.
template<int n>
struct VecBase;
template<>
struct VecBase<1> {
float x;
};
template<>
struct VecBase<2> : VecBase<1> {
float y;
};
template<>
struct VecBase<3> : VecBase<2> {
float z;
};
template<>
struct VecBase<4> : VecBase<3> {
float w;
};
And then define your vector class:
template<int n>
struct Vector : VecBase<n> {
// operations
};
If you want to make n-dimentional operation, you can use std::get and std::index_sequence. Let's start by making overloads for std::get:
namespace std {
template<size_t I, int n, enable_if_t<(I == 0 && I < n), int> = 0>
float& get(VecBase<n>& vec) {
return vec.x;
}
template<size_t I, int n, enable_if_t<(I == 1 && I < n), int> = 0>
float& get(VecBase<n>& vec) {
return vec.y;
}
template<size_t I, int n, enable_if_t<(I == 2 && I < n), int> = 0>
float& get(VecBase<n>& vec) {
return vec.z;
}
template<size_t I, int n, enable_if_t<(I == 3 && I < n), int> = 0>
float& get(VecBase<n>& vec) {
return vec.w;
}
}
Note that you will have to implement them for const.
Then, you can implement your operations by making a base class that implement operations:
template<int, typename>
struct VecOps;
template<int n, std::size_t... S>
struct VecOps<n, std::index_sequence<S...>> : VecBase<n> {
float dotp(Vector<n>& vec) const {
// Where sum is adding each parameter variadically.
return sum((std::get<S>(*this) * std::get<S>(vec))...);
}
};
And finally, make Vector extending VecOps:
template<int n>
struct Vector : VecOps<n, std::make_index_sequence<n>> {};
Note that I don't have a compiler at my disposition for the moment. If your got compiler error, just leave a comment and I'll check this out.
Specialize the entrie class for 1, 2, 3 dimensions if you want this, i.e., template<> class Vector<3> { ... }.
You don't really need SFINAE here. Just use specialization:
template<unsigned int DIMENSIONS>
class Vector {
template<int I>
struct ZContainer {};
template<> struct ZContainer<3> {
float z;
};
ZContainer<DIMENSIONS> possibleZ;
};
This has the advantage that you don't need additional templates. You can just add functions to the ZContainers for your class behavior.
Related
The point is to create uniform struct for vertex and for matrix in c++
Pseudocode:
vector<COUNT, T> {
union {
T data[COUNT];
struct { T x, y, z, w; } // if size is 1 -> only x, if 2 -> only x and y ... etc
struct { T u, v; } // same }
}
template<COUNT, VCOUNT, T>
matrix<COUNT, VCOUNT> : vector<COUNT, vector<VCOUNT, T>> {}
And use like this:
void foo() {
v3f pos(0,1,0);
pos.xy /= pos.z;
m4f transform = {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,3.456}}
transform.x.y = transform[3][2];
}
I am too close to make this possible, the only thing I can't reach is initialization of matrix:
v4f v; // v4f typedef of vector<4,float>, m4f is matrix<4,4,float>
v = {0,0,1,0}; //work
m4f mat = {v,v,{},v}; //don't work "excess elements in struct initialization"
m4f mat = {{v,v,{}, v}}; // same
Some implementation details
namespace math::detail
{
template<int SIZE, typename T>
struct vector_base {
T _[SIZE];
};
template<typename T>
struct vector_base<1, T> {
union { T _[1]; T x; };
};
template<typename T>
struct vector_base<2, T> {
union { T _[2]; struct { T x, y; }; struct { T u, v; }; };
};
template<typename T>
struct vector_base<3, T> {
union { T _[3]; struct { T x, y, z; }; };
};
template<typename T>
struct vector_base<4, T> {
union { T _[4]; struct { T x, y, z, w; }; };
};
}
namespace math
{
enum VecI { X, Y, Z, W, U = X, V = Y };
template<int SIZE, typename T>
struct vector : detail::vector_base<SIZE, T>
{
using base = detail::vector_base<SIZE, T>;
vector() = default;
template<typename ... Types,
typename std::enable_if<sizeof...(Types) == SIZE, int>::type = 0>
constexpr vector(Types ... s) : base{static_cast<T>(s)... } {}
// some foos, operator overloading
}
template<int size, int vector_size, typename type>
struct matrix : vector<size, vector<vector_size, type>>
{
// special foos only for matrix
}
}
Vector constructor with parameter pack is not compiling in case of matrix:
error: no matching constructor for initialization of 'vector<3, vector<3, float> >'
return {{{1,0,0}, {0,1,0}, {0,0,1}}};
^~~~~~~~~~~~~~~~~~~~~~~~~~~
/math/vector/base.h:62:15: note: candidate template ignored: requirement 'sizeof...(Types) == 3' was not satisfied [with Types = <>]
constexpr vector(Types ... s) : base{static_cast<T>(s)... } {}
^
Next implementation of matrix at least compile, but need initializing with extra braces {{{},{},{}} and can't inherit some vector properties:
template<int size, int vector_size, typename type>
struct matrix
{
vector _[size];
// foos
}
I am also tried writing constructors for vector_base, for matrix, deduction guides for all of them, nothing works.
How to initialize array of arrays in C++ with automatic deduction (can initialize {5, 0.43, .1f} without errors of type incompatibilities), without implicitly writing types like in m4f mat = {v4f(0,0,1),v4f(0,1,0), v4f(1,0,0)}, and without "excess" nested braces {{{}}}?
My compiler is clang:9.0.0 with c++17, also can use c++2a, or c++20 when it comes.
Answer to your actual question
C++ is just lousy at deducing things from braced lists. It won't look at the lists, notice your constructor is initializing a member with them, and try to fit the braces to that member. Basically, multi-level brace initialization only works if it doesn't have to deduce anything (e.g. a non-template constructor, aggregate initialization, or a varible-length initializer_list)
To get this working for you, give each base type a simple constructor:
template<typename T>
struct vector_base<2, T> {
vector_base(T x, T y) : x(x), y(y) { } // <-- add these to each specialization
union { T _[2]; struct { T x, y; }; struct { T u, v; }; };
};
Then, for the generic derived type, promote the constructor:
template<int SIZE, typename T>
struct vector : detail::vector_base<SIZE, T>
{
using detail::vector_base<SIZE, T>::vector_base;
/* overloads and methods */
};
Now all forms of vector have a non-generic constructor and brace-initialization works as expected.
math::vector<3, math::vector<3, float>> foo {{1,0,0},{0,1,0},{0,0,1}};
Now, about the type punning
There's actually a neat trick for adding array-like access to struct members without breaking strict aliasing here: Overload the [] operator to access a static constexpr array of member-to-pointers to your struct members.
Then there is only one level of indirection between array-like access and member access, in a constant-foldable map. This allows the compiler to, in most cases, produce the same assembly as if you used union punning or a reinterpret_cast to access the member data in a for loop.
Applied to your code:
template <size_t SIZE, typename T>
struct vector_base;
template <typename T>
struct vector_base<1, T> {
constexpr vector_base(T x) : x(x) {}
T x;
constexpr T& operator [](size_t index) { return this->*(vector_base::map[index]); }
constexpr const T& operator [](size_t index) const { return this->*(vector_base::map[index]); }
static constexpr T vector_base::* map[1] = {&vector_base::x};
};
template <typename T>
struct vector_base<2, T> {
constexpr vector_base(T x, T y) : x(x), y(y) {}
union {
T x;
T u;
};
union {
T y;
T v;
};
constexpr T& operator [](size_t index) { return this->*(vector_base::map[index]); }
constexpr const T& operator [](size_t index) const { return this->*(vector_base::map[index]); }
static constexpr T vector_base::* map[2] = {&vector_base::x, &vector_base::y};
};
template <typename T>
struct vector_base<3, T> {
constexpr vector_base(T x, T y, T z) : x(x), y(y), z(z) {}
T x;
T y;
T z;
constexpr T& operator [](size_t index) { return this->*(vector_base::map[index]); }
constexpr const T& operator [](size_t index) const { return this->*(vector_base::map[index]); }
static constexpr T vector_base::* map[3] = {&vector_base::x, &vector_base::y, &vector_base::z};
};
template <typename T>
struct vector_base<4, T> {
constexpr vector_base(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {}
T x;
T y;
T z;
T w;
constexpr T& operator [](size_t index) { return this->*(vector_base::map[index]); }
constexpr const T& operator [](size_t index) const { return this->*(vector_base::map[index]); }
static constexpr T vector_base::* map[4] = {&vector_base::x, &vector_base::y, &vector_base::z, &vector_base::w};
};
Note: the union between x/y and u/v is actually well-formed, even for accessing inactive members
Demo: https://godbolt.org/z/ibpSqB
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 have template class which looks like this:
template<int n=3> struct Vec{
double Values[n];
};
Of course I can access elements of this class directly or by method, for example:
double& x(){
return Values[0];
}
But if I would like to make more intuitive code: instead of
Vec<3> v;
v.x() = 5.2;
I would like to let user do it by:
v.x = 5.2. It's not only a whim. If I have existing library which uses simple vector structures of a form like struct{double x,y,z;}, I could by method of duck-typing create my template class to be efficient in this case. Of course I could (probably, I'm not sure) pass to this library predefined structure of referenes - like:
struct{double &x=v.Values[0], &y=v.Values[1], &z=v.Values[2];} but I am afraid that's not the simplest way to get a goal.
Ah - of course I could add reference-type parameters in my structure, but this would cause in increase of a size of elements.
Here's one way to get the same syntactic effect:
template<int n=3> struct Vec{
double Values[n];
};
template<int N> struct xyzAdapter;
template<> struct xyzAdapter<3>
{
xyzAdapter(Vec<3>& vec) : x(vec.Values[0]), y(vec.Values[1]), z(vec.Values[2]) {}
double& x;
double& y;
double& z;
};
template<int N> auto make_adapter(Vec<N>& vec)
{
return xyzAdapter<N>(vec);
}
int main()
{
Vec<3> point;
auto adapter = make_adapter(point);
adapter.x = 6;
}
Going the other way is not quite so pleasant. There is no such thing as an array of references, so one workaround is to resort to an array of std::reference_wrapper:
#include <tuple>
#include <array>
#include <functional>
#include <iostream>
template<int N = 3> struct Vector;
template<> struct Vector<3>
{
double x, y, z;
auto as_tuple() {
return std::tie(x, y, z);
}
};
template<std::size_t...Is, class Tuple>
auto as_array(std::index_sequence<Is...>, Tuple&& t)
{
return std::array<std::reference_wrapper<double>, sizeof...(Is)> {
std::get<Is>(t)...
};
}
template<int N> auto as_array(Vector<N>& v)
{
return as_array(std::make_index_sequence<N>(), v.as_tuple());
}
int main2()
{
Vector<3> point;
point.x = 6;
auto arr = as_array(point);
for (auto ref : arr) {
std::cout << ref.get() << std::endl;
}
return 0;
}
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
}
I've a code smth like this:
template<int N, typename T>
class XYZ {
public:
enum { value = N };
//...
}
Is there a way to restrict N in some way? Specifically I want to allow compilation only if N is divided by some number, let's say 6.
So it turned out to be not just a type restriction.
Preferred way is to do this without Boost.
One C++03 approach:
template<int X, int Y>
struct is_evenly_divisible
{
static bool const value = !(X % Y);
};
template<int N, typename T, bool EnableB = is_evenly_divisible<N, 6>::value>
struct XYZ
{
enum { value = N };
};
template<int N, typename T>
struct XYZ<N, T, false>; // undefined, causes linker error
For C++11, you can avoid some boilerplate and give a nicer error message:
template<int N, typename T>
struct XYZ
{
static_assert(!(N % 6), "N must be evenly divisible by 6");
enum { value = N };
};
I leave this here for the future, since I couldn't find a good example online at the time of posting.
The C++20 way with concepts:
template<int X, int Y>
concept is_evenly_divisible = X % Y == 0;
template <int N, int M> requires is_evenly_divisible<N, M>
struct XYZ
{
enum class something { value = N };
};
XYZ<12, 6> thing; // OK
//XYZ<11, 6> thing; // Error
Or even shorter:
template <int N, int M> requires (N % M == 0)
struct XYZ
{
enum class something { value = N };
};