C++: safely initialize a class with pointer to a large static array - c++

I have a low-level embedded application, where I have some relatively large const, global, static arrays (lookup tables and such). The compiler (or linker) stores them in Flash memory rather than in RAM, since they are const.
Now, I have a class that needs to be initialized with one such array. It will use the data from that array throughout the lifetime of the class object.
My question is: how can I safely pass a pointer to this global, static array to the object, while preventing mistakenly passing an array with a short lifetime rather than a static one?
For example, consider the naive implementation that doesn't protect from incorrect initialization:
class Interpolator
{
public:
Interpolator(const float table[], int size);
float interpolate(float x); // uses 'table' data member
private:
const float* table;
int size;
};
Interpolator::Interpolator(const float table[], int size) :
table(table), size(size)
{
}
const float table1[] = {1.0, 2.0, 42.0 /* a few thousand more */ };
void main()
{
Interpolator interpolator1(table1, sizeof(table1) / sizeof(float));
float x = interpolator1.interpolate(17.0); // OK
float* table2 = new float[1024];
// ... calculate and fill in values in table2 ...
Interpolator interpolator2(table2, 1024); // how to prevent this usage?
delete[] table2; // incorrectly assume the object created a copy for itself and the delete is safe...
float y = interpolator2.interpolate(17.0); // ERROR, undefined behavior
}
How do I prevent the second instantiation in the example? perhaps through constexpr somehow, or some clever usage of templates...?
Notes:
I realize that the problem here is that my class doesn't conform to RAII. However, under the constraints explained above (use a large static array from Flash memory), I don't see how I can make it conform to RAII.
Copying the data from the static array to a local data member in the object is out of the question - a single array may literally be larger than my whole RAM, which is only tens of kB in size.
I will have multiple instances of the class, multiple static data tables, and several instances of the class may be initialized with the same static data table.
Any idea for a design pattern that enforces safety here?
thanks!

The address of a variable is a constant expression. This means we can use the address of the table as a template argument.
In this way we can build a specific template class for each interpolation table that exists, and no others.
This removes the possibility of creating an interpolator which points to a transient table.
It also has the advantage of requiring less storage since it does not need to maintain pointers to the data.
example:
#include <cstddef>
template<const float* const Table, std::size_t Size>
struct InterpolatorImpl
{
public:
float interpolate(float x)
{
// use Table and Size here as constant expressions
// or write in terms of begin() and end()
return 0;
}
constexpr std::size_t size() const
{
return Size;
}
constexpr const float* begin() const
{
return Table;
}
constexpr const float* end() const
{
return begin() + size();
}
};
const float table1[] = {1.0, 2.0, 42.0 /* a few thousand more */ };
using Interpolator1 = InterpolatorImpl<table1, sizeof(table1) / sizeof(float)>;
const float table2[] = {1.0, 3.0, 5.0 /* a few thousand more */ };
using Interpolator2 = InterpolatorImpl<table2, sizeof(table2) / sizeof(float)>;
int main()
{
Interpolator1 interpolator1;
float x = interpolator1.interpolate(17.0); // OK
float y = Interpolator2().interpolate(21);
}
But what if there were cases where we wanted to conditionally interpolate against one or another table?
In this case we could make the InterpolatorImpl polymorphic, deriving from a common base. We could then provide the common base with a means of performing interpolation based on table details acquired through a private virtual function.
#include <cstddef>
struct Interpolator
{
float interpolate(float x) const
{
return interpolate(getDetails(), x);
}
protected:
struct Details
{
const float* first;
std::size_t length;
};
private:
virtual Details getDetails() const = 0;
static float interpolate(Details details, float x)
{
// do interpolation here
auto begin = details.first;
auto size = details.length;
// ...
return 0;
}
};
template<const float* const Table, std::size_t Size>
struct InterpolatorImpl : Interpolator
{
public:
constexpr std::size_t size() const
{
return Size;
}
constexpr const float* begin() const
{
return Table;
}
constexpr const float* end() const
{
return begin() + size();
}
private:
virtual Details getDetails() const override
{
return { Table, Size };
}
friend auto poly(InterpolatorImpl const& i) -> Interpolator const&
{
return i;
}
};
const float table1[] = {1.0, 2.0, 42.0 /* a few thousand more */ };
using Interpolator1 = InterpolatorImpl<table1, sizeof(table1) / sizeof(float)>;
const float table2[] = {1.0, 3.0, 5.0 /* a few thousand more */ };
using Interpolator2 = InterpolatorImpl<table2, sizeof(table2) / sizeof(float)>;
float doInterpolation(Interpolator const& interp, float x)
{
return interp.interpolate(x);
}
bool choice();
int main()
{
Interpolator1 interpolator1;
Interpolator2 interpolator2;
float x = doInterpolation(choice() ? poly(interpolator1) : poly(interpolator2) , 17.0); // OK
}
But what if my compiler is a little old and does not treat the address of a variable as a constant expression?
Then we need a little hand-rolling for each interpolator:
#include <cstddef>
#include <type_traits>
struct Interpolator
{
float interpolate(float x) const
{
return interpolate(getDetails(), x);
}
protected:
struct Details
{
const float* first;
std::size_t length;
};
private:
virtual Details getDetails() const = 0;
static float interpolate(Details details, float x)
{
// do interpolation here
auto begin = details.first;
auto size = details.length;
// ...
return 0;
}
friend Interpolator const& poly(Interpolator const& self) { return self; }
};
const float table1[] = {1.0, 2.0, 42.0 /* a few thousand more */ };
struct Interpolator1 : Interpolator
{
virtual Details getDetails() const override
{
return {
table1,
std::extent<decltype(table1)>::value
};
}
};
const float table2[] = {1.0, 3.0, 5.0 /* a few thousand more */ };
struct Interpolator2 : Interpolator
{
virtual Details getDetails() const override
{
return {
table2,
std::extent<decltype(table2)>::value
};
}
};
float doInterpolation(Interpolator const& interp, float x)
{
return interp.interpolate(x);
}
bool choice();
int main()
{
Interpolator1 interpolator1;
Interpolator2 interpolator2;
float x = doInterpolation(choice() ? poly(interpolator1) : poly(interpolator2) , 17.0); // OK
}
https://godbolt.org/z/6m2BM8

