Little program to generate swizzle functions for C++ vector classes

Started by
3 comments, last by mfawcett 15 years, 11 months ago
Hi all - just wanted to share a neat little program I wrote today, maybe some of you will find it useful. It generates swizzle functions for C++ vector classes that allow you to use swizzles similar to those available in high-level shading languages. I was experimenting with templates and macros, but all my solutions either had an ugly syntax or were a bit unsafe (I don't really like macros named 'xy'...). So I decided to use explicit functions like 'v.yxz()' and wrote the program below to generate them. I only conducted some small unit tests with my vector classes so far, but the functions seem to work pretty well. The program should work for most vector classes, regardless of the underlying scalar type. All you have to do is to adjust the strings to match your needs. The program will generate files containing the (inline) function definitions. These files can easily be included in our vector class, e.g.:

class MyVector3
{
public:
	// ...blabla...

	#include "Vector3Swizzles.inc"
};


Here is the program (a bit lengthy but since it's only one source file I think it's ok to post it here):

#include <iostream>
#include <sstream>
#include <fstream>

#define MIN_DIM 2
#define MAX_DIM 4

static const bool writeToStdOut = true;
static const bool writeToFiles = true;

// Files to which the functions for a particular class should be written.
static const char* fileNames[ MAX_DIM ] =
{
	"Dummy",
	"Vector2Swizzles.inc",
	"Vector3Swizzles.inc",
	"Vector4Swizzles.inc"
};

// Names of vector types to generate code for.
// Works also with class templates.
static const char* vectorTypeNames[ MAX_DIM ] =
{
	"Dummy",
	"Vector2",
	"Vector3",
	"Vector4"
};

// These are the strings used to access single components of the vector.
// You can also use operators [] and (), like "(*this)[0]".
static const char* vectorAccessors[ MAX_DIM ] =
{
	"x",
	"y",
	"z",
	"w"
};

// These are the letters used to generate names for the swizzle functions.
static const char* swizzleAccessors[ MAX_DIM ] =
{
	"x",
	"y",
	"z",
	"w"
};

// Optional prefix for the swizzle functions.
static const char* swizzlePrefix = "";


// Writes a definition of a swizzle function to a stream
// stream: ostream to write to
// dim: dimension of result
// p: permutation 
void defineFunction( std::ostream& stream, int dim, int p[MAX_DIM] )
{
	stream << "const " << vectorTypeNames[dim-1] << " ";
	stream << swizzlePrefix;
	for( int i=0; i<dim; ++i )
		stream << swizzleAccessors[ p ];
	stream << "() const" << std::endl;
	
	stream << "{ return " << vectorTypeNames[dim-1] << "( ";
	for( int i=0; i<dim; ++i )
	{
		stream << vectorAccessors[ p ];
		stream << ( ( i < dim-1 ) ? ", " : " ); }" );
	}

	stream << std::endl;
	stream << std::endl;
}

// Helper to enumerate all permuations of dimension 'dim'.
// vecdim is the dimension of the base vector.
void recurse( std::ostream& stream, int vecdim, int dim, int p[MAX_DIM], int recp )
{
	if( recp == dim )
	{
		defineFunction( stream, dim, p );
	}
	else
	{
		for( int i = 0; i < vecdim; ++i )
		{
			recurse( stream, vecdim, dim, p, recp+1 );
			p[ recp ] = ( p[ recp ] + 1 ) % vecdim ;
		}
	}
}

int main( int argc, char** argv )
{
	// for all vector classes...
	for( int vecDim = MIN_DIM; vecDim <= MAX_DIM; ++vecDim )
	{
		std::stringstream ss;
		ss << "// " << fileNames[vecDim-1] << ": Swizzles for " << vectorTypeNames[vecDim-1] << std::endl;
		ss << std::endl;

		// ... generate all valid permutations...
		for( int dim = MIN_DIM; dim <= vecDim; ++dim )
		{
			int p[MAX_DIM] = { 0,0,0,0 };
			recurse( ss, vecDim, dim, p, 0 );
		}

		// ... and write out the function definitions.
		if( writeToStdOut )
		{
			std::cout << ss.str();
			std::cin.get();
		}

		if( writeToFiles )
		{
			std::ofstream ofs( fileNames[vecDim-1] );
			if( ofs.is_open() )
				ofs << ss.str();
			ofs.close();
		}
	}

	return 0;
}

You can also download a VS solution from http://karsten-schwenk.de/downloads/swizzle_generator.zip. EDIT: D'oh! I knew I was missing something. Hope it's correct now. [Edited by - macnihilist on May 1, 2008 10:40:27 AM]
Advertisement
This isn't really core math/physics, so I'm moving to general programming (but leaving a link here).
Graham Rhodes Moderator, Math & Physics forum @ gamedev.net
I programmed something similar, but used the preprocessor to generate all possible permutations. You can download the program from Boost's Vault. It's called swizzle_demo_01.zip.

Boost Vault

Syntax looks like:
vec.zxy();vec.xy();vec.zwyx();// etc...
--Michael Fawcett
Quote:Original post by mfawcett
I programmed something similar, but used the preprocessor to generate all possible permutations. You can download the program from Boost's Vault. It's called swizzle_demo_01.zip.


Thanks for the link. It looks by far more elegant than my solution. Although I have to admit that I haven't completely figured out how you did it yet. :)
(I have no experience with the boost preprocessor library.)
If you actually use ideas from it, let me know. I've only used it in one product so far (no problems). I'd love to make it better.

Basically, if you can wrap your head around BOOST_PP_SEQ_FOR_EACH_PRODUCT you should be able to grasp what I did. The rest of the macros are simply details to get around the quirks of generating code using macros.

Take a look at Boost.Preprocessor for a pretty big hint as to how it's done.
--Michael Fawcett

This topic is closed to new replies.

Advertisement