Skip to content

Instantly share code, notes, and snippets.

@fef1312
Last active July 27, 2020 14:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fef1312/4eab71a8454c690ce636cc9c6c278520 to your computer and use it in GitHub Desktop.
Save fef1312/4eab71a8454c690ce636cc9c6c278520 to your computer and use it in GitHub Desktop.
A little essay on why C++ is the most transparent language on the planet.

On the Virtue of Transparent Language Design

The motivation for writing this comes from this interview with Bjarne Stroustrup which, even though unfortunately being fake, makes a good point and I found quite entertaining to read.

Consider this example code in C++ (You can assume vec3 is a class):

int main(int argc, char **argv)
{
	vec3 a(1, 2, 3);
	const vec3 b(4, 5, 6);

	vec3 val1;
	auto val2 = a * b;
	auto val3 = b * a;
	auto val4 = a;
	auto val5 = cross_product(a, b);

	return 0;
}

Now, try to answer any of these questions:

  1. What is the content of val1?
  2. What is the type of val2, and what does the * operator do?
  3. Is val3 equal to val2?
  4. Do mutations of a also affect val4?
  5. Does cross_product mutate any of a or b?
  6. What is the type of val5?

Solution

Ok, so here are the implementations of vec3 and cross_product:

/*
 * Spoiler protection; scroll down for the code
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
 
class vec3 {
public:
	std::vector<double> data;

	vec3(double x, double y, double z)
	{
		this->data(3);
		this->data[0] = x;
		this->data[1] = y;
		this->data[2] = z;
	}

	/* Create a unit vector in y direction for whatever reason */
	vec3():
	vec3(0, 1, 0) {};

	/** Copy constructor */
	vec3(vec3 &original)
	{
		/*
		 * I really hope std::vector has a copy constructor, otherwise
		 * we would create a mystical quantum entanglement between this
		 * vec3 and `original`!  On the other hand, this seemingly
		 * simple assignment could take an eternity if the vector were
		 * really long.  Either way, implicit copy constructors are broken.
		 */
		this->data = original.data;
	}

	/** Get the dot product... */
	double operator *(const vec3 &other)
	{
		return std::sqrt(
			this->data[0] * other.data[0]
				+ this->data[1] * other.data[1]
				+ this->data[2] * other.data[2]
		);
	}

	/**
	 * ... except if the instance is `const`.
	 * In that case, multiply value-by-value and return a new `vec3`.
	 * Just because we can.
	 */
	vec3 operator *(const vec3 &other) const
	{
		return vec3(
			this->data[0] * other.data[0],
			this->data[1] * other.data[1],
			this->data[2] * other.data[2]
		);
	}
}

/* Surprise, cross_product is a class and we called its constructor! */
class cross_product {
public:
	vec3 val;

	cross_product(vec3 &v1, const vec3 &v2):
	val(0, 0, 0)
	{
		/* randomly mutate something, just because we can */
		v1.data[std::rand() % 3] *= (double)std::rand() % 2 + 1;

		/* calculate the cross product and store it in this->val */
		this->val.data[0] = v1.data[1] * v2.data[2] - v1.data[2] * v2.data[1];
		this->val.data[1] = v1.data[2] * v2.data[0] - v1.data[0] * v2.data[2];
		this->val.data[2] = v1.data[0] * v2.data[1] - v1.data[1] * v2.data[0];
	}
}

I realize this example is really stupid. However, it also is perfectly valid C++; and if it is possible, it has been done before (i.e. some codebase out there looks pretty much like this). Nobody in their right mind would design a language that allows this.

And yet here we are.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment