Exposing union's variables as class member variables [duplicate] - c++

This question already has answers here:
Naming Array Elements, or Struct And Array Within a Union
(4 answers)
Closed 4 years ago.
I've got this code that I just compiled successfully :
template <typename T, unsigned int N>
struct Vector
{
struct Vec1
{
T x;
};
struct Vec2 : public Vec1
{
T y;
};
struct Vec3 : public Vec2
{
T z;
};
struct Vec4 : public Vec3
{
T w;
};
template <unsigned int N>
union Data
{
std::array<T, N> components;
};
template <>
union Data<1>
{
Vec1 vec;
std::array<T, 1> components;
};
template <>
union Data<2>
{
Vec2 vec;
std::array<T, 2> components;
};
template <>
union Data<3>
{
Vec3 vec;
std::array<T, 3> components;
};
template <>
union Data<4>
{
Vec4 vec;
std::array<T, 4> components;
};
Data<N> data;
};
It works as intended, however I would like the struct Vector to expose the data's variables as its own member variables.
Is it possible?
The solution would allow me to do Vector<int, 3> vec; vec.x ...; vec.components[0] ...;
The purpose of the union is to access easily both the vector's components as an array and individually.
Also, if you happen to know a better way to implement the templated union Data specializations, please say so as I find it kinda hard coded. It would be perfect to recursively add variables without having to add the variables of the previous specialization.
For example, I would only need to declare T x once.

I think you need to bring some clarity to your design and the code.
Use of
template <>
union Data<3>
{
T x;
T y;
T z;
std::array<T, 3> components;
};
does not sound right. You need to have {x, y, z} or components, not x, or y, or z, or components. What you need is something along the lines of
template <>
union Data<3>
{
struct
{
T x;
T y;
T z;
} members;
std::array<T, 3> components;
};
Having said that, the cleanest member variable is just
std::array<T, N> components;
As far as the member variables are concerned, Vector can be defined as:
template <typename T, unsigned int N>
struct Vector
{
std::array<T, N> components;
};
If you need to expose the elements of components through x, y, and z-like abstractions, it will be better to add member functions.
template <typename T, unsigned int N>
struct Vector
{
std::array<T, N> components;
T& x()
{
static_assert(N > 0);
return components[0];
}
T& y()
{
static_assert(N > 1);
return components[1];
}
T& z()
{
static_assert(N > 2);
return components[2];
}
};
with the above definition of Vector, the following main function should work.
int main()
{
Vector<int, 1> v1;
v1.x() = 20;
Vector<int, 2> v2;
v2.x() = 20;
v2.y() = 30;
Vector<int, 3> v3;
v3.x() = 20;
v3.y() = 30;
v3.z() = 40;
}
If you use
Vector<int, 2> v2;
v2.z() = 20;
you should get a compile-time error.
You can add the const versions of the above functions to make the member functions work with const objects too.
template <typename T, unsigned int N>
struct Vector
{
std::array<T, N> components;
T& x()
{
static_assert(N > 0);
return components[0];
}
T const& x() const
{
static_assert(N > 0);
return components[0];
}
T& y()
{
static_assert(N > 1);
return components[1];
}
T const& y() const
{
static_assert(N > 1);
return components[1];
}
T& z()
{
static_assert(N > 2);
return components[2];
}
T const& z() const
{
static_assert(N > 2);
return components[2];
}
};

Related

Initialization of class with member array of arrays with parameter pack

