Hi,
A number of 'C-style' languages define a 'swizzle' operator which allows
the permutation of the elements of vector datatypes. This is typically
done with an extra meaning to the . operator.
In C++ we can't overload operator. for fairly good reasons! Is there a
tidier way of emulating this syntax than the following:
#include <iostream>
#define zyxw swizzle(3,2,1,0)
struct ivec4 {
int data[4];
ivec4 swizzle(unsigned char p1, unsigned char p2, unsigned char
p3, unsigned char p4) {
ivec4 ret;
ret.data[0] = data[p1];
ret.data[1] = data[p2];
ret.data[2] = data[p3];
ret.data[3] = data[p4];
return ret;
}
};
int main() {
ivec4 f1, f2;
f1.data[0] = -666;
f2 = f1.zyxw;
std::cout << f2.data[3] << std::endl;
return 0;
}
This has some pretty serious problems, because of the macro use (and
generating a macro like this for all possible permutations of larger
vectors is quite sizeable!)
Can anyone suggest a cleaner way to achieve this 'syntax' within the
existing (including proposed C++0x) language?
Well, I don't know what you mean by cleaner but I can get something
working with an approaching syntax with low runtime overhead.
I'll try to have something like the following working:
int main() {
ivec4 f1, f2;
f1.data[0] = -666;
f2 = f1.swizzle.z.y.x.w;
std::cout << f2.data[2] << std::endl; // expect -666
return 0;
}
Here comes the show.
In order to achieve that, I will define a temple operator=() that can
take a type representing the information data+swizzle.
The information content will be the data table (f1.data) and the
swizzle will be represented as a type:
// type representing the swizzle transformation
// index are mapped in parameter
template<int p0, int p1, int p2, int p3>
struct swizzle_spec
{
static const int index0 = p0;
static const int index1 = p1;
static const int index2 = p2;
static const int index3 = p3;
};
Hence the operator:
template<class TSwizzleContainer>
ivec4& operator=(const TSwizzleContainer& rhs)
{
typedef typename TSwizzleContainer::type TSwizzle;
data[0] = rhs.content[TSwizzle::index0];
data[1] = rhs.content[TSwizzle::index1];
data[2] = rhs.content[TSwizzle::index2];
data[3] = rhs.content[TSwizzle::index3];
return *this;
}
Now we must determine a suitable type for this SwizzleContainer that
will effectively store the sequence x.y.z.w or any combination with
the original pointer, without duplication. (Note if ivec4 is a pod,
the same can be achieved with offsetof() without storing the pointer
but it is IMO dangerous design).
To that end, I will use recursive template unions (berk).
// recursive template on depth from 0 to 3
// TSwizzleType is an helper defining the types o x,y,z,w at each
depth
// TContent will store the content of the data
template<class TContent, int depth=0,class TSwizzleType=swizzle_type >
union swizzle_container
{
TContent content;
swizzle_container<TContent,depth+1,typename TSwizzleType::x> x;
swizzle_container<TContent,depth+1,typename TSwizzleType::y> y;
swizzle_container<TContent,depth+1,typename TSwizzleType::z> z;
swizzle_container<TContent,depth+1,typename TSwizzleType::w> w;
typedef TSwizzleType type;
};
// end of recursion
template<class TContent,class TSwizzleType>
union swizzle_container<TContent,4,TSwizzleType>
{
TContent content;
typedef TSwizzleType type;
};
Note that whatever subtype x.y... you have, type gives you the
transformation and content is the same data storage.
Now we must define the type swizzle_type that will give us the
corresponding type for x,y,z,w. Each depth will be basically a
transformation of a swizzle_spec<> at and index (0,1,2,3) of a
corresponding index (0,1,2,3).
To that end I define the transform type operator:
template<int selindex, int selvalue, class TSwizzleSpec>
struct swizzle_select;
Which will be specialized for each index (boost integer typelist could
be a help to avoid this but I won't use them here).
template<int selvalue, class TSwizzleSpec>
struct swizzle_select<0,selvalue,TSwizzleSpec>
{
typedef swizzle_spec<
selvalue,
TSwizzleSpec::index1,
TSwizzleSpec::index2,
TSwizzleSpec::index3
};
template<int selvalue, class TSwizzleSpec>
struct swizzle_select<1,selvalue,TSwizzleSpec>
{
typedef swizzle_spec<
TSwizzleSpec::index0,
selvalue,
TSwizzleSpec::index2,
TSwizzleSpec::index3
};
template<int selvalue, class TSwizzleSpec>
struct swizzle_select<2,selvalue,TSwizzleSpec>
{
typedef swizzle_spec<
TSwizzleSpec::index0,
TSwizzleSpec::index1,
selvalue,
TSwizzleSpec::index3
};
template<int selvalue, class TSwizzleSpec>
struct swizzle_select<3,selvalue,TSwizzleSpec>
{
typedef swizzle_spec<
TSwizzleSpec::index0,
TSwizzleSpec::index1,
TSwizzleSpec::index2,
selvalue
};
Then the only operation left it to define the swizzle type with
recursive templated types:
template<int depth, class TSwizzleSpec>
struct swizzle_elt: TSwizzleSpec
{
typedef swizzle_elt<depth+1,typename swizzle_select<depth,
0,TSwizzleSpec>::type > x;
typedef swizzle_elt<depth+1,typename swizzle_select<depth,
1,TSwizzleSpec>::type > y;
typedef swizzle_elt<depth+1,typename swizzle_select<depth,
2,TSwizzleSpec>::type > z;
typedef swizzle_elt<depth+1,typename swizzle_select<depth,
3,TSwizzleSpec>::type > w;
};
// terminal case
template<class TSwizzleSpec>
struct swizzle_elt<4,TSwizzleSpec>:TSwizzleSpec
{
};
And my ivec4 class becomes:
struct ivec4{
int data[4];
swizzle_container<int*> swizzle;
ivec4():data(){swizzle.content=this->data;}
template<class TSwizzleContainer>
ivec4& operator=(const TSwizzleContainer& rhs)
{
typedef typename TSwizzleContainer::type TSwizzle;
data[0] = rhs.content[TSwizzle::index0];
data[1] = rhs.content[TSwizzle::index1];
data[2] = rhs.content[TSwizzle::index2];
data[3] = rhs.content[TSwizzle::index3];
return *this;
}
};
It is very long and in the wrong order but if you copy/paste in the
right order, you'll get a working example.