Jump to content
Sign in to follow this  
  • entries
    195
  • comments
    198
  • views
    104256

Bad code here

Sign in to follow this  
SiCrane

361 views

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 {
public:
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  


3 Comments


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.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!