Related

How to design a class with const, non const member function and it can take over const raw pointer?

I have a class, it can define some operations on a raw pointer.
For example, the class is called Vector.
class Vector
{
public:
explicit Vector(double *ptr_, int size_)
:ptr(ptr_), size(size_)
{
}
// some operation change data in ptr
void notConstOperation()
{
ptr[0]=ptr[0]+1;
}
// some operation do not change data in ptr
double constOperation() const
{
return ptr[0];
}
private:
double *ptr;
int size;
};
And I have a lot of others operations on Vector
int doNotConstOperation(Vector &vec);
int doConstOperation(const Vector &vec);
My problem is, I find I can not create the Vector object from a raw const pointer.
Vector createVectorFromNonConstPtr(const *ptr, int size)
{
// ok.
return Vector(ptr, size);
}
Vector createVectorFromConstPtr(const double *ptr, int size)
{
// can not create, because the args in Vector is not const.
return Vector(ptr, size);
}
I want to use the Vector in the follow way:
// using const ptr
const double *cptr;
const Vector cvec = createVectorFromConstPtr(cptr, 10);
cvec.constOperation();
doConstOperation(cvec);
// using non-const ptr
const double *ptr;
Vector vec = createVectorFromPtr(cptr, 10);
vec.notConstOperation();
doNotConstOperation(vec);
I know splitting the class into Vector and ConstVector can help.
But it leads to some verbose, the const operation will be written twice.
How can I implement that?
Any suggestion is I looking forward. Thanks for your time.
Any suggestion is I looking forward
Inheritance.
class ConstVector {
const double *ptr;
protected:
double *_ptr() {
return const_cast<double *>(ptr);
}
void _set_ptr(double *new_ptr) {
ptr = new_ptr;
}
friend Vector;
};
class Vector : ConstVector {
// some operation change data in ptr
using ConstVector::ConstVector;
void notConstOperation() {
// _ptr() is inherited.
_ptr()[0] = _ptr()[0]+1;
}
}
Template overload.
template<bool isconst>
class Vector {
double *ptr;
Vector(double *ptr) : ptr(ptr) {}
template<int empty = 1>
Vector(const double *ptr,
typename std::enable_if<isconst && empty>::type* = 0
) : ptr(const_cast<double *>(ptr)) {}
typename std::enable_if<!isconst>::type notConstOperation() {
ptr[0] = ptr[0] + 1;
}
};
Runtime tracking.
class Vector {
bool isconst;
double *ptr;
Vector(double *ptr) : ptr(ptr), isconst(0) {}
Vector(const double *ptr) : ptr(const_cast<double *>(ptr)), isconst(1) {}
void notConstOperation() {
assert(!isconst);
....
}
};
Or return a const vector and const cast to construct it:
class Vector
{
public:
Vector(double *ptr, int size)
: ptr(ptr), size(size) {}
void notConstOperation() { .... }
double constOperation() const { ... }
private:
double *ptr;
int size;
};
Vector createVectorFromNonConstPtr(double *ptr, int size)
{
return Vector(ptr, size);
}
const Vector createVectorFromConstPtr(const double *ptr, int size)
{
return Vector(const_cast<double*>(ptr), size);
}
a const double* pointer is a pointer which preserve the value of the variable it is pointing to. The class Vector has (among its purposes) ways to change the value pointed to by this pointer. In short, the const qualifier is a kind of programmer's rule. The rule is : You can't change the value of the variable.
The compiler detect your attempt to manipulate the variable with objects that might change its value and consider it an error.
The problems start as soon as the compiler assigns to your member ptr (which is not aconst double *but adouble *) the value of your variable const double * ptr. This assignment can't happen since you can't assign to a double* the value of a const double*.
You can basically try to compile the following example.
int main() {
const double* ptr = nullptr;
double* error = ptr;
}
a const_cast<double*> could solve this issue.
int main() {
const double* ptr = nullptr;
double* ok = const_cast<double*>(ptr);
}
I personally don't like const_cast with removing const qualifier to the type, i think i would prefer to make two classes : class VectorView and class Vector which inherit the first.

