I try to make a generic, but still efficient multi dimension Point class.
What I have is a Dimensions enum
enum Dimension : std::size_t { _2D = 2, _3D = 3 };
And a Point class
template <typename T, Dimension D>
class Point : public std::array<T, D>
{
public:
T& at(size_t idx) { return std::array<T,D>::at(idx); };
const T& at(size_t idx) const { return std::array<T,D>::at(idx); };
Dimension dim() const { return D; }
...
};
I would like to create nices constructors so I added (outside my class definition)
template <typename T>
Point<T,_2D>::Point(T x, T y) { at(0) = x; at(1) = y; }
template <typename T>
Point<T,_3D>::Point(T x, T y, T z) { at(0) = x; at(1) = y; at(2) = z; }
But still I cannot use thoses. The compiler tells me only default (empty) and copy constructors are registered.
Question:
How do I define constructors with arguments list length being dependant on my Dimension template ?
I would do it like this because I'm too lazy to write many versions of the same thing:
template<class T, Dimension D>
class Point : public std::array<T,D> {
template<class... Args>
Point(Args... vs) :
std::array<T,D>{{vs...}}
{
static_assert(sizeof...(Args) == D, "wrong number of args");
}
...
};
There are a few ways to accomplish this. In your case, it might be easiest to use std::enable_if:
template <typename T, Dimension D>
class Point : public std::array<T, D>
{
public:
template <
Dimension D2=D,
typename = typename std::enable_if<D2==_2D>::type
>
Point(T x,T y);
template <
Dimension D2=D,
typename = typename std::enable_if<D2==_3D>::type
>
Point(T x,T y,T z);
T& at(size_t idx) { return std::array<T,D>::at(idx); };
const T& at(size_t idx) const { return std::array<T,D>::at(idx); };
Dimension dim() const { return D; }
...
};
Another option would be to break this into multiple classes and use specialization:
template <typename T, Dimension D>
class PointBase : public std::array<T, D>
{
public:
T& at(size_t idx) { return std::array<T,D>::at(idx); };
const T& at(size_t idx) const { return std::array<T,D>::at(idx); };
Dimension dim() const { return D; }
...
};
template <typename T, Dimension D> class Point;
template <typename T>
class Point<T,_2D> : public PointBase<T,_2D> {
public:
Point(T x,T y);
};
template <typename T>
class Point<T,_3D> : public PointBase<T,_3D> {
public:
Point(T x,T y,T z);
};
Related
I have a class that wraps some type and attaches a Dimension. It should be convertible to the underlying type, but only if Dim=0. The conversion operator should not be callable in other cases (so a static_assert in the function would not work for me).
The following code works, if the enable_if-construction is deleted, but not in this form.
template<class T, int Dim>
class Unit {
public:
explicit Unit(T const& value): _value(value) {}
template<int D = Dim, typename = typename std::enable_if<D == 0>::type>
operator T() { return _value; }
private:
T _value;
};
auto main() -> int
{
auto a = double{0};
auto u = Unit<double, 0>{a};
auto i = static_cast<int>(u);
return i;
}
What is the reason for this and is there a work around to allow the cast, but also restrict the conversion?
As I understand, you want:
template <class T, int Dim>
class Unit {
public:
explicit Unit(T const& value): _value(value) {}
template <typename U, int D = Dim,
std::enable_if_t<D == 0 && std::is_convertible_v<T, U>, int> = 0>
operator U() { return _value; }
private:
T _value;
};
Demo
And in C++20, look nicer
template<class T, int Dim>
class Unit {
public:
explicit Unit(T const& value): _value(value) {}
template <typename U>
requires(Dim == 0 && std::is_convertible_v<T, U>)
operator U() const { return _value; }
private:
T _value;
};
Demo
Is there any way in C++17 or older to use single template implementation of function compute in the code below?
template <typename T>
class C
{
public:
T x, y;
C() = default;
C(T x, T y) : x(x), y(y) {}
operator T() const { return x; }
C operator +(const C<T> &c) const { return C(x + c.x, y + c.y); }
C operator +(const T &t) const { return C(x + t, y); }
};
template <typename T>
void compute(C<T> *a, T *b, size_t size)
{
while (size--)
a[size] = a[size] + b[size];
}
template <typename T>
void compute(T *a, C<T> *b, size_t size)
{
while (size--)
a[size] = a[size] + b[size];
}
template <typename T>
void compute(C<T> *a, C<T> *b, size_t size)
{
while (size--)
a[size] = a[size] + b[size];
}
int main()
{
const size_t size = 4;
C<float> a[size];
float b[size];
compute(a, b, size);
compute(b, a, size);
compute(a, a, size);
}
Yes, if you are willing to tolerate a little boilerplate (I imagine you have many, many functions just like compute).
template <typename A, typename B>
struct is_cable_t : std::false_type {};
template <typename A>
struct is_cable_t<A, C<A>> : std::true_type {};
template <typename A>
struct is_cable_t<C<A>, A> : std::true_type {};
template <typename A>
struct is_cable_t<C<A>, C<A>> : std::true_type {};
template <typename A, typename B>
constexpr bool is_cable_v = is_cable_t<A,B>::value;
template <typename K, typename S,
typename R = typename std::enable_if<is_cable_v<K,S>>::type>
void compute(K *a, S *b, size_t size)
{
while (size--)
a[size] = a[size] + b[size];
}
Imagine you have a simple 2D Point object with two setters and getters.
template <typename T>
class Point
{
public:
Point(T x, T y);
T getX() const;
T getY() const;
void setX(T x);
void setY(T y);
private:
T _x;
T _y;
};
But I want to work with this class in more 'scriptable-like' syntax. Something like :
auto point = Point<double>(10, 10);
point.x = 20;
point.y = point.x + 10;
You will say, just use a struct with public variable :
template <typename T>
struct Point
{
T x;
T y;
};
Yes, but I want to keep the privacy of the parameters and extend the class with some methods. So another idea is to make a wrapper helper that add operator alias to the setters/getters :
template <typename T, typename Get, Get(T::*Getter)() const,
typename Set, void(T::*Setter)(Set)>
struct ReadWrite
{
ReadWrite(T& ptr) : ptr(ptr) {}
inline void operator= (Set const& rhs)
{
(ptr.*Setter)(rhs);
}
inline Get operator()()
{
return (ptr.*Getter)();
}
private:
T& ptr;
};
OK, I just modify my Point class to do the work :
template <typename T>
class Point
{
public:
Point(T x, T y);
T getX() const;
T getY() const;
void setX(T x);
void setY(T y);
private:
T _x;
T _y;
public:
ReadWrite<Point<T>, T, &Point<T>::getX, T, &Point<T>::setX> x;
ReadWrite<Point<T>, T, &Point<T>::getY, T, &Point<T>::setY> y;
};
By adding some arithmetics operators ( + - * / ), I can use it like that:
auto point = Point<double>(10, 10);
point.x = 20;
point.y = point.x + 10;
Here, point.x is ok in case of operator overloading in the form:
template <typename T, typename V> inline T operator+(ReadWrite<T> const& lhs, V const& rhs) { return lhs() + rhs; }
template <typename T, typename V> inline T operator-(ReadWrite<T> const& lhs, V const& rhs) { return lhs() - rhs; }
template <typename T, typename V> inline T operator*(ReadWrite<T> const& lhs, V const& rhs) { return lhs() * rhs; }
template <typename T, typename V> inline T operator/(ReadWrite<T> const& lhs, V const& rhs) { return lhs() / rhs; }
If I want use this syntax, but without parenthesis on point.x getter :
auto point = Point<double>(10, 10);
auto x = point.x();
I extend the ReadWrite helper with:
template <typename T, typename Get, Get(T::*Getter)() const,
typename Set, void(T::*Setter)(Set)>
struct ReadWrite
{
ReadWrite(T& ptr) : ptr(ptr) {}
inline void operator= (Set const& rhs)
{
(ptr.*Setter)(rhs);
}
inline Get operator()()
{
return (ptr.*Getter)();
}
inline operator auto() -> Get
{
return operator()();
}
private:
T& ptr;
};
Now with no parenthesis:
double x = point.x; // OK, x is my x value (Point).
auto x = point.x; // Wrong, x is my ReadWrite<T> struct.
What is wrong with the overloading of the auto operator?
Thank you very much for your answer.
There is nothing wrong with your class. The problem is how auto deduces types. The thing you have to remember about auto is it basically follows the rules for template argument deduction and the main thing about that is no implicit conversion will be done. This means that in
auto x = point.x;
You say compiler, give me a variable named x that has the type of the initialization expression. In this case point.x is a ReadWrite<Point<T>, T, &Point<T>::getX, T, &Point<T>::setX> so that is the type x gets. The only way to change that is to change what the initialization expression returns.
Unfortunately I'm not sure how you could do that. Proxy objects don't play well with auto type deduction as we pick up their type, not what they are mimicking.
I want to design a Matrix class which allows to specify the method of memory management, i.e.
Matrix<Allocator::vector, int> mv(2, 2);
Matrix<Allocator::unique_pointer, int> mu(2, 2);
mv(1, 1) = 1;
mu(1, 2) = 1;
and with mv and mu being compatible to each other (i.e. when I overload the "+" operator) despite different memory strategies.
I found already very good help from Class template specializations with shared functionality which does the same for an n dimensional vector class.
using dim_t = std::pair<size_t, size_t>;
enum class Allocator { vector, raw_pointer, unique_pointer };
template <typename T>
class MatrixBase {
public:
MatrixBase() : MatrixBase(0, 0){};
MatrixBase(size_t m, size_t n) : dim_(m, n){};
size_t rows() const;
virtual T& at(size_t i, size_t j);
private:
dim_t dim_;
};
template <typename T>
size_t MatrixBase<T>::rows() const {
return dim().first;
}
template <Allocator A, typename T>
class Matrix : public MatrixBase<T> {};
template <typename T>
class Matrix<Allocator::vector, T> : public MatrixBase<T> {
private:
std::vector<T> data_;
};
template <typename T>
T& Matrix<Allocator::vector, T>::at(size_t i, size_t j) {
return data_[i * rows() + j];
}
template <typename T>
class Matrix<Allocator::unique_pointer, T> : public MatrixBase<T> {
private:
std::unique_ptr<T[]> data_;
};
template <typename T>
T& Matrix<Allocator::unique_pointer, T>::at(size_t i, size_t j) {
return data_[i * rows() + j];
}
Unfortunately the compiler complains
./matrix.hpp:100:34: error: out-of-line definition of 'at' does not match any declaration in 'Matrix<linalg::Allocator::vector,
type-parameter-0-0>'
T& Matrix<Allocator::vector, T>::at(size_t i, size_t j) {
^
./matrix.hpp:103:20: error: use of undeclared identifier 'rows'
return data_[i * rows() + j];
I assume the error originates from
template <typename T>
class Matrix<Allocator::vector, T> : public MatrixBase<T> {
private:
std::vector<T> data_;
};
How do I fix this?
I have fixed Your code. Here You go:
using dim_t = std::pair<size_t, size_t>;
enum class Allocator { vector, raw_pointer, unique_pointer };
template <typename T>
class MatrixBase {
public:
MatrixBase() : MatrixBase(0, 0){};
MatrixBase(size_t m, size_t n) : dim_(m, n){};
size_t rows() const;
virtual T& at(size_t i, size_t j);
private:
dim_t dim_;
};
template <typename T>
size_t MatrixBase<T>::rows() const {
return dim_.first;
}
template <Allocator A, typename T>
class Matrix : public MatrixBase<T> {};
template <typename T>
class Matrix<Allocator::vector, T> : public MatrixBase<T> {
public:
T &at(size_t i, size_t j);
private:
std::vector<T> data_;
};
template <typename T>
T& Matrix<Allocator::vector, T>::at(size_t i, size_t j) {
return data_[i * this->rows() + j];
}
template <typename T>
class Matrix<Allocator::unique_pointer, T> : public MatrixBase<T> {
public:
T &at(size_t i, size_t j);
private:
std::unique_ptr<T[]> data_;
};
template <typename T>
T& Matrix<Allocator::unique_pointer, T>::at(size_t i, size_t j) {
return data_[i * this->rows() + j];
}
First of all, if You override a method You have to declare it in derived class. Secondly, the rows() function could not be resolved by default as a member.
Move the definition inside will solve the problem. This problem is due to scoping.
template <typename T>
class Matrix<Allocator::vector, T> : public MatrixBase<T> {
public: // or private:
T& at(size_t i, size_t j) {
return data_[i * MatrixBase<T>::rows() + j];
}
private:
std::vector<T> data_;
};
Consider the following example
class P {
public:
virtual int test();
};
class C : public P {
};
int C::P:: test() { // if you omit P, it will not compile
return 21;
}
Or you can re-declare it inside C, so it will be in C's scope.
I'm trying to implement a Vector class for use in my graphics projects. I'd like to template the vector by length and type, and I'd like to be able to use A.x, A.y, A.z, A.w to access the members of vector A (for example).
Here's my first attempt. I'm definitely not an expert in C++ templates! I was trying to implement a general Vector class with specialized versions for Vec2, Vec3, and Vec4. Each of the specialized classes would have a union allowing me to access the coordinates using their names. Unfortunately, I can't figure out how to do this without re-implementing every vector function for each of the specialized classes.
Keep in mind that I want to implement some functions that only apply to vectors of a certain length. For example, cross product only applies to vec3, but dot product (or operator*) applies to vectors of all length.
#include <cstdint>
using namespace std;
//*********************************************************************
// Vector implementation parameterized by type and size.
//*********************************************************************
template <typename T, size_t SIZE>
class Vector {
public:
T data[SIZE];
size_t size;
Vector(T* arr);
};
template <typename T, size_t SIZE> Vector<T, SIZE>::Vector(T* arr) {
size = SIZE;
for(int i=0; i<size; i++) {
data[i] = arr[i];
}
}
//*********************************************************************
// Vec2 is a specialization of Vector with length 2.
//*********************************************************************
typedef Vector<float, 2> Vec2f;
typedef Vector<int, 2> Vec2d;
template <typename T>
class Vector <T, 2> {
public:
union {
T data[2];
struct {
T x;
T y;
};
};
size_t size;
Vector(T x, T y);
};
template <typename T> Vector<T, 2>::Vector(T x, T y) {
data[0] = x; data[1] = y;
size = 2;
}
//*********************************************************************
// Vec3 is a specialization of Vector with length 3.
//*********************************************************************
typedef Vector<float, 3> Vec3f;
typedef Vector<int, 3> Vec3d;
template <typename T>
class Vector <T, 3> {
public:
union {
T data[3];
struct {
T x;
T y;
T z;
};
};
size_t size;
Vector(T x, T y, T z);
};
template <typename T> Vector<T, 3>::Vector(T x, T y, T z) {
data[0] = x; data[1] = y; data[2] = z;
size = 3;
}
//*********************************************************************
// Vec4 is a specialization of Vector with length 4.
//*********************************************************************
typedef Vector<float, 4> Vec4f;
typedef Vector<int, 4> Vec4d;
template <typename T>
class Vector <T, 4> {
public:
union {
T data[4];
struct {
T x;
T y;
T z;
T w;
};
};
size_t size;
Vector(T x, T y, T z, T w);
};
template <typename T> Vector<T, 4>::Vector(T x, T y, T z, T w) {
data[0] = x; data[1] = y; data[2] = z; data[3] = w;
size = 4;
}
The usual workaround to avoid repeatedly implementing identical features in multiple specializations is to inherit from a common base class, and implement those features in the base:
template <typename T>
struct VectorBase {
// common stuff
};
template <typename T, std::size_t N>
struct Vector : VectorBase<T> {
// ...
};
template <typename T>
struct Vector<T, 2> : VectorBase<T> {
// ...
};
template <typename T>
struct Vector<T, 3> : VectorBase<T> {
// ...
friend Vector<T, 3> cross(const Vector<T, 3>&, const Vector<T, 3>&);
};
The next problem you will have is needing to access members in the derived class from the common base (e.g., get the value of x, or size()). You do that using the Curiously Recurring Template Pattern (CRTP):
template <typename T, typename CRTP>
struct VectorBase {
CRTP& crtp() { return static_cast<CRTP&>(*this); }
const CRTP& crtp() const { return static_cast<const CRTP&>(*this); }
std::size_t size() const {
return std::extent<decltype(CRTP::data)>::value;
}
void zero() {
std::fill(std::begin(crtp().data), std::end(crtp().data), T());
}
using iterator = T*;
using const_iterator = const T*;
iterator begin() { return &crtp().data[0]; }
iterator end() { return &crtp().data[0] + size(); }
const_iterator begin() const { return &crtp().data[0]; }
const_iterator end() const { return &crtp().data[0] + size(); }
T& operator [] (std::size_t i) {
return crtp().data[i];
}
const T& operator [] (std::size_t i) const {
return crtp().data[i];
}
};
template <typename T, std::size_t N>
struct Vector : VectorBase<T, Vector<T, N>> {
union {
T data[N];
struct {
T x, y, z, w;
};
};
};
template <typename T>
struct Vector<T, 2> : VectorBase<T, Vector<T, 2>> {
union {
T data[2];
struct {
T x, y;
};
};
};
template <typename T>
struct Vector<T, 3> : VectorBase<T, Vector<T, 3>> {
union {
T data[3];
struct {
T x, y, z;
};
};
};
template <typename T, typename U, std::size_t N>
auto operator * (const Vector<T, N>& a, const Vector<U, N>& b)
-> Vector<decltype(a[0] * b[0]), N> {
Vector<decltype(a[0] * b[0]), N> result;
for (std::size_t i = 0; i < N; ++i) {
result[i] = a[i] * b[i];
}
return result;
}
template <typename T, typename U, std::size_t N>
auto dot(const Vector<T, N>& a, const Vector<U, N>& b)
-> decltype(a[0] * b[0]) {
auto product = a * b;
using V = decltype(product.x);
return std::accumulate(std::begin(product), std::end(product), V(0));
}
** Sample Code at Coliru **
There two issues with undefined behavior to which the commenters refer are:
The anonymous structs (e.g., struct { T x, y, z; };) are an GNU extension, and will thus likely only work with GCC and compatible compilers (clang).
Reading from a union member other than the member last stored into is typically undefined behavior; this particular example is at least borderline given that every type involved is standard-layout and that all values read/written are of the same type. I'll let someone else do the exact language lawyering and simply state that the code will almost certainly perform as intended when using a recent compiler that supports the anonymous struct extension.
If either of those non-standard requirements bothers you, drop the structs and unions so that the array is the only data member. Then add functions for the symbolic names, e.g. T& x() { return data[0]; }, it's only slightly more cumbersome.