Jump to content
  • Advertisement
Sign in to follow this  
  • entries
  • comments
  • views

Bad code here

Sign in to follow this  


Despite the fact you can't actually create new operators in C++, it's possible to create new "psuedo-operators". Warning: bad code ahead.

Let's say you're a game developer working on your first 3D engine. One of the classes you're going to write is a vector class. It might look something like:

class Vector {
Vector(float x = 0.0f, float y = 0.0f, float z = 0.0f)
: x_(x), y_(y), z_(z) {}

float x_;
float y_;
float z_;

// other stuff excluded

float dot_product(const Vector & lhs, const Vector & rhs) {
return ( (lhs.x_ * rhs.x_) +
(lhs.y_ * rhs.y_) +
(lhs.z_ * rhs.z_) );

Vector cross_product(const Vector & lhs, const Vector & rhs) {
return Vector( (lhs.y_ * rhs.z_ - lhs.z_ * rhs.y_),
(lhs.z_ * rhs.x_ - lhs.x_ * rhs.z_),
(lhs.x_ * rhs.y_ - lhs.y_ * rhs.x_) );

You'd really like to overload operators for the dot and cross product, but everyone tells you it's a honking bad idea. operator * is ambiguous, you can't get at operator . and so on.

Now what if you could turn any identifier into an operator? Let's say the dot product could be represented by dot. So if you had two vectors you could do just a dot b. Of course this generates a syntax error, consecutive identifiers aren't a happy construct in C++. So the operator needs to be made to be something other than an identifier somehow. One method is to bracket it on either side by something. %dot% or or *dot* or ^dot^ or something. Let's go with for the moment since in C++, <> brackets are often used for magic things (like templates and new style casts).

Now, what happens when the parser runs across a b? It turns it into ((a < dot) > b). So if we make dot an object of some sort, we can abuse operator overloading, and the associativity of relational operators to get this to actually compile and do what we want. And since we're already off the deep in terms of good coding practices, might as well throw in a preprocessor macro to automate the whole thing.

template <typename T, typename Op>
struct LtProxy {
LtProxy(T r) : ref_(r) {}
T ref_;

#define MAKE_OPERATOR(name, lhs_type, rhs_type, result_type, worker_function) \
const struct name ## _t {} name; \
LtProxy operator <(lhs_type lhs, name ## _t rhs) { \
return LtProxy(lhs); \
} \
result_type operator>(LtProxy & lhs, rhs_type rhs) { \
return worker_function(lhs.ref_, rhs); \
MAKE_OPERATOR(dot, const Vector &, const Vector &, float, dot_product);
MAKE_OPERATOR(cross, const Vector &, const Vector &, Vector, cross_product);

int main(int, char **) {
Vector a(0, 1, 3);
Vector b(4, 2, 1);

float dot_product = a b;
std::cout << dot_product << std::endl;

Vector cross_product = a b;
std::cout << cross_product.x_ << std::endl;
std::cout << cross_product.y_ << std::endl;
std::cout << cross_product.z_ << std::endl;

return 0;

It's horribly abusive, not too maintainable, and can give operator precedence headaches, but I think it's kind of fun.

I'm tempted to put it into my new unified code library somewhere, but it violates just about every coding standard I've set for the the project. Oh well. It's immortalized here if I ever want to get at it again. :)
Sign in to follow this  


Recommended Comments

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!