Access data from base class with the correct typecasting of it derived templated class

I have the following scenario:
class ScalarField
{
void* _buffer; //Array for data.
};
And a derived class:
template <typename T>
class ScalarFieldT : public ScalarField
{
ScalarFieldT(int size)
{
_data = new T[size];
_buffer = _data;
}
T& get(int index)
{
return _data[index];
}
T* _data; // Typed array for data
};
Notice that T can assume only basic types such float, int, double and so on.
This is a very old legacy code, so I don't have too much flexibility to adjust it properly doing a better design. What a need to do is to access the data from ScalarField::_buffer with the correct typecasting of it derived class.
Something like this:
void main()
{
int n = TOTAL_SIZE;
ScalarFieldT<int> typedScalarField(n);
ScalarField* scalarField = &typedScalarField;
// This is what I need to do:
int index = ELEMENT_INDEX;
float value = scalarField->method(index); // Get the value from base class correctly converted from int to float, for example.
}
The point is, I only have access to the base class abstraction, but I need to get the value from _buffer converted to another simple data type, such float, int, uchar, etc.
What do you guys recommend?
Thanks in advance!
You can use a type that provides a set of cast operators as a return type for method.
It follows a minimal, working example:
#include<memory>
class ScalarField {
protected:
struct Value {
virtual operator float() const = 0;
virtual operator int() const = 0;
// ...
};
public:
virtual const Value & method(int i) = 0;
protected:
void* buffer;
};
template <typename T>
class ScalarFieldT : public ScalarField {
struct TValue: Value {
TValue(T value): value{value} {}
operator float() const override { return float(value); }
operator int() const override { return int(value); }
// ...
private:
T value;
};
public:
ScalarFieldT(int size) {
data = new T[size];
buffer = data;
}
T& get(int index) {
return data[index];
}
const Value & method(int i) {
std::make_unique<TValue>(data[i]);
}
private:
std::unique_ptr<Value> value;
T* data;
};
int main() {
ScalarFieldT<int> typedScalarField(10);
ScalarField* scalarField = &typedScalarField;
float f = scalarField->method(2);
int i = scalarField->method(5);
}
Hm - please don't throw stones on me for a probably silly answer, but...
Let's assume that T is restricted to numeric data types, e.g. int, long, double, float, and let's further assume that the largest integral value to be stored is about 2^53. Then double could serve as an data exchange data type, to which any other data type can be converted without losing precision.
Then one could define virtual double ScalarField::method(int index) = 0, which is then overloaded accordingly in the typed variants, which do an "up"-cast from actual type T to double.
Unless you are allowed to encapsulate the values in an object (e.g. struture/class Value), the following code using the "use double as data exchange data type" could work:
class ScalarField
{
public:
void* _buffer; //Array for data.
virtual double method(int index) = 0;
};
template <typename T>
class ScalarFieldT : public ScalarField
{
public:
ScalarFieldT(int size)
{
_data = new T[size];
_buffer = _data;
}
virtual double method(int index) { return get(index); }
T& get(int index)
{
return _data[index];
}
T* _data; // Typed array for data
};

