I hate coding
Sometimes writing software drives me absolutely nuts. Why couldn't I have specialized into something cooler, like music?
When I'm writing a song, I can usually intuitivley "tell" why something is wrong. That chord sounds weird, that snare isn't "punchy" enough, or that melody isn't resolving.
Unless the tune is truly unsalvageable crap, there's (usually) a light at the end of the tunnel.
With software, some problems feel more akin to being burried in a small dark hole. This dark hole was depressing me, so I fired off some code I'm 90% sure won't compile and ran off to write this blog post instead.
After years of frustration, I've come to appreciate programming languages and tools that like to "get out of my way" and just let-me-do-the-thing-I-want-to-do. On the audio side, FL Studio is a great example of this.
I can have an idea for a song, open FL, and within a couple minutes have the entire thing transcribed. The way the program is laid out facilitates this flow state, and as such
is incredibly fun to play around with and work in. C++ is the opposite of that, it takes me ages to get into a flow state. If/when I eventually do, it only takes 10 minutes for me to piss off the compiler.
And sometimes you need that. There are languages out there that let you do "anything" and get away way with stupid bullshit (Javascript, hello!). Granted, you need SOME sort of structure.
Going back to FL Studio, the flipside of it's "do anything" attitude has led to me developing a workflow in that program that terrifies everyone I show it to (sorry Henry).
That said, nobody who listens to my songs has to know how I have my mixers labeled. weezer's White Album wasn't improved by having the cleanest audio clip arrangement in Pro Tools!!
Anyways, the point I'm circling is that there's a happy middle ground where your tools give you enough structure to be productive, but not so much as to get in your way.
That's part of why I love C# so much. It gives you (arguably) just enough fine grained control over how the internals of your software works, while also making it easy to get into a flow state while writing code.
I've spent most of my day wrestling with my C++ homework. How many triangles does a graph have? How do you count them using a multithreaded program? Answer: the graph can get fucked!! C++ has so many tiny gotchas, so many easy mistakes to make; god forbid you make an error using anything in the standard library and its glorious Templates.
Right, so a lot of modern C++ is built around the concept of "templating". Templating is, in theory, very cool.
Without diving too deep into the boring details, its the reason you can write one generic un-typed List data structure, and
have it work across any data type, from primitives (int, bool, floats) to classes (Player, Camera, HotDog).
You can think of it as operating similar to a fancy version of find and replace. Take the following code, for example:
/* C-esque PSEUDOCODE!! */
// This instantiation of a list of HotDogs...
List<HotDog> dogCollection;
// ...will at compile time, generate a whole class.
class List_HotDog {
HotDog add(HotDog* item);
HotDog remove(HotDog* item);
int size();
}
NB: Syntax highlighting courtesy of Prismjs, and Gabe (who told me about Prism)!
Which might seem fine at first glance. But, lets say you make a typo somewhere. Accidentally substitute `HerfbDug` for `HotDog` in your code. Rewriting this hypothetical as a block of actual C++ code.
#include <iostream>
#include <vector>
class HotDog {
public:
void Eat() {
// yum
}
};
int main() {
// Create a hotdog
HotDog h;
// Whoops! All typos!
std::vector dogs;
// Attempt to iterate through each hot dog
for (HotDog dog : dogs) {
dog.Eat();
}
// Program was a success, self destruct
return 0;
}
Hm, okay. The mistake is pretty obvious. Our compiler should be able to spot that and provide an insightful error message. Let's fire it off and see what happens:
ERROR!
/tmp/zvVEjil2JK/main.cpp: In function 'int main()':
/tmp/zvVEjil2JK/main.cpp:15:17: error: class template argument deduction failed:
15 | std::vector dogs;
| ^~~~
/tmp/zvVEjil2JK/main.cpp:15:17: error: no matching function for call to 'vector()'
In file included from /usr/local/include/c++/14.2.0/vector:66,
from /tmp/zvVEjil2JK/main.cpp:2:
/usr/local/include/c++/14.2.0/bits/stl_vector.h:707:9: note: candidate: 'template<class _Tp, class _Alloc, class _InputIterator, class> vector(_InputIterator, _InputIterator, const _Alloc&)-> std::vector<_Tp, _Alloc>'
707 | vector(_InputIterator __first, _InputIterator __last,
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:707:9: note: candidate expects 2 arguments, 0 provided
/usr/local/include/c++/14.2.0/bits/stl_vector.h:678:7: note: candidate: 'template<class _Tp, class _Alloc> vector(std::initializer_list<_Tp>, const _Alloc&)-> std::vector<_Tp, _Alloc>'
678 | vector(initializer_list<value_type> __l,
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:678:7: note: candidate expects 1 argument, 0 provided
/usr/local/include/c++/14.2.0/bits/stl_vector.h:659:7: note: candidate: 'template<class _Tp, class _Alloc> vector(std::vector<_Tp, _Alloc>&&, std::__type_identity_t<_Alloc>&)-> std::vector<_Tp, _Alloc>'
659 | vector(vector&& __rv, const __type_identity_t<allocator_type>& __m)
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:659:7: note: candidate expects 2 arguments, 0 provided
/usr/local/include/c++/14.2.0/bits/stl_vector.h:640:7: note: candidate: 'template<class _Tp, class _Alloc> vector(std::vector<_Tp, _Alloc>&&, const _Alloc&, std::false_type)-> std::vector<_Tp, _Alloc>'
640 | vector(vector&& __rv, const allocator_type& __m, false_type)
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:640:7: note: candidate expects 3 arguments, 0 provided
/usr/local/include/c++/14.2.0/bits/stl_vector.h:635:7: note: candidate: 'template<class _Tp, class _Alloc> vector(std::vector<_Tp, _Alloc>&&, const _Alloc&, std::true_type)-> std::vector<_Tp, _Alloc>'
635 | vector(vector&& __rv, const allocator_type& __m, true_type) noexcept
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:635:7: note: candidate expects 3 arguments, 0 provided
/usr/local/include/c++/14.2.0/bits/stl_vector.h:624:7: note: candidate: 'template<class _Tp, class _Alloc> vector(const std::vector<_Tp, _Alloc>&, std::__type_identity_t<_Alloc>&)-> std::vector<_Tp, _Alloc>'
624 | vector(const vector& __x, const __type_identity_t<allocator_type>& __a)
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:624:7: note: candidate expects 2 arguments, 0 provided
/usr/local/include/c++/14.2.0/bits/stl_vector.h:620:7: note: candidate: 'template<class _Tp, class _Alloc> vector(std::vector<_Tp, _Alloc>&&)-> std::vector<_Tp, _Alloc>'
620 | vector(vector&&) noexcept = default;
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:620:7: note: candidate expects 1 argument, 0 provided
/usr/local/include/c++/14.2.0/bits/stl_vector.h:601:7: note: candidate: 'template<class _Tp, class _Alloc> vector(const std::vector<_Tp, _Alloc>&)-> std::vector<_Tp, _Alloc>'
601 | vector(const vector& __x)
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:601:7: note: candidate expects 1 argument, 0 provided
/usr/local/include/c++/14.2.0/bits/stl_vector.h:569:7: note: candidate: 'template<class _Tp, class _Alloc> vector(std::size_t, const _Tp&, const _Alloc&)-> std::vector<_Tp, _Alloc>'
569 | vector(size_type __n, const value_type& __value,
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:569:7: note: candidate expects 2 arguments, 0 provided
/usr/local/include/c++/14.2.0/bits/stl_vector.h:556:7: note: candidate: 'template<class _Tp, class _Alloc> vector(std::size_t, const _Alloc&)-> std::vector<_Tp, _Alloc>'
556 | vector(size_type __n, const allocator_type& __a = allocator_type())
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:556:7: note: candidate expects 1 argument, 0 provided
/usr/local/include/c++/14.2.0/bits/stl_vector.h:542:7: note: candidate: 'templateERROR!
<class _Tp, class _Alloc> vector(const _Alloc&)-> std::vector<_Tp, _Alloc>'
542 | vector(const allocator_type& __a) _GLIBCXX_NOEXCEPT
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:542:7: note: candidate expects 1 argument, 0 provided
/usr/local/include/c++/14.2.0/bits/stl_vector.h:531:7: note: candidate: 'template<class _Tp, class _Alloc> vector()-> std::vector<_Tp, _Alloc>'
531 | vector() = default;
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:531:7: note: template argument deduction/substitution failed:
/tmp/zvVEjil2JK/main.cpp:15:17: note: couldn't deduce template parameter '_Tp'
15 | std::vector dogs;
| ^~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:428:11: note: candidate: 'template<class _Tp, class _Alloc> vector(std::vector<_Tp, _Alloc>)-> std::vector<_Tp, _Alloc>'
428 | class vector : protected _Vector_base<_Tp, _Alloc>
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:428:11: note: candidate expects 1 argument, 0 provided
/usr/local/include/c++/14.2.0/bits/stl_vector.h:2033:5: note: candidate: 'template<class _InputIterator, class _ValT, class _Allocator, class, class> std::vector(_InputIterator, _InputIterator, _Allocator)-> vector<_ValT, _Allocator>'
2033 | vector(_InputIterator, _InputIterator, _Allocator = _Allocator())
| ^~~~~~
/usr/local/include/c++/14.2.0/bits/stl_vector.h:2033:5: note: candidate expects 2 arguments, 0 provided
=== Code Exited With Errors ===
Crunch. Not so helpful. Now imagine this level of verbosity and uselessness around a multithreaded programming problem. Sob.
After writing my first draft of this post, I wandered off, brewed a cup of (strong) tea, and had a think. I eventually solved my unreadable template error by reducing it down to a subset of smaller and smaller problems, until I squished it. You can thank a few years of working "professionally" with C++ for this habit. The answer for those curious (nobody), was that I had fucked up trying to pass an object directly to a thread, I had to wrap it in a
std::ref() statement. Of course. How obvious. (please read in Wayne's World voice) Not!!
Well, fortunately, with the compiler bug squished, I can fire my code off to my university's compute cluster to see if my compiled code can count triangles. You know, impostor syndrome is a real bastard. It doesn't matter how many projects you work on, all it takes is one little standard library to make you write a blog post of vengance.
That said, throughout all my time writing software (12 years, who's counting?), there's only been a handful of moments that made me feel like a "real" "professional" programmer. Releasing PSYCRON, landing my first software job at Charm, my first console dev kit arriving in the back of a car; all of these moments have felt undescribably special. Despite how absolutely maddening C++ has been today, submitting my job to a compute cluster, and watch it tick away in the background, trying to identify triangles... As trivial as that seems, is this what real programmers do? Is this what it's like to send programs off to supercomputers and watch them try to decipher the secrets of the universe?
Ah, the magic's faded. Back to work, triangles are't being counted correctly.


