How to represent a 3D array with map? - c++

How can I represent a 3D array like
myarray[1000][1000][1000];
this is a large array which gives memory limit exceeded. Every cell will not be used so there is a huge waste of memory. I want to map 3 numbers and the value, Is there any other way to do this?

#include <map>
#include <iostream>
struct MapIndex{
int x, y, z;
MapIndex()
:x(0), y(0), z(0){
}
MapIndex(int x_, int y_, int z_)
:x(x_), y(y_), z(z_){
}
};
bool operator<(const MapIndex &v1, const MapIndex &v2){
if (v1.z > v2.z)
return false;
if (v1.z < v2.z)
return true;
if (v1.y > v2.y)
return false;
if (v1.y < v2.y)
return true;
if (v1.x < v2.x)
return true;
return false;
}
template<typename Val> struct Array3D{
typedef std::map<MapIndex, Val> Data;
Data data;
Val defaultValue;
const Val& getValue(int x, int y, int z) const{
MapIndex index(x, y, z);
Data::const_iterator found = data.find(index);
if (found == data.end())
return defaultValue;
return found->second;
}
void setValue(int x, int y, int z, const Val &val){
data.insert(std::make_pair(MapIndex(x, y, z), val));
}
bool hasValue(int x, int y, int z) const{
Data::const_iterator found = data.find(MapIndex(x, y, z));
return found != data.end();
}
Array3D(const Val& defaultValue_ = Val())
:defaultValue(defaultValue_){
}
};
int main(int argc, char** argv){
Array3D<int> ints;
std::cout << ints.hasValue(0, 1, 2) << std::endl;
std::cout << ints.getValue(0, 1, 2) << std::endl;
ints.setValue(0, 1, 2, 47);
std::cout << ints.hasValue(0, 1, 2) << std::endl;
std::cout << ints.getValue(0, 1, 2) << std::endl;
return 0;
}

Related

C++ keep force driven entities in the window

I have a window application of 1024 width and 768 height and contains a bunch of meteorites, ships and boats. The meteorites freely roam across the window driven by forces.
The forces are: random position, towards/away from boat, towards/away from ship and cohesion, seperation, alignment to other meteorites
I feel like the forces are not fully working since they sometimes move off the screen and move with inverted velocity eg: they are roaming from top right straight to top left and when reached go straight to bottom left.
Are my calculations correct or did I mess up something at the forces?
Meteorite header:
#include <chrono>
#include <cmath>
#include <array>
#include <random>
#include <algorithm>
using scalar = float;
template <typename Scalar> class basic_vector2d {
public:
constexpr basic_vector2d() noexcept = default;
constexpr basic_vector2d(Scalar x, Scalar y) noexcept : x_{ x }, y_{ y } {}
constexpr Scalar x() const noexcept { return x_; }
constexpr void x(Scalar newX) noexcept { x_ = newX; }
constexpr Scalar y() const noexcept { return y_; }
constexpr void y(Scalar newY) noexcept { y_ = newY; }
constexpr bool operator==(basic_vector2d other) const noexcept {
return x_ == other.x_ && y_ == other.y_;
}
constexpr bool operator!=(basic_vector2d other) const noexcept {
return x_ != other.x_ || y_ != other.y_;
}
constexpr basic_vector2d& operator+=(basic_vector2d other) noexcept {
x_ += other.x_;
y_ += other.y_;
return *this;
}
constexpr basic_vector2d& operator-=(basic_vector2d other) noexcept {
x_ -= other.x_;
y_ -= other.y_;
return *this;
}
constexpr basic_vector2d& operator*=(Scalar s) noexcept {
x_ *= s;
y_ *= s;
return *this;
}
constexpr basic_vector2d& operator/=(Scalar s) noexcept {
x_ /= s;
y_ /= s;
return *this;
}
private:
Scalar x_{};
Scalar y_{};
};
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator-(basic_vector2d<Scalar> a,
basic_vector2d<Scalar> b) {
return { a.x() - b.x(), a.y() - b.y() };
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator+(basic_vector2d<Scalar> a,
basic_vector2d<Scalar> b) {
return { a.x() + b.x(), a.y() + b.y() };
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator*(basic_vector2d<Scalar> v, scalar s) {
return v *= s;
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator*(scalar s, basic_vector2d<Scalar> v) {
return operator*(v, s);
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator/(basic_vector2d<Scalar> v, scalar s) {
return v /= s;
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator/(scalar s, basic_vector2d<Scalar> v) {
return operator/(v, s);
}
template <typename Scalar>
constexpr scalar dot(basic_vector2d<Scalar> a, basic_vector2d<Scalar> b) {
return a.x() * b.x() + a.y() * b.y();
}
template <typename Scalar> constexpr auto norm(basic_vector2d<Scalar> p) {
return std::sqrt(dot(p, p));
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> normalize(basic_vector2d<Scalar> p) {
auto ls = norm(p);
return { p.x() / ls, p.y() / ls };
}
using vector2d = basic_vector2d<scalar>;
template <typename T> class basic_size {
public:
constexpr basic_size() noexcept = default;
constexpr basic_size(T width, T height) noexcept
: width_{ width }, height_{ height } {}
constexpr T width() const noexcept { return width_; }
constexpr T height() const noexcept { return height_; }
constexpr void width(T new_width) noexcept { width_ = new_width; }
constexpr void height(T new_height) noexcept { height_ = new_height; }
constexpr basic_size& operator*=(T x) {
width(width() * x);
height(height() * x);
return *this;
}
private:
T width_{};
T height_{};
};
using size = basic_size<scalar>;
template <typename Scalar> class basic_rectangle {
public:
constexpr basic_rectangle(basic_vector2d<Scalar> top_left,
basic_size<Scalar> size)
: top_left_{ top_left }, size_{ size } {}
constexpr basic_vector2d<Scalar> const& top_left() const noexcept {
return top_left_;
}
constexpr basic_size<Scalar> const& size() const noexcept { return size_; }
private:
basic_vector2d<Scalar> top_left_;
basic_size<Scalar> size_;
};
using rectangle = basic_rectangle<scalar>;
inline float to_seconds(std::chrono::nanoseconds dt) {
return std::chrono::duration_cast<std::chrono::duration<float>>(dt).count();
}
class meteorite {
public:
meteorite(int id, vector2d location);
int id;
/*!
* Called every tick
* \param dt the time that has passed since the previous tick
*/
void act(std::chrono::nanoseconds dt);
vector2d location() const { return location_; }
std::vector<vector2d> random_meteorite_locations(std::size_t n);
private:
vector2d velocity;
scalar max_velocity;
vector2d location_;
vector2d acceleration;
void location(vector2d loc) { location_ = loc; }
void random_position_force();
void screen_force(std::chrono::nanoseconds dt);
void move(std::chrono::nanoseconds dt);
void add_force(vector2d force);
void island_avoidance();
};
Meteorite source:
#include "meteorite.h"
meteorite::meteorite(int id, vector2d location) : id(id), velocity{ 0, 0 }, max_velocity(0.15), acceleration{ 0, 0 }, location_(location) {}
void meteorite::act(std::chrono::nanoseconds dt) {
move(dt);
}
void meteorite::move(std::chrono::nanoseconds dt) {
this->location(this->location() + velocity);
random_position_force();
screen_force(dt);
this->velocity += this->acceleration * to_seconds(dt);
// prevent velocity from exceeding max_velocity
float velocity_length = std::sqrt((this->velocity.x() * this->velocity.x()) + (this->velocity.y() * this->velocity.y()));
if (velocity_length >= this->max_velocity) {
this->velocity = normalize(this->velocity) * this->max_velocity;
}
/*directions:
* y -1 up
* y 1 down
*
* x 1 right
* x -1 left
*/
// reset acceleration to 0 for the next set of forces to be applied
this->acceleration = vector2d(0, 0);
}
// add force propeling meteorite to a random position
void meteorite::random_position_force() {
float x = (rand() % 100 - 50);
float y = (rand() % 100 - 50);
add_force(this->velocity + vector2d((x / 5), (y / 5)));
}
void meteorite::add_force(vector2d force) {
this->acceleration += force;
}
void meteorite::screen_force(std::chrono::nanoseconds dt)
{
auto new_position = this->location() + (this->velocity + (this->acceleration * to_seconds(dt)));
auto height = 1068 - 32;
auto width = 724 - 32;
if (new_position.x() <= 32) {
vector2d screen_vector = vector2d(0, 0);
if (this->acceleration.x() < 0)
{
screen_vector = vector2d(-this->acceleration.x() * 2, 0);
}
add_force(screen_vector);
}
else if (new_position.x() >= width)
{
vector2d screen_vector = vector2d(0, 0);
if (this->acceleration.x() > 0)
{
screen_vector = vector2d(-this->acceleration.x() * 2, 0);
}
add_force(screen_vector);
}
if (new_position.y() <= 32) {
vector2d screen_vector = vector2d(0, 0);
if (this->acceleration.y() < 0)
{
screen_vector = vector2d(0, -this->acceleration.y() * 2);
}
add_force(screen_vector);
}
else if (new_position.y() >= height)
{
vector2d screen_vector = vector2d(0, 0);
if (this->acceleration.y() > 0)
{
screen_vector = vector2d(0, -this->acceleration.y() * 2);
}
add_force(screen_vector);
}
}
std::vector<vector2d> meteorite::random_meteorite_locations(std::size_t n) {
// from 0x2 to 13x17 = 195
// from 13x0 to 28x9 = 135
// from 20x9 to 32x19 = 120
// from 6x17 to 25x24 = 133
// sum = 583
std::random_device rd{};
std::default_random_engine re{ rd() };
std::uniform_int_distribution<> id{ 0, 583 };
std::uniform_real_distribution<scalar> sd{ 0, 1 };
auto rv = [&](rectangle const& r) {
return r.top_left() + vector2d{ r.size().width() * sd(re),
r.size().height() * sd(re) };
};
std::array<rectangle, 4> rects{
rectangle{vector2d{0.1f, 2}, size{13, 15}},
rectangle{vector2d{13.f, 0.1f}, size{15, 9}},
rectangle{vector2d{20, 9}, size{12, 10}},
rectangle{vector2d{6, 17}, size{17, 6}} };
auto to_index = [](int i) -> std::size_t {
if (i < 195)
return 0;
else if (i < 330)
return 1;
else if (i < 450)
return 2;
else
return 3;
};
std::vector<vector2d> result(n);
std::generate_n(result.begin(), result.size(), [&] {
auto val = id(re);
auto index = to_index(val);
auto rect = rects[index];
return 32 * rv(rect);
});
return result;
}
Main.cpp
#include <iostream>
#include "meteorite.h"
int main()
{
meteorite m = meteorite{ 0, {} };
std::vector<meteorite*> meteorites;
std::vector<vector2d> locations = m.random_meteorite_locations(1);
int i = 1;
for (auto& loc : locations) {
meteorites.push_back(new meteorite(i, loc));
}
auto t_prev = std::chrono::high_resolution_clock::now();
while (true) {
auto t_current = std::chrono::high_resolution_clock::now();
std::chrono::nanoseconds dt = std::chrono::nanoseconds(200);
t_prev = t_current;
for (auto& m : meteorites) {
m->act(dt);
std::cout << m->location().x() << " " << m->location().y() << "\n";
}
}
for (auto& m : meteorites) {
delete m;
}
}
You're computing the new position incorrectly in both places, move() and screen_force(). You're doing s = s0 + (v + a * t), but you should be doing s = s0 + v * t + (a * t^2) / 2.
Here's a working example:
http://cpp.sh/9uu3w

How to check type being assigned to result of call operator?

I'm trying to make a simple matrix class
Relevant parts of "mymat.h"
#ifndef _MYMAT_H_GUARD_
#define _MYMAT_H_GUARD_
#include <iostream>
constexpr auto MYMAT_ERR_UNEXPECTED_TYPE = "Error, unexpected type!";
constexpr auto MYMAT_ERR_CODE_UNEXPECTED_TYPE = 0;
constexpr auto MYMAT_ERR_OUT_OF_BOUND = "Error, out of bound!";
constexpr auto MYMAT_ERR_CODE_OUT_OF_BOUND = 0;
template <typename T>
class MYMAT{
public:
T* data;
int x, y;
public:
MYMAT(int x, int y);
~MYMAT();
template <typename C>
void set(int x, int y, C val);
template<typename C>
bool checkType(C val) const;
void print_mat();
public:
T& operator ()(int x, int y);
private:
bool inRange(int x, int y);
};
#endif // !_MYMAT_H_GUARD_
template<typename T>
inline MYMAT<T>::MYMAT(int x, int y){
this->data = new T[x * y]();
this->x = x;
this->y = y;
}
template<typename T>
inline MYMAT<T>::~MYMAT(){
delete this->data;
}
template<typename T>
inline void MYMAT<T>::print_mat(){
int x, y;
for (y = 0; y < this->y; y++)
{
for (x = 0; x < this->x; x++)
{
std::cout << this->data[y * this->x + x] << ' ';
}
std::cout << std::endl;
}
std::cout << std::endl;
}
template<typename T>
inline bool MYMAT<T>::inRange(int x, int y){
return !((x < 1) && (x > this->x) && (y < 1) && (y > this->y));
}
template<typename T>
template<typename C>
inline void MYMAT<T>::set(int x, int y, C val){
if (this->checkType(val)) {
if (this->inRange(x, y)) {
this->data[(y - 1) * this->x + (x - 1)] = val;
}
else {
std::cout << MYMAT_ERR_OUT_OF_BOUND;
exit(MYMAT_ERR_CODE_OUT_OF_BOUND);
}
}
else {
std::cout << MYMAT_ERR_UNEXPECTED_TYPE;
exit(MYMAT_ERR_CODE_UNEXPECTED_TYPE);
}
}
template<typename T>
inline T& MYMAT<T>::operator()(int x, int y)
{
return this->data[this->x * (y - 1) + (x - 1)];
}
template<typename T>
template<typename C>
inline bool MYMAT<T>::checkType(C val) const
{
return std::is_same_v<T, C>;
}
And below is how I call matrix and use set method
#include <iostream>
#include "mymat.h"
int main()
{
MYMAT<int> m(3, 3);
m.set(2, 2, 500);
m.print_mat();
m.set(2, 2, 500.0);
m.print_mat();
}
It prints
0 0 0
0 500 0
0 0 0
Error, unexpected type!
But when call operator is used:
#include <iostream>
#include "mymat.h"
int main()
{
MYMAT<int> m(3, 3);
m(2, 2) = 500;
m.print_mat();
m(2, 2) = 500.0;
m.print_mat();
}
It prints:
0 0 0
0 500 0
0 0 0
0 0 0
0 500 0
0 0 0
As you see, the value is casted from double to int.
How can I apply the condition in set() for call operator?
To achieve what you want:
m(2, 2) = 500.0; // do custom checks for conversions from
// right hand side to left hand side
returning a T& from operator() is not going to work, because you have no control over implicit conversions to T. In this case, you can't prevent the conversion from double to int.
Instead, you can return a type from operator() that you write yourself, so you have all the control you need over implicit conversions. This type needs to hold onto the information on the left hand side, which is the this pointer of m, and the arguments to operator(). It only needs to support operator= to check for implicit conversions from the right hand side:
private:
struct Wrapper
{
MYMAT *t; // holds onto the this pointer
int x, y;
template <typename C>
void operator=(C val)
{
t->set(x, y, val); // uses MYMAT::set to do the conversion checking
}
};
Now you can declare your operator() like this:
public:
Wrapper operator ()(int x, int y);
and define it like this:
template<typename T>
inline auto MYMAT<T>::operator()(int x, int y) -> Wrapper
{
return {this, x, y};
}
Here's a demo.

Create a sum of two Array instances

The essence of what I want to do is to take two instances of Vector2D and create a third vector that is to be returned and made into the third instance. The problem I am facing is that I am not entirely sure on how to go ahead in doing so. I have tried to find the syntax for sending in instances, if there is such a one, but I have not managed to find anything useful in any of my books.
#include<iostream>
#include<string>
#include<array>
using namespace std;
class vector2D
{
public:
array<float, 2> get()
{
return xy_coord;
}
void set(float x, float y)
{
xy_coord[0] = x;
xy_coord[1] = y;
}
array<float, 2> vectorAdd(a, b)
{
array<float, 2> c;
for (int i = 0; i < 2; i++)
{
c[i] = a[i] + b[i];
}
return c;
}
private:
array<float, 2> xy_coord;
};
int main()
{
string y;
vector2D a, b, c;
array<float, 2> temp;
a.set(2.0, 3.0);
b.set(4.0, 5.0);
temp = c.vectorAdd(a, b);
c.set(temp[0], temp[1]);
getline(cin, y);
}
The idea is to send in the instances a and b to vectorAdd and sum them up and then set c equal to the returned value (I am sure there is a better way to write the code in the main(), but I am not sure how). In short, what would a and b need to be defined as to make this work, assuming it can work at all.
Maybe you could do something like this instead, so you don't have to pass array around:
#include <iostream>
class Vector2D
{
private:
double _x;
double _y;
public:
Vector2D() = delete;
Vector2D(double x, double y) : _x(x), _y(y) {}
double X() const { return _x; }
double Y() const { return _y; }
Vector2D operator+(Vector2D const &v) const
{
return Vector2D(X() + v.X(), Y() + v.Y());
}
};
int main()
{
Vector2D v1(10.0, 20.0);
Vector2D v2(100.0, 200.0);
Vector2D v3 = v1 + v2;
std::cout << v3.X() << " " << v3.Y();
return 0;
}
Prints:
110 220
Do you need to use array<float, 2>? Have you thought of using pair<float, float>?
A lot (all?) of the operations that you have in your Vector2D class come for free with Pair<>.
Then you just create operator+ as others have suggested.
#include <iostream>
#include <utility>
using namespace std;
using Coord = pair<float, float>;
template <typename L, typename R>
Coord operator+(const L& x, const R& y) { return std::make_pair(x.first + y.first, x.second + y.second); }
int main()
{
Coord a { 5.0f, 6.0f };
Coord b { 7.0f, 9.0f };
Coord c = a + b;
std::cout.precision(5);
std::cout << "c= (" << std::fixed << c.first << ", " << c.second << ")" << std::endl;
return 0;
}

std::map erase wrong element

When I try to erase elements of my map, it seems to be an another element who is erased instead.
I thought it was a bad override of operator< but I don't see the problem.
inline bool operator<(const Etat &et){
if (et.x < this->x){
return false;
}
else if (et.x > this->x){
return true;
}
if (et.y < this->y){
return false;
}
else if (et.y > this->y){
return true;
}
if (et.ry < this->ry){
return false;
}
else if (et.ry > this->ry){
return true;
}
};
Etat constructor :
Etat(x, y, ry, useless, useless);
and the map :
std::map< Etat, double > map;
//The 2 last parameters of Etat are useless
map.insert(std::pair< Etat, double >(Etat(2, 2, 2, 0, 0), 0.0));
//map.size() = 1
Etat e (0, 5 ,3, 0, 0);
map.erase(e);
//map.size() = 0
//Etat(2, 2, 2) is gone
I don't have the problem for all instance of Etat in the map, just this case on and some others.
Test it at home :
#ifndef Etat_H
#define Etat_H
#include<iostream>
class Etat
{
public:
Etat(const int x, const int y, const int ry, const double vx, const double vy)
{
this->x = x;
this->y = y;
this->ry = ry;
this->vx = vx;
this->vy = vy;
};
Etat(){};
inline bool operator==(const Etat& et){
if (et.x == this->x && et.y == this->y && et.ry == this->ry && et.vx == this->vx && et.vy == this->vy){
return true;
}
return false;
};
inline bool operator!=(const Etat& et){
if (*this == et){
return false;
}
return true;
};
inline bool operator<(const Etat &et){
if (et.x < this->x){
return false;
}
else if (et.x > this->x){
return true;
}
if (et.y < this->y){
return false;
}
else if (et.y > this->y){
return true;
}
if (et.ry < this->ry){
return false;
}
else if (et.ry > this->ry){
return true;
}
};
inline bool operator>(const Etat& et){
if (*this < et){
return false;
}
return true;
};
inline const int getX() const {
return this->x;
};
inline const int getY() const {
return this->y;
};
inline const int getRY() const {
return this->ry;
};
private:
int x, y;
int ry;
double vx, vy;
};
#endif // !Etat_H
The little piece of code can reproduce the problem :
std::map< Etat, double > map;
map.insert(std::pair< Etat, double >(Etat(2, 2, 2, 0, 0), 0.0));
Etat e (0, 5 ,3, 0, 0);
map.erase(e);
To use your class as a key in map, you must define when your elements are equal. So define comparator class:
struct EtatCompare {
bool operator() (const Etat& e1, const Etat& e2) const {
return e1.x != e2.x || e1.y != e2.y || e1.ry != e2.ry || e1.vx != e2.vx || e1.vy != e2.vy; //TODO: fill all the needed conditions here
}
};
And use it creating map:
std::map< Etat, double, EtatCompare > map;
Not all paths of your operator< return a value. Specifically, when et == *this.
If you are using C++11 or later, use std::tie() instead:
inline bool operator<(const Etat &rhs) const
{
return std::tie(x, y, ry) < std::tie(rhs.x, rhs.y, rhs.ry);
}
You can do the same thing for your operator== and operator> implementations:
bool operator==(const Etat &rhs) const
{
return std::tie(x, y, ry) == std::tie(rhs.x, rhs.y, rhs.ry);
}
bool operator>(const Etat &rhs) const
{
return std::tie(x, y, ry) > std::tie(rhs.x, rhs.y, rhs.ry);
}
Your missing an
else {
throw "no matching if"; // alternatively return either true or false.
}
Your compiler should give an warning that there is a case where there is no return.

Define or bind member function to 'variable like' keyword. Execution without parentheses

Is there a way to bind member function to something like member variable?
Let's say i have simple vector struct:
struct Vec3 {
int x, y, z;
Vec2 xy() const { return Vec2(x, y); }
Vec2 xz() const { return Vec2(x, z); }
Vec2 yz() const { return Vec2(y, z); }
}
Now i can use it like:
Vec3 t = { 5, 3, 2 };
Vec2 s = t.xy() + t.yz();
But is there a way i could use it like:
Vec3 t = { 5, 3, 2 };
Vec2 s = t.xy; // this here ? execute function without '()'.
While C++ does not offer properties by default, you can implement them pretty easily by yourself.
Here is a simplistic approach:
#include <functional>
template<typename T>
struct property
{
public:
typedef std::function<T()> getter;
typedef std::function<void(T)> setter;
public:
property(getter get, setter set)
: get_(get)
, set_(set)
{ }
operator T() const { return get_(); }
property& operator=(T x) { set_(x); return *this; }
private:
getter get_;
setter set_;
};
We can now rewrite your Vec3 class using these 'properties':
class Vec3
{
public:
Vec3(int vx, int vy, int vz)
: x(std::bind(&Vec3::get_x, this), std::bind(&Vec3::set_x, this, std::placeholders::_1))
, y(std::bind(&Vec3::get_y, this), std::bind(&Vec3::set_y, this, std::placeholders::_1))
, z(std::bind(&Vec3::get_z, this), std::bind(&Vec3::set_z, this, std::placeholders::_1))
, xy(std::bind(&Vec3::get_xy, this), std::bind(&Vec3::set_xy, this, std::placeholders::_1))
, xz(std::bind(&Vec3::get_xz, this), std::bind(&Vec3::set_xz, this, std::placeholders::_1))
, yz(std::bind(&Vec3::get_yz, this), std::bind(&Vec3::set_yz, this, std::placeholders::_1))
, x_(vx)
, y_(vy)
, z_(vz)
{ }
property<int> x;
property<int> y;
property<int> z;
property<Vec2> xy;
property<Vec2> xz;
property<Vec2> yz;
protected:
int get_x() { return x_; }
void set_x(int x) { x_ = x; }
int get_y() { return y_; }
void set_y(int y) { y_ = y; }
int get_z() { return z_; }
void set_z(int z) { z_ = z; }
Vec2 get_xy() { return { x_, y_ }; }
void set_xy(Vec2 xy) { x_ = xy.x; y_ = xy.y; }
Vec2 get_xz() { return { x_, z_ }; }
void set_xz(Vec2 xz) { x_ = xz.x; z_ = xz.y; }
Vec2 get_yz() { return { y_, z_ }; }
void set_yz(Vec2 yz) { y_ = yz.x; z_ = yz.y; }
private:
int x_, y_, z_;
};
Which can be used like this:
std::ostream& operator<<(std::ostream& out, const Vec2& v2)
{
out << '[' << v2.x << ", " << v2.y << ']';
return out;
}
std::ostream& operator<<(std::ostream& out, const Vec3& v3)
{
out << '[' << v3.x << ", " << v3.y << ", " << v3.z << ']';
return out;
}
int main(int argc, char** argv)
{
Vec3 v3 { 2, 0, 1 };
std::cout << v3 << std::endl;
v3.y = 3;
std::cout << v3.xy << std::endl;
std::cout << v3.xz << std::endl;
std::cout << v3.yz << std::endl;
return 0;
}
As you can see, what you are asking is possible, it just requires a lot of code.
See the live example on ideone
You can do it by adding helper structs with user-defined convertion functions to Vec2, each containing a reference to the parent (Vec3) in order to have access to its members.
A working example (Visual Studio 2015 update 3):
#include <iostream>
struct Vec2 {
int x, y;
};
struct Vec3 {
int x, y, z;
struct XY {
Vec3& outer;
XY(Vec3& _outer) : outer {_outer} {};
operator Vec2() { return Vec2 {outer.x, outer.y}; };
} xy;
struct XZ {
Vec3& outer;
XZ(Vec3& _outer) : outer {_outer} {};
operator Vec2() { return Vec2 {outer.x, outer.z}; };
} xz;
struct YZ {
Vec3& outer;
YZ(Vec3& _outer) : outer {_outer} {};
operator Vec2() { return Vec2 {outer.y, outer.z}; };
} yz;
Vec3(int _x, int _y, int _z) :
xy {*this}, xz {*this}, yz {*this},
x {_x}, y {_y}, z {_z} {};
};
int main() {
Vec3 t {5,3,2};
Vec2 xy = t.xy; // look, mom, no parentheses!
Vec2 xz = t.xz;
Vec2 yz = t.yz;
std::cout << xy.x << ", " << xy.y << std::endl;
std::cout << xz.x << ", " << xz.y << std::endl;
std::cout << yz.x << ", " << yz.y << std::endl;
}
Output:
5, 3
5, 2
3, 2