Const and non-const c++ constructor

Is there a way in C++03 (or earlier) to write a class that can either store a const or non-const pointer, and handles access appropriately? Take the usage of the non-functional "SometimesConst" class as an example:
class SometimesConst
{
public:
SometimesConst(int * buffer) : buffer(buffer) {} // Needs const qualifier?
int* get() { return buffer; } // Needs const qualifier?
void increment() { counter++; }
private:
int * buffer; // Needs const qualifier?
int counter;
};
void function(int * n, const int * c)
{
// These are both okay
SometimesConst wn(n);
SometimesConst wc(c);
// Reading the value is always allowed
printf("%d %d", wn.get()[0], wc.get()[0]);
// Can increment either object's counter
wn.increment();
wc.increment();
// Can set non-const pointer
wn.get()[0] = 5;
// Should generate a compiler error
wc.get()[0] = 5;
}
Creating a const SometimesConst would not allow modification of the counter property of the object. Can a class be designed that has compile-time const safety for input objects, only if they are passed in as const?
No, not the way you are wanting to use it. The only way to have different behavior at compile time is to have different types. However, you can make that fairly easy to use:
#include <stdio.h>
template <typename T>
class SometimesConst
{
public:
SometimesConst(T* buffer) : buffer(buffer) { }
T* get() { return buffer; }
void increment() { ++counter; }
private:
T *buffer;
int counter;
};
typedef SometimesConst<const int> IsConst;
typedef SometimesConst<int> IsNotConst;
void function(int * n, const int * c)
{
IsNotConst wn(n);
IsConst wc(c);
// Reading the value is always allowed
printf("%d %d", wn.get()[0], wc.get()[0]);
// Can increment either object's counter
wn.increment();
wc.increment();
// Can set non-const pointer
wn.get()[0] = 5;
// Should generate a compiler error
wc.get()[0] = 5;
}
The language already mostly lets you do this with a simple class; with the way const cascades to access to members (combined with mutable for the counter member, which you've indicated should always be mutable), you can provide both read-only and read-write access to a buffer quite easily:
class C
{
public:
C(int* buffer) : buffer(buffer) {}
const int* get() const { return buffer; }
int* get() { return buffer; }
void increment() const { counter++; }
private:
int* buffer;
mutable int counter;
};
void function(int* n)
{
// These are both okay
C wn(n);
const C wc(n);
// Reading the value is always allowed
printf("%d %d", wn.get()[0], wc.get()[0]);
// Can increment either object's counter
wn.increment();
wc.increment();
// Can set non-const pointer
wn.get()[0] = 5;
// Generates a compiler error
wc.get()[0] = 5;
}
What you can't do with this is neatly arrange for the class to be instantiated with either a int* or a const int*; the two lead to totally different semantics for your class, so you should split it into two if you really need that.
Fortunately, templates make this easy:
template <typename T>
class C
{
public:
C(T* buffer) : buffer(buffer) {}
const T* get() const { return buffer; }
T* get() { return buffer; }
void increment() const { counter++; }
private:
T* buffer;
mutable int counter;
};
Now a C<int> is as above, but a C<const int> only provides read-only access to the buffer, even when the C<const int> object itself is not marked as const:
void function(int* n1, const int* n2)
{
C<int> a(n1);
C<const int> b(n2);
const C<int> c(n1);
const C<const int> d(n2);
// Reading the value is always allowed
printf("%d %d %d %d",
a.get()[0], b.get()[0],
c.get()[0], d.get()[0]
);
// Incrementing the counter is always allowed
a.increment();
b.increment();
c.increment();
d.increment();
// Can set non-const pointer
a.get()[0] = 5;
// Cannot set const pointer, or const/non-const pointer behind const object
//b.get()[0] = 5;
//c.get()[0] = 5;
//d.get()[0] = 5;
}
Live demo
I think that there is a design problem if you want to store two different things which must be handled in different ways in one class. But yes, you can do it:
struct X{};
class A
{
public:
A(const X*) { cout << "const" << endl; }
A(X*) { cout << "non const" << endl; }
};
int main()
{
const X x1;
X x2;
A a1(&x1);
A a2(&x2);
}
the output is expected:
const
non const

const * const * member variable initialization c++

I am having trouble with the initialization of this struct (simplified for example)
struct S{ const float * const * const data;};
Basically I have a buffer of buffers of floats, and I use const to ensure someone using S cannot change anything to this member (read only).
My problem is that this is complicated and hard to read to initialize, I would like to use a lambda that return an const S, and so I could initialize members in my lambda by writing the member name : s.data = ptr;
Now this code is complex and I wonder what could be a better solution.
AFAIK, having struct S{float ** data;} a const S would not protect as efficiently the content of the member, I could not modify S::data, but I could modify *S::data.
How should I do ?
Thank you
Why not just remove the last const?
struct S{ const float * const * data;};
That way you can initialize data however you like, and it still can't be used to modify anything it points to.
data itself can be modified, but should that be prevented, it should simply be private.
The recommended way is to add a constructor to S. This allows you to set the value of data in the ctor initializer list.
struct S
{
explicit S(const float *const *const d) : data(d) {}
const float * const * const data;
};
S GetS()
{
float **data = GetData();
return S(data);
}
If you want to restrict who can change S::data after it has been initialized you can box the member variable and use friendship to allow access. This requires encapsulating the data member in an additional struct that provides conversion and assignment operators.
struct Outer
{
struct S
{
private:
struct ConstBox
{
friend Outer;
ConstBox(const ConstBox& other) : data_(other.data_) {}
explicit ConstBox(const float *const *const data) : data_(data) {}
operator const float* const* () const { return data_; }
private:
ConstBox& operator=(const float * const * data)
{
data_ = data;
return *this;
}
const float * const * data_;
};
public:
S() : data(nullptr) {}
explicit S(const float *const *const d) : data(d) {}
ConstBox data;
};
S DoSomething() const
{
S s(nullptr);
auto f = []() -> S
{
S s;
s.data = new float*[10];
return s;
};
return f();
}
};
typedef Outer::S S;
void FailTest()
{
S s;
s.data = nullptr; // <-- fails
float** v1 = s.data; // <-- fails
const float** v1 = s.data; // <-- fails
// These are ok
const float* const* v2 = s.data;
}
#CaptainObvious's answer is the correct one. Write a constructor for S, taking whatever arguments it needs, and use a member initialiser rather than an assignment statement to set 'data'.
With your simplified example I would simply do:
struct S{ float const * const * const data;};
auto create_buffer() -> float const * const * {
float **buf;
/* ... compute buffer contents */
return buf;
}
S s {create_buffer()};
However, in a comment you mention that you have many members and that initializing members based on order is not sufficiently clear.
struct S { const A a; const B b; const C c; };
S s {x,y,z}; // order based, not readable enough.
const members must be initialized as part of the object's construction. You must either specify them somehow in the initializer, or you must set their value in the class, so that they are set at construction time.
Solution 1
One way to pass them during construction, but in a readable fashion is to use a second object to help initialization:
struct S_initializer { A a; B b; C c; }
struct S {
const A a; const B b; const C c;
S(S_initializer &s) : a(s.a), b(s.b), c(s.c) {}
};
S make_S() {
S_initializer s;
s.a = x;
s.b = y;
s.c = z;
return S{s};
}
The above involves some repitition, which you can avoid by just making the initialization helper object a const member of S:
struct S {
const S_initializer m;
S(S_initializer &s) : m{s} {}
};
S make_S() {
S_initializer s;
s.a = x;
s.b = y;
s.c = z;
return S{s};
}
The tradeoff is that now to access the members of S you have to have an extra .m in there:
A a = s.m.a; // versus just s.a;
Solution 2
A second method relies on a compiler extension; Although not standard C++, gcc and clang implement C99 designated initializers in C++. VC++ does not implement this.
S s { .a = x, .b = y, .c = z };

How to access members by both name and as an array?

I have a bunch of vector classes. I have a 2D point vec2_t, a 3D point vec3_t and a 4D point vec4_t (you often want these when you have do graphics; this is graphics code, but the question has a generic C++ flavour).
As it is now, I have vec2_t declaring two members x and y; vec3_t subclasses vec2_t and has a third member z; vec4_t subclasses vec3_t and adds a w member.
I have a lot of near-duplicate code for operator overloading computing things like distances, cross products, multiplication by a matrix and so on.
I've had a few bugs where things have been sliced when I've missed to declare an operator explicitly for the subclass and so on. And the duplication bugs me.
Additionally, I want to access these members as an array too; this would be useful for some OpenGL functions that have array parameters.
I imagine that perhaps with a vec_t<int dimensions> template I can make my vector classes without subclassing. However, this introduces two problems:
How do you have a variable number of members that are also array entries, and ensure they align? I don't want to lose my named members; vec.x is far nicer than vec.d[0] or whatever imo and I'd like to keep it if possible
How do you have lots of the more expensive methods in a CPP source file instead of the header file when you take the templating route?
One approach is this:
struct vec_t {
float data[3];
float& x;
float& y;
float& z;
vec_t(): x(data[0]), y(data[1]), z(data[2]) {}
};
Here, it correctly aliases the array members with names, but the compiler I've tested with (GCC) doesn't seem to work out they are just aliases and so the class size is rather large (for something I might have an array of, and want to pass e.g. as a VBO; so size is a big deal) and how would you template-parameterise it so only the vec4_t had a w member?)
A possible solution (I think).
main.cpp:
#include <iostream>
#include "extern.h"
template <int S>
struct vec_t_impl
{
int values[S];
bool operator>(const vec_t_impl<S>& a_v) const
{
return array_greater_than(values, a_v.values, S);
}
void print() { print_array(values, S); }
virtual ~vec_t_impl() {}
};
struct vec_t2 : vec_t_impl<2>
{
vec_t2() : x(values[0]), y(values[1]) {}
int& x;
int& y;
};
struct vec_t3 : vec_t_impl<3>
{
vec_t3() : x(values[0]), y(values[1]), z(values[2]) {}
int& x;
int& y;
int& z;
};
int main(int a_argc, char** a_argv)
{
vec_t3 a;
a.x = 5;
a.y = 7;
a.z = 20;
vec_t3 b;
b.x = 5;
b.y = 7;
b.z = 15;
a.print();
b.print();
cout << (a > b) << "\n";
return 0;
}
extern.h:
extern bool array_greater_than(const int* a1, const int* a2, const size_t size);
extern void print_array(const int* a1, const size_t size);
extern.cpp:
#include <iostream>
bool array_greater_than(const int* a1, const int* a2, const size_t size)
{
for (size_t i = 0; i < size; i++)
{
if (*(a1 + i) > *(a2 + i))
{
return true;
}
}
return false;
}
void print_array(const int* a1, const size_t size)
{
for (size_t i = 0; i < size; i++)
{
if (i > 0) cout << ", ";
std::cout << *(a1 + i);
}
std::cout << '\n';
}
EDIT:
In an attempt to address the size issue you could change the member reference variables to member functions that return a reference.
struct vec_t2 : vec_t_impl<2>
{
int& x() { return values[0]; }
int& y() { return values[1]; }
};
Downside to this is slightly odd code:
vec_t2 a;
a.x() = 5;
a.y() = 7;
Note: Updated and improved the code a lot.
The following code uses a macro to keep the code clean and partial specialization to provide the members. It relies heavily on inheritence, but that makes it very easy to extend it to arbitary dimensions. It's also intended to be as generic as possible, that's why the underlying type is a template parameter:
// forward declaration, needed for the partial specializations
template<unsigned, class> class vec;
namespace vec_detail{
// actual implementation of the member functions and by_name type
// partial specializations do all the dirty work
template<class Underlying, unsigned Dim, unsigned ActualDim = Dim>
struct by_name_impl;
// ultimate base for convenience
// this allows the macro to work generically
template<class Underlying, unsigned Dim>
struct by_name_impl<Underlying, 0, Dim>
{ struct by_name_type{}; };
// clean code after the macro
// only need to change this if the implementation changes
#define GENERATE_BY_NAME(MEMBER, CUR_DIM) \
template<class Underlying, unsigned Dim> \
struct by_name_impl<Underlying, CUR_DIM, Dim> \
: public by_name_impl<Underlying, CUR_DIM - 1, Dim> \
{ \
private: \
typedef vec<Dim, Underlying> vec_type; \
typedef vec_type& vec_ref; \
typedef vec_type const& vec_cref; \
typedef by_name_impl<Underlying, CUR_DIM - 1, Dim> base; \
protected: \
struct by_name_type : base::by_name_type { Underlying MEMBER; }; \
\
public: \
Underlying& MEMBER(){ \
return static_cast<vec_ref>(*this).member.by_name.MEMBER; \
} \
Underlying const& MEMBER() const{ \
return static_cast<vec_cref>(*this).member.by_name.MEMBER; \
} \
}
GENERATE_BY_NAME(x, 1);
GENERATE_BY_NAME(y, 2);
GENERATE_BY_NAME(z, 3);
GENERATE_BY_NAME(w, 4);
// we don't want no pollution
#undef GENERATE_BY_NAME
} // vec_detail::
template<unsigned Dim, class Underlying = int>
class vec
: public vec_detail::by_name_impl<Underlying, Dim>
{
public:
typedef Underlying underlying_type;
underlying_type& operator[](int idx){
return member.as_array[idx];
}
underlying_type const& operator[](int idx) const{
return member.as_array[idx];
}
private:
typedef vec_detail::by_name_impl<Underlying, Dim> base;
friend struct vec_detail::by_name_impl<Underlying, Dim>;
typedef typename base::by_name_type by_name_type;
union{
by_name_type by_name;
underlying_type as_array[Dim];
} member;
};
Usage:
#include <iostream>
int main(){
typedef vec<4, int> vec4i;
// If this assert triggers, switch to a better compiler
static_assert(sizeof(vec4i) == sizeof(int) * 4, "Crappy compiler!");
vec4i f;
f.w() = 5;
std::cout << f[3] << '\n';
}
Of course you can make the union public, if you want to, but I think accessing the members through the function is better.
Note: The above code compiles cleanly without any warnings on MSVC10, GCC 4.4.5 and Clang 3.1 with -Wall -Wextra (/W4 for MSVC) and -std=c++0x (only for static_assert).
This would be one way to do it:
#include<cstdio>
class vec2_t{
public:
float x, y;
float& operator[](int idx){ return *(&x + idx); }
};
class vec3_t : public vec2_t{
public:
float z;
};
Edit: #aix is right in saying that it's non-standard and could cause problems. Perhaps a more appropriate solution would then be:
class vec3_t{
public:
float x, y, z;
float& operator[](int idx){
static vec3_t v;
static int offsets[] = {
((char*) &(v.x)) - ((char*)&v),
((char*) &(v.y)) - ((char*)&v),
((char*) &(v.z)) - ((char*)&v)};
return *( (float*) ((char*)this+offsets[idx]));
}
};
Edit #2: I have an alternative, where it's possible to only write your operators once, and not end up with a bigger class, like so:
#include <cstdio>
#include <cmath>
template<int k>
struct vec{
};
template<int k>
float abs(vec<k> const&v){
float a = 0;
for (int i=0;i<k;i++)
a += v[i]*v[i];
return sqrt(a);
}
template<int u>
vec<u> operator+(vec<u> const&a, vec<u> const&b){
vec<u> result = a;
result += b;
return result;
}
template<int u>
vec<u>& operator+=(vec<u> &a, vec<u> const&b){
for (int i=0;i<u;i++)
a[i] = a[i] + b[i];
return a;
}
template<int u>
vec<u> operator-(vec<u> const&a, vec<u> const&b){
vec<u> result;
for (int i=0;i<u;i++)
result[i] = a[i] - b[i];
return result;
}
template<>
struct vec<2>{
float x;
float y;
vec(float x=0, float y=0):x(x), y(y){}
float& operator[](int idx){
return idx?y:x;
}
float operator[](int idx) const{
return idx?y:x;
}
};
template<>
struct vec<3>{
float x;
float y;
float z;
vec(float x=0, float y=0,float z=0):x(x), y(y),z(z){}
float& operator[](int idx){
return (idx==2)?z:(idx==1)?y:x;
}
float operator[](int idx) const{
return (idx==2)?z:(idx==1)?y:x;
}
};
There are some problems, though:
1) I don't know how you'd go around defining member functions without having to write them (or at least some sort of stub) more than once.
2) It relies on compiler optimizations. I looked at the output from g++ -O3 -S and it seems that the loop gets unrolled and the ?:s get replaced with the proper field accesses. The question is, would this still be handled properly in a real context, say within an algorithm?
A simple solution might be the best here:
struct Type
{
enum { x, y };
int values[2];
};
Type t;
if (t.values[0] == t.values[Type::x])
cout << "Good";
You can also do something like this:
struct Type
{
int values[2];
int x() const {
return values[0];
}
void x(int i) {
values[0] = i;
}
};
If you do not want to write it yourself, you may check some of the libraries suggested on:
C++ Vector Math and OpenGL compatable
If you use one specific compiler, you may use non standard methods, like packing information or nameless structs (Visual Studio):
union Vec3
{
struct {double x, y, z;};
double v[3];
};
On the other hand, casting several member variables to an array seems dangerous because the compiler may change the class layout.
So the logic solution seems to have one array and using methods to access that array. For example:
template<size_t D>
class Vec
{
private:
float data[D];
public: // Constants
static const size_t num_coords = D;
public: // Coordinate Accessors
float& x() { return data[0]; }
const float& x() const { return data[0]; }
float& y() { static_assert(D>1, "Invalid y()"); return data[1]; }
const float& y() const { static_assert(D>1, "Invalid y()"); return data[1]; }
float& z() { static_assert(D>2, "Invalid z()"); return data[2]; }
const float& z() const { static_assert(D>2, "Invalid z()"); return data[2]; }
public: // Vector accessors
float& operator[](size_t index) {return data[index];}
const float& operator[](size_t index) const {return data[index];}
public: // Constructor
Vec() {
memset(data, 0, sizeof(data));
}
public: // Explicit conversion
template<size_t D2>
explicit Vec(const Vec<D2> &other) {
memset(data, 0, sizeof(data));
memcpy(data, other.data, std::min(D, D2));
}
};
Using the above class, you may access the member array using the [] operator, coordinates using the accessor methods x(), y(), z(). Slicing is prevented using explicit conversion constructors. It disables the use of the accessors for lower dimensions using static_assert. If you are not using C++11, you may use Boost.StaticAssert
You can also templatized your methods. You can use for in order to extend them to N dimensions or use recursive calls. For example, in order to compute the square sum:
template<size_t D>
struct Detail
{
template<size_t C>
static float sqr_sum(const Vec<D> &v) {
return v[C]*v[C] + sqr_sum<C-1>(v);
}
template<>
static float sqr_sum<0>(const Vec<D> &v) {
return v[0]*v[0];
}
};
template<size_t D>
float sqr_sum(const Vec<D> &v) {
return Detail<D>::sqr_sum<D-1>(v);
}
The above code can be used:
int main()
{
Vec<3> a;
a.x() = 2;
a.y() = 3;
std::cout << a[0] << " " << a[1] << std::endl;
std::cout << sqr_sum(a) << std::endl;;
return 0;
}
In order to prevent template bloat, you may code your templated methods on a cpp and instantiated them for D=1, 2, 3, 4.
This is yet another approach. The following works on gcc C++11:
#include <stdio.h>
struct Vec4
{
union
{
float raw[4];
struct {
float x;
float y;
float z;
float w;
};
};
};
int main()
{
Vec4 v = { 1.f, 2.f, 3.f, 4.f };
printf("%.2f, %.2f, %.2f, %.2f\n", v.x, v.y, v.z, v.w);
printf("%.2f, %.2f, %.2f, %.2f\n", v.raw[0], v.raw[1], v.raw[2], v.raw[3]);
return 0;
}