The point is to create uniform struct for vertex and for matrix in c++
Pseudocode:
vector<COUNT, T> {
union {
T data[COUNT];
struct { T x, y, z, w; } // if size is 1 -> only x, if 2 -> only x and y ... etc
struct { T u, v; } // same }
}
template<COUNT, VCOUNT, T>
matrix<COUNT, VCOUNT> : vector<COUNT, vector<VCOUNT, T>> {}
And use like this:
void foo() {
v3f pos(0,1,0);
pos.xy /= pos.z;
m4f transform = {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,3.456}}
transform.x.y = transform[3][2];
}
I am too close to make this possible, the only thing I can't reach is initialization of matrix:
v4f v; // v4f typedef of vector<4,float>, m4f is matrix<4,4,float>
v = {0,0,1,0}; //work
m4f mat = {v,v,{},v}; //don't work "excess elements in struct initialization"
m4f mat = {{v,v,{}, v}}; // same
Some implementation details
namespace math::detail
{
template<int SIZE, typename T>
struct vector_base {
T _[SIZE];
};
template<typename T>
struct vector_base<1, T> {
union { T _[1]; T x; };
};
template<typename T>
struct vector_base<2, T> {
union { T _[2]; struct { T x, y; }; struct { T u, v; }; };
};
template<typename T>
struct vector_base<3, T> {
union { T _[3]; struct { T x, y, z; }; };
};
template<typename T>
struct vector_base<4, T> {
union { T _[4]; struct { T x, y, z, w; }; };
};
}
namespace math
{
enum VecI { X, Y, Z, W, U = X, V = Y };
template<int SIZE, typename T>
struct vector : detail::vector_base<SIZE, T>
{
using base = detail::vector_base<SIZE, T>;
vector() = default;
template<typename ... Types,
typename std::enable_if<sizeof...(Types) == SIZE, int>::type = 0>
constexpr vector(Types ... s) : base{static_cast<T>(s)... } {}
// some foos, operator overloading
}
template<int size, int vector_size, typename type>
struct matrix : vector<size, vector<vector_size, type>>
{
// special foos only for matrix
}
}
Vector constructor with parameter pack is not compiling in case of matrix:
error: no matching constructor for initialization of 'vector<3, vector<3, float> >'
return {{{1,0,0}, {0,1,0}, {0,0,1}}};
^~~~~~~~~~~~~~~~~~~~~~~~~~~
/math/vector/base.h:62:15: note: candidate template ignored: requirement 'sizeof...(Types) == 3' was not satisfied [with Types = <>]
constexpr vector(Types ... s) : base{static_cast<T>(s)... } {}
^
Next implementation of matrix at least compile, but need initializing with extra braces {{{},{},{}} and can't inherit some vector properties:
template<int size, int vector_size, typename type>
struct matrix
{
vector _[size];
// foos
}
I am also tried writing constructors for vector_base, for matrix, deduction guides for all of them, nothing works.
How to initialize array of arrays in C++ with automatic deduction (can initialize {5, 0.43, .1f} without errors of type incompatibilities), without implicitly writing types like in m4f mat = {v4f(0,0,1),v4f(0,1,0), v4f(1,0,0)}, and without "excess" nested braces {{{}}}?
My compiler is clang:9.0.0 with c++17, also can use c++2a, or c++20 when it comes.
Answer to your actual question
C++ is just lousy at deducing things from braced lists. It won't look at the lists, notice your constructor is initializing a member with them, and try to fit the braces to that member. Basically, multi-level brace initialization only works if it doesn't have to deduce anything (e.g. a non-template constructor, aggregate initialization, or a varible-length initializer_list)
To get this working for you, give each base type a simple constructor:
template<typename T>
struct vector_base<2, T> {
vector_base(T x, T y) : x(x), y(y) { } // <-- add these to each specialization
union { T _[2]; struct { T x, y; }; struct { T u, v; }; };
};
Then, for the generic derived type, promote the constructor:
template<int SIZE, typename T>
struct vector : detail::vector_base<SIZE, T>
{
using detail::vector_base<SIZE, T>::vector_base;
/* overloads and methods */
};
Now all forms of vector have a non-generic constructor and brace-initialization works as expected.
math::vector<3, math::vector<3, float>> foo {{1,0,0},{0,1,0},{0,0,1}};
Now, about the type punning
There's actually a neat trick for adding array-like access to struct members without breaking strict aliasing here: Overload the [] operator to access a static constexpr array of member-to-pointers to your struct members.
Then there is only one level of indirection between array-like access and member access, in a constant-foldable map. This allows the compiler to, in most cases, produce the same assembly as if you used union punning or a reinterpret_cast to access the member data in a for loop.
Applied to your code:
template <size_t SIZE, typename T>
struct vector_base;
template <typename T>
struct vector_base<1, T> {
constexpr vector_base(T x) : x(x) {}
T x;
constexpr T& operator [](size_t index) { return this->*(vector_base::map[index]); }
constexpr const T& operator [](size_t index) const { return this->*(vector_base::map[index]); }
static constexpr T vector_base::* map[1] = {&vector_base::x};
};
template <typename T>
struct vector_base<2, T> {
constexpr vector_base(T x, T y) : x(x), y(y) {}
union {
T x;
T u;
};
union {
T y;
T v;
};
constexpr T& operator [](size_t index) { return this->*(vector_base::map[index]); }
constexpr const T& operator [](size_t index) const { return this->*(vector_base::map[index]); }
static constexpr T vector_base::* map[2] = {&vector_base::x, &vector_base::y};
};
template <typename T>
struct vector_base<3, T> {
constexpr vector_base(T x, T y, T z) : x(x), y(y), z(z) {}
T x;
T y;
T z;
constexpr T& operator [](size_t index) { return this->*(vector_base::map[index]); }
constexpr const T& operator [](size_t index) const { return this->*(vector_base::map[index]); }
static constexpr T vector_base::* map[3] = {&vector_base::x, &vector_base::y, &vector_base::z};
};
template <typename T>
struct vector_base<4, T> {
constexpr vector_base(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {}
T x;
T y;
T z;
T w;
constexpr T& operator [](size_t index) { return this->*(vector_base::map[index]); }
constexpr const T& operator [](size_t index) const { return this->*(vector_base::map[index]); }
static constexpr T vector_base::* map[4] = {&vector_base::x, &vector_base::y, &vector_base::z, &vector_base::w};
};
Note: the union between x/y and u/v is actually well-formed, even for accessing inactive members
Demo: https://godbolt.org/z/ibpSqB

Make longer std::array accessible as if it's shorter

I am implementing my static multi-dimentional vector class. I am using std::array as the underlying data type.
template <typename T, std::size_t N>
class Vector {
private:
std::array<T, N> data;
};
I want to make my class downwards-compatible, so I am writing this:
template <typename T, std::size_t N>
class Vector : public Vector<T, N-1>{
private:
std::array<T, N> data;
};
template <typename T>
class Vector<T, 0> {};
My goal is that when one instance is used in downwards-compatible mode, its underlying data should be able to be reliably accessed:
template<typename T, std::size_t N>
T& Vector<T, N>::operator[](int i) {
// Do boundary checking here
return this->data[i];
}
void foo(Vector<int, 3>& arg) {
arg[1] = 10;
}
Vector<int, 5> b;
foo(b);
// Now b[1] should be 10
There are two points here:
Vector<T, 5> should be accepted by foo(), Vector<T, 2> should be rejected.
Changes to b[0] through b[2] in foo() should pertain. b[3] and b[4] should not be accessible in foo().
How can I achieve that?
How about a simple read wrapper around std::array<> itself?
template<typename T, std::size_t N>
struct ArrayReader {
public:
// Intentionally implicit.
template<std::size_t SRC_LEN>
ArrayReader(std::array<T, SRC_LEN> const& src)
: data_(src.data()) {
static_assert(SRC_LEN >= N);
}
private:
T const* data_;
};
void foo(ArrayReader<float, 3>);
void bar() {
std::array<float, 4> a;
std::array<float, 2> b;
foo(a);
foo(b); //BOOM!
}
Of course, you can easily substitute std::array for your own type, this is just an example of the principle.
Have array keep the data, and then create additional non-owning class, e.g. array_view that will keep only a pointer. It will have generic constructor that accepts the array, and will have a static_assert to check the sizes.
Here's how I would approach this:
template <class Container, std::size_t size>
struct range_view
{
range_view(Container * p): container(p) { assert(size <= p->size()); }
auto & operator[](std::size_t i) { return (*container)[i]; }
private:
Container * container;
};
Then you simply define foo as:
template <class C>
void foo(range_view<C, 3> c)
{
c[1] = 1;
}
Here's something that is closest to what I think you would need.
Make Vector a viewer/user of the data, not the owner of the data.
#include <array>
template <typename T, std::size_t N, std::size_t I>
class Vector : public Vector<T, N, I-1>
{
public:
Vector(std::array<T, N>& arr) : Vector<T, N, I-1>(arr), arr_(arr) {}
T& operator[](int i) {
return arr_[i];
}
private:
std::array<T, N>& arr_;
};
template <typename T, std::size_t N>
class Vector<T, N, 0ul>
{
public:
Vector(std::array<T, N>& arr) : arr_(arr) {}
private:
std::array<T, N>& arr_;
};
void foo(Vector<int, 5, 3>& arg) {
arg[1] = 10;
// Can't find a way to make this a compile time error.
arg[3] = 10;
}
#include <iostream>
int main()
{
std::array<int, 5> arr;
Vector<int, 5, 5> b(arr);
foo(b);
std::cout << b[1] << std::endl;
}
Here's a demonstration of how to implement the Vector class that you tried in your question. At each level you only store 1 value instead of an array and that way when you compose all your N Arrays together you get space for N values. Of course then calling operator[] gets tricky, which is the meat of what I wanted to demonstrate.
#include <utility>
template <class T, std::size_t N>
struct Array : Array<T, N-1>
{
T & operator[](std::size_t i)
{
return const_cast<T&>((*const_cast<const Array*>(this))[i]);
}
const T & operator[](std::size_t i) const
{
return Get(i, std::make_index_sequence<N>());
}
template <std::size_t i>
const T & Geti() const
{
return static_cast<const Array<T, i+1>&>(*this).GetValue();
}
const T & GetValue() const { return value; }
template <std::size_t ... indices>
const T & Get(std::size_t i, std::integer_sequence<std::size_t, indices...>) const
{
using X = decltype(&Array::Geti<0>);
X getters[] = { &Array::Geti<indices>... };
return (this->*getters[i])();
}
template <std::size_t i, class = typename std::enable_if<(i <= N)>::type>
operator Array<T, i>&() { return (Array<T, i>&)*this; }
private:
T value;
};
template <class T>
struct Array<T, 0>{};
void foo(Array<float, 3> & a) { a[1] = 10; }
int main()
{
Array<float, 10> a;
foo(a);
}

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.

Typecasting from class to integral type

In order to deal with vectors and fixed/dynamic allocation in some linear algebra problems, I built the following classes (which I would prefer not to modify ):
// Traits : n is the size of the vector, if -1 : dynamic allocation
template<typename RealType, int n = -1>
struct ClassAVectorTraits
{
typedef typename LinearAlgebraLibrary::FixedSizeVector<RealType, n> type;
};
template<typename RealType>
struct ClassAVectorTraits<T, -1>
{
typedef typename LinearAlgebraLibrary::DynamicSizeVector<RealType> type;
};
template<typename RealType>
struct ClassAVectorTraits<T, 1>
{
typedef typename RealType type;
};
// Implementation
template<typename RealType, int n = -1>
struct ClassA
{
typedef typename ClassAVectorTraits<RealType, n>::type vector;
void doSomething( const vector& vec )
{
...
}
};
Moreover I have an interface class (which I can modify) :
template<typename RealType, int n = -1>
struct UserClass
{
typedef typename ClassAVectorTraits<RealType, n>::type vector;
ClassA ca;
void doSomething( const vector& vec )
{
ca.doSomething( vec );
}
};
Now I want the user to be able to give STL vectors in input instead of LinearAlgebraLibrary vectors, so I did the following :
// A class to do automatic conversion
template<typename RealType, int n = -1>
struct Field : public ClassAVectorTraits<RealType,n>::type
{
// Constructor NOT explicit
Field( const std::vector<RealType>& vec )
{
// Copy vec into this (a LinearAlgebraLibrary vector)
}
}
template<typename RealType>
struct Field<RealType, 1> { }; // Can't derive from integral type : RealType !
// And to classes to tag the solution
template<typename RealType, int n>
struct USE_LINEAR_ALGEBRA_LIBRARY
{
typedef typename ClassAVectorTraits<RealType,n>::type vector;
};
template<typename RealType, int n>
struct USE_STL
{
typedef typename Field<RealType,n> vector;
};
And finally :
template<typename RealType, int n = -1, class VectorTag = USE_LINEAR_ALGEBRA_LIBRARY<RealType, n> >
struct UserClass
{
typedef typename VectorTag::vector vector;
ClassA ca;
void doSomething( const vector& vec )
{
ca.doSomething( vec );
}
};
And therefore, if we use the USE_STL tag, there is an automatic conversion :
UserClass<double, 2, USE_STL<double, 2> > userClass;
std::vector<double> vecSTL(2, 12.0);
userClass.doSomething( vecSTL ); // vecSTL is converted to LinearAlgebraLibrary::FixedSizeVector<double, 2>
My question : how can I deal with the n=1 case in which the vector type is an integral type. How can I define an implicit conversion between STL vector of double (size == 1) with a double ?
Any suggestions ? Thanks.
You don't have to follow the scheme of generic template when you are about to specialize. Hope this helps:
template<typename RealType>
struct Field<RealType, 1> {
typename ClassAVectorTraits<RealType,1>::type value;
operator ClassAVectorTraits<RealType,1>::type () const { return value; }
Field(const std::vector<RealType>& vec)
{
if (vec.size() == 1) value = vec[0];
else throw runtime_error("wrong size");
}
};