C++: Access class member by member-like method - c++

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;
}

Related

How to build an efficient named array?

I want to have a type that both has named members and also is iterable, so one should be able to refer to the members by index, by label or from a for-each loop. One way to realise this would be to use std::unordered_map<std::string,T> with some helper data for the indices. Clearly this would be horribly inefficient as for each member access you need to hash a std::string.
My current attempt looks like this:
// named-array.h
#pragma once
#include <array>
#include <cstddef>
#define NamedArray_KeyDecl(Name, ...) enum class Name : std::size_t { __VA_ARGS__, NUM }
namespace util {
template <typename K, typename T>
struct NamedArray {
static constexpr std::size_t cast(K k) {
return static_cast<std::size_t>(k);
}
std::array<T,cast(K::NUM)> array;
NamedArray(std::array<T,cast(K::NUM)> a) : array(a) {
}
constexpr T& operator[](K k) {
return array[cast(k)];
}
constexpr T const& operator[](K k) const {
return array[cast(k)];
}
};
}
Which can be used like so:
struct Gadget {
int i;
Gadget(int i) : i(i) {}
void operator()() const {
std::cout << "Gadget(" << i << ")\n";
}
};
NamedArray_KeyDecl(Test1, a,b,c,d);
util::NamedArray<Test1,Gadget> gadgets {{0,1,2,3}};
// for each works:
for (auto const& gadget: gadgets.array) {
gadget();
}
// named access works:
gadgets[Test1::b]();
// access by index works:
gadgets.array[1]();
Exposing the array member could be avoided by forwarding all interface functions of std::array.
However, an obvious drawback is that
gadgets[Test1::b] is not as pretty as something along the lines of gadgets.member().b and
there is an exposed #define in a c++ header file (which is extremely smelly)
Is there a way to have a named array with the same performance as an std::array?
Minimalistic example of how it could be done:
#include <array>
#include <type_traits>
template<class Tag, class...Tags>
struct position {
};
template<class Tag, class...Tags>
struct position<Tag, Tag, Tags...> {
constexpr static unsigned value = 0;
};
template<class Tag, class First, class...Tags>
struct position<Tag, First, Tags...> {
constexpr static unsigned value = 1 + position<Tag, Tags...>::value;
};
template<class T, class...Tags>
class NamedArray {
public:
template<class U>
constexpr T& operator[](U tag) {
return array_[position<U, Tags...>::value];
}
constexpr T& operator[](unsigned val) {
return array_[val];
}
template<class U>
constexpr T& member(U u = U{}) {
return (*this)[u];
}
private:
std::array<T, sizeof...(Tags)> array_;
};
struct tag1{};
struct tag2{};
int main() {
NamedArray<int, tag1, tag2> a;
a[tag1{}];
a[tag2{}];
a.member(tag1{});
a.member<tag1>();
}
Just define the enums as you want them. And leaving them unscoped is fine, you want the names to leak into the declaration's scope, and are helped by the implicit conversion to std::size_t.
template <typename K, typename T>
using NamedArray = std::array<T, K::NUM>;
Then
struct Gadget {
int i;
Gadget(int i) : i(i) {}
void operator()() const {
std::cout << "Gadget(" << i << ")\n";
}
};
enum Test1 : std::size_t { a, b, c, d, NUM };
int main() {
NamedArray<Test1,Gadget> gadgets { 0,1,2,3 };
// for each works:
for (auto const& gadget: gadgets) {
gadget();
}
// named access works:
gadgets[b]();
// access by index works:
gadgets[1]();
}

Calculate something with user defined classes by using variadic templates

struct for coordinates;
struct point{
int x_, y_;
}
Assume we have a coordinates as it is defined above. In addition to this struct i want to create a function which takes as many parameters as user wants(for instance a function to find the barycentre of three or more points). So i have decided to use variadic templates. But i recognized that i am lack of info about variadic templates.
As to calculate barycentre, you should add all x's, divide the sum to element count. And do it for y's of elements and return a point. Can you lead me a way to implement this.
C++17's fold-expressions make this a breeze:
template <class... Points>
point barycentre(Points... points) {
point const sum{
(points.x_ + ...),
(points.y_ + ...)
};
int const num = sizeof...(Points);
return {
sum.x_ / num,
sum.y_ / num
};
}
See it live on Coliru
If you are using C++14 you could simply iterate through points in constexpr function, e.g. like this (example borrowed from Quentin's answer):
#include <iostream>
struct point{
int x_, y_;
};
template <class... Points>
constexpr point barycentre(Points... points) {
point ps[] = {points...};
point result {0, 0};
for (std::size_t i = 0; i < sizeof...(points); i++) {
result.x_ += ps[i].x_;
result.y_ += ps[i].y_;
}
result.x_ /= sizeof...(points);
result.y_ /= sizeof...(points);
return result;
}
int main() {
constexpr auto bary = barycentre(point{0, 0}, point{2, 2});
std::cout << "(" << bary.x_ << ", " << bary.y_ << ")\n";
}
[live demo]
And some C++11:
#include <iostream>
#include <array>
#include <type_traits>
struct point{
int x_, y_;
};
template <std::size_t I, std::size_t N>
constexpr typename std::enable_if<I + 1 == N, point>::type barysum(std::array<point, N> const &points) {
return point{ points[I].x_, points[I].y_ };
}
template <std::size_t I, std::size_t N>
constexpr typename std::enable_if<I + 1 != N, point>::type barysum(std::array<point, N> const &points) {
return point{ points[I].x_ + barysum<I+1>(points).x_, points[I].y_ + barysum<I+1>(points).y_ };
}
template <std::size_t N>
constexpr point barycentre_impl(std::array<point, N> const points_array) {
return point{ barysum<0>(points_array).x_ / static_cast<int>(N), barysum<0>(points_array).y_ / static_cast<int>(N)};
}
template <class... Pts>
constexpr point barycentre(Pts... pts) {
return barycentre_impl<sizeof...(Pts)>( {{pts...}} );
}
int main() {
constexpr auto bary = barycentre(point{0, 0}, point{2, 2});
std::cout << "(" << bary.x_ << ", " << bary.y_ << ")\n";
}
[live demo]
Initializer list is simpler for taking elements of the same type.
Point average(std::initializer_list<Point> list)
{
int x = 0, y = 0;
for(auto &i: list) {x += i.x_; y += i.y_;}
return {x / list.size(), y / list.size()};
}
Variadic templates is generic on types, so it doesn't fit for your case. It doesn't make your code execute faster in any way, just bloating the binary.

DRY way to construct all elements of an array with the same initializer list?

In C++11, is there a DRY way to construct all elements of an array with some same set of parameters for all elements? (e.g. via a single initializer list?)
For example:
class C {
public:
C() : C(0) {}
C(int x) : m_x{x} {}
int m_x;
};
// This would construct just the first object with a parameter of 1.
// For the second and third object the default ctor will be called.
C ar[3] {1};
// This would work but isn't DRY (in case I know I want all the elements in the array to be initialized with the same value.
C ar2[3] {1, 1, 1};
// This is DRYer but obviously still has repetition.
const int initVal = 1;
C ar3[3] {initVal, initVal, initVal};
I know my goal is easily achievable by using an std::vector. I'm wondering if it's possible with raw arrays as well.
c++14 - a little work will make this work for c++11
#include <iostream>
#include <array>
#include <utility>
class C {
public:
C() : C(0) {}
C(int x) : m_x{x} {}
int m_x;
};
namespace detail {
template<class Type, std::size_t...Is, class...Args>
auto generate_n_with(std::index_sequence<Is...>, const Args&...args)
{
return std::array<Type, sizeof...(Is)> {
{(void(Is), Type { args... })...} // Or replace '{ args... }' with '( args... )'; see in comments below.
};
}
}
template<class Type, std::size_t N, class...Args>
auto generate_n_with(const Args&...args)
{
return detail::generate_n_with<Type>(std::make_index_sequence<N>(), args...);
}
int main()
{
auto a = generate_n_with<C, 3>(1);
for (auto&& c : a)
{
std::cout << c.m_x << std::endl;
}
}
results:
1
1
1
I want to guarantee no copies prior to c++17
The you would need to generate into a vector:
template<class Container, class...Args>
auto emplace_n(Container& c, std::size_t n, Args const&...args)
{
c.reserve(n);
while(n--) {
c.emplace_back(args...);
}
};
used like this:
std::vector<C> v2;
emplace_n(v2, 3, 1);
You can construct a sequence of elements using an std::index_sequence<...> and expand that into the initializers of an array. I don't know of any approach avoiding an auxiliary function, though. Here is an example:
#include <iterator>
#include <algorithm>
#include <iostream>
struct S {
int value;
S(int value): value(value) {}
};
std::ostream& operator<< (std::ostream& out, S const& s) {
return out << s.value;
}
#include <array>
#include <iterator>
#include <algorithm>
#include <iostream>
struct S {
int value;
S(int value): value(value) {}
};
std::ostream& operator<< (std::ostream& out, S const& s) {
return out << s.value;
}
template <typename T, std::size_t... I>
std::array<T, sizeof...(I)> fill_aux(T value, std::index_sequence<I...>)
{
return std::array<T, sizeof...(I)>{ (void(I), value)... };
}
template <std::size_t N, typename T>
std::array<T, N> fill(T value) {
return fill_aux(value, std::make_index_sequence<N>());
}
int main()
{
std::array<S, 10> array = fill<10>(S(17));
std::copy(array.begin(), array.end(), std::ostream_iterator<S>(std::cout, " "));
}
By creating derived class, you can effectively create a new default value. It's a bit hackish, but may be less hackish than other solutions. Here's an example:
class C {
public:
C() : C(0) {}
C(int x) : m_x{x} {}
int m_x;
};
template <int init>
struct CInit : C { CInit() : C(init) {} };
CInit<1> ar2[3];
const int initVal = 1;
CInit<initVal> ar3[3];
Another approach is to wrap your raw array inside a struct with a variadic constructor:
template <size_t n>
struct Array {
C array[n];
template <size_t... seq>
Array(int init,std::index_sequence<seq...>)
: array{(void(seq),init)...}
{
}
Array(int init)
: Array(init,std::make_index_sequence<n>())
{
}
};
const int initVal = 1;
Array<3> ar3_1(initVal);
const C (&ar3)[3] = ar3_1.array;
Building on Richard's answer, it's also possible to define
template<class Type, std::size_t N, class...Args>
auto generate_n_with(const std::array<Type, N>&, const Args&...args)
{
return detail::generate_n_with<Type>(std::make_index_sequence<N>(), args...);
};
Allowing you to enter the array as a parameter to make the code more dry in case you already know the type of the array, e.g.
class D {
public:
D();
std::array<int, 3> m_ar;
};
Allowing
D::D() : m_ar{generate_n_with{m_ar, 5}} {}
Instead of the less DRY
D::D() : m_ar{generate_n_with<int, 3>{5}} {}
P.S. maybe there's an even DRYer way without repeating m_ar twice?

Passing temporary struct as template argument

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
}

Using SFINAE on non static data members?

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.