Trying Out C++ As A Web Developer
I am a web developer by trade. And I know that web development comes in quite a range of qualities - but I do believe myself to be operating on the more sophisticated side of things. My experience lies mostly with programming DBMS backends in PHP using stuff like composer, docker, github/gitlab CI, unit testing, json rest, graphql, aws, mysql + postgres, psr-4 autoloading and all that jazz.
Obviously I was in for a bit of a culture shock, when deciding to try out doing a little bit of C++, just for fun. I hadn’t touched any C++ since back in my university days - and some regards, it’s fundamentally different and requires a different approach. But getting things setup in a way that works for me, was more of a struggle than I expected.
Tutorials
My main intention was to play around with SDL3, just for fun - and most of the tutorials for that, seem to start with “How to setup Visual Studio”. And immediately, my gut feeling tells me, like… maybe not do that? Well, there’s also a tutorial to be found, to setup things as a git submodule, but again, I’m not sure about that.
Also, why are so many tutorials youtube videos? I mean, when actually coding along, having to pause and scrub through a video is so much more inconvenient then having a structured text post that you can scroll through. It’s a rethorical question, I know the answer of course: video has better ad-revenue.
I end up trying to go the visual studio route, and get that up and compiling - but I’m not happy with it. I try manually chaining together the powershell commands for the compiler and linker and get that to work for just SDL3, but already when trying to just add SDL_image to that, it starts feeling too cumbersome already.
Eventually I come across a tutorial on how to set things up using WSL, vcpkg, mingw and cmake - and that’s the first time things start feeling somewhat familiar-ish and comfortable to me. But WSL is a pain in some ways. It does weird stuff at times - like starting to randomly core dump when the heap gets a bit bigger? Also, issues with permissions and mtimes and inode watchers… It’s just not as good as the real thing.
What Worked For Me
To me, the solution finally came with switching to Linux. Since I was now using mingw anyways, there was no reason to stick with Windows. And yes, I do see some irony there - developing for Windows feels more comfortable to do on Linux, than it does on Windows? Well, at least for me, it surely does - but maybe I’m just too much conditioned to developing on Linux. Might be just me.
I probably should have looked at that much sooner - but I was just messing around for fun at home, and my gaming PC still runs Windows. I’m okay with Windows for gaming purposes, I just really don’t like Microsoft’s shenanigans. What tricks you need to employ to be able to keep running a local account is getting increasingly ridiculous. Turning off telemetry settings just doesn’t seem to stick - checking back every other update, will reveal certain things are set to “ON” again. And you keep getting pushed to use Edge, Bing, OneDrive, etc. - in the most annoying way. All Microsoft issues, rather than Windows issues. But I digress.
After dusting off a somewhat older laptop I had lying around, and installing Linux Mint onto that, things finally started falling into place.
The Final Setup
I have very little idea what I am doing, so take everything that follows with a grain of salt - but this is what I ended up using, and feeling comfortable with:
First setup a file structure somewhat like this:
myProject/
┃
┣━ build/
┣━ src/
┣━ CMakeLists.txt
┣━ CMakePresets.json
┗━ vckpg.json
Of course, you might want to add a .gitignore, a README.md, a data/ folder and maybe some other stuff. This is just the bare minimum to start with.
Then make sure, your Linux has the basics installed:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
sudo apt install -y \
build-essential cmake ninja-build git zip unzip tar pkg-config mingw-w64 \
libx11-dev libxext-dev libxrandr-dev libx-cursor-dev libxi-dev libx-fixes-dev \
libx-render-dev libxss-dev libxxf86vm-dev libxinerama-dev libxtst-dev \
libxkbcommon-dev libxb1-dev libxcb-render0-dev libxcb-shm0-dev libxcb-xfixes0-dev \
libxcb-xinput-dev libxcb-randr0-dev libxcb-util-dev libxcb-image0-dev \
libwayland-dev wayland-protocols libegl1-mesa-dev libgbm-dev \
libasound2-dev libpulse-dev libdbus-1-dev libudev-dev \
libgl1-mesa-dev libdrm-dev
cd ~
git clone https://github.com/microsoft/vcpkg.git
cd vcpg
./bootstrap-vcpkg.sh -disableMetrics
I don’t know if all of that is truly needed - and I also learned the hard way that you better not install lib-ibus unless you really need it and want to set it up all the way. But if you are missing too much of that stuff, you might end up with a headless version of SDL3, that’s just not able to initiate any video mode. Ask me how I know…
myProject/CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
cmake_minimum_required(VERSION 3.20)
project(myProject LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(SDL3 CONFIG REQUIRED)
find_package(SDL3_image CONFIG REQUIRED)
add_exectuable(myProject)
target_sources(myProject PRIVATE
src/main.pp
)
if (MINGW)
set(SDL3_LIB SDL3::SDL3-static)
set(SDL3_IMAGE_LIB SDL3_image::SDL3_image-static)
else()
set(SDL3_LIB SDL3::SDL3-static)
set(SDL3_IMAGE_LIB SDL3_image::SDL3_image-static)
endif()
target_link_libraries(myProject PRIVATE
$(SDL3_LIB)
$(SDL3_IMAGE_LIB)
)
target_compile_options(myProject PRIVATE
$<$<CONFIG:Release>: -O2>
)
target_compile_options(myProject PRIVATE
-Wall
-Wextra
-Wpedantic
)
target_compile_options(myProject PRIVATE
$<$<CONFIG:Debug>: -fsanitize=address>
)
target_link_options(myProject PRIVATE
$<$<CONFIG:Debug>: -fsanitize=address>
)
if(MINGW)
target_link_options(myProject PRIVATE
-static
-static-libgcc
-static-libstc++
)
endif()
if(WIN32)
set_target_properties(myProject PROPERTIES
WIN32_EXECUTABLE TRUE
)
endif()
myProject/CMakePresets.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
{
"version": 3,
"configPresets": [
{
"name": "linux-debug",
"generator": "Ninja",
"binaryDir": "build/linux-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_TOOLCHAIN_FILE": "$env{HOME}/vcpkg/scripts/buildsystems/vcpkg.cmake"
}
},
{
"name": "linux-release",
"generator": "Ninja",
"binaryDir": "build/linux-release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_TOOLCHAIN_FILE": "$env{HOME}/vcpkg/scripts/buildsystems/vcpkg.cmake"
}
},
{
"name": "windows-debug",
"generator": "Ninja",
"binaryDir": "build/windows-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_TOOLCHAIN_FILE": "$env{HOME}/vcpkg/scripts/buildsystems/vcpkg.cmake",
"VCPKG_TARGET_TRIPLET": "x64-mingw-static",
"CMAKE_C_COMPILER": "x86_64-w64-mingw32-gcc",
"CMAKE_CXX_COMPILER": "x86_64-w64-mingw32-g++"
}
},
{
"name": "windows-release",
"generator": "Ninja",
"binaryDir": "build/windows-release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_TOOLCHAIN_FILE": "$env{HOME}/vcpkg/scripts/buildsystems/vcpkg.cmake",
"VCPKG_TARGET_TRIPLET": "x64-mingw-static",
"CMAKE_C_COMPILER": "x86_64-w64-mingw32-gcc",
"CMAKE_CXX_COMPILER": "x86_64-w64-mingw32-g++"
}
},
]
}
myProject/vcpkg.json
1
2
3
4
5
6
7
8
9
10
11
{
"name": "myProject",
"version": "0.1.0",
"dependencies": [
"sdl3",
{
"name": "sdl3-image",
"features": ["png"],
}
]
}
And that’s all there is to it.
Now you can just run cmake --preset windows-release and vcpkg will automatically download and compile SDL3 for you. The first time you do that, it’s gonna take a while, but then everything is cached and a lot faster on subsequent runs. Then you can run cmake --build build/windows-release and it will compile your code. That’s just main.cpp in this case, but you can add more files to CMakeLists.txt as needed.
If you then want to add sqlite support, like I did, you only have to add two extra lines to vcpkg.json: "sqlite3", and "sqlitecpp". And add SQLiteCpp to the target_link_libraries in CMakeLists.txt. After that run a cmake --fresh --preset windows-release and SQLite is ready to use.
Also - remember how I said “it caches everything” earlier? If you do end up in some weird, broken state, were nothing seems to work - delete the entire vcpkg folder, as well as .cache/vcpkg and then run git clone and ./bootstrap-vcpkg.sh -disableMetrics again. And yes, you have to add “-disableMetrics” every time to opt out of telemetry, because Microsoft. That goes completely against how stuff on Linux usually works and is not normal or common in any way. As you can see, everything else we installed - except for vcpkg - just used apt-install and was no-telemetry by default. That’s how things usually are.
The Struggle
It took me quite a while and some effort, to finally end up with that setup. Maybe it’s just me - but most of that stuff was not really evident to me, and not really easy to figure out. I tried getting help from LLMs like chatGPT, but they proved surprisingly bad at this - giving misleading advice, being overconfident in knowing “the actual true solution this time honestly for real” - while going in circles and just suggesting the same nonsense over and over again. What do they say about people trying the same thing over and over again and expecting different results?
Also, no matter how often I do insist on wanting pure SDL3 - the chatbots keep consisently mixing up SDL2 and SDL3 all the time. Overall, I feel like so far they may have wasted more of my time than they actually provided help with this. Which is not what I expected - I knew that these chatbots weren’t quite up to snuff in areas that I am well experienced and good at - but I honestly thought they might help me with C++, given that I’m a raw beginner with that. But nope, I really had to question their advice and double-check everything, and once even actively do exactly the opposite of what they told me to do. I honestly do worry for beginners who just blindly accept what they are told by these LLMs. It might kinda work - but I sure wouldn’t want to maintain that later…
Anyways, I’m very much looking forward to having this setup just work, and never needing to touch it again. I’d rather spend my time writing actual code - than tinkering with the build scripts.
Why I Didn’t Just Use Visual Studio
I had that set up and working early on - so why not use it, and go through all this instead? Well, I just wanted to feel comfortable… And the setup now is more in line with what I’m used to and would expect from a programming project.
Everything is documented in code. Like, you have a json manifest that tells vcpkg which packages to install. And all changes to that will get a human-readable version history in git.
You never run vcpkg install x by hand. The whole setup is easily reproducible on a new machine or for a new user - git clone, maybe an install.sh script that does the apt install stuff, cmake --preset and cmake --build - and done. Nice and straight-forward. Also IDE agnostic. Theoretically you can even run that on WSL if you want to. It’s also CI friendly - you could have a machine that automatically compiles a new build on commit to a release branch.
That’s just the way I roll, after two decades of working as a web developer. You might think this is overkill for just a little throw-away fun project - but I feel it already paid off, when was able to very quickly add in SQLite support without any hassle. And I probably would have gotten there a lot faster, had I decided earlier to just switch to developing on Linux, and follow those tutorials.
What’s Still Missing
You may have noticed that I’ve got no unit tests yet. I’m not a religious fanatic who insists on 100% coverage for everything - but I’ve grown fully convinced that you definitely always end up wanting at least some test coverage for some things.
And if you don’t have got a running unit test setup right from the start - you’ll only go about adding that in, when the lack of tests already is a pain, and negatively impacts you so much, that you decide “this has to change”. And I don’t know about you - but I personally am one of those people, who in hindsight always let things slide too long, before finally making then change. And then thinking to myself: “Had I only done that sooner.”
That’s why I think it’s best to setup unit-tests right away. Doesn’t mean you have to write tests right away - but that there is no “I have to set that up first” hurdle, when you first feel like: “maybe it would help to have a test for that”.
Why Use Static Linking?
I don’t know.
I just liked the idea of being able to have just one executable and one data file (sqlite db) - at least for early on, when I might want to share progress with some people, without having an installer or anything. But I might still change that down the line.
As I said - I’m pretty new to C++ and basically have not much of an idea what I’m doing. Have to make some mistakes to be able to learn from them later, right? Fingers crossed I didn’t make too many mistakes with this setup, and didn’t commit to any hard-to-undo decisions that will come back to bite me later. You don’t know what you don’t know, right? But hey, I’m feeling optimistic!
So What Is It That You Are Making?
For now, I’m just playing around. I do have a little idea for a potential hobby project - of course. But chances are that nothing ever comes from it, so for now I will rather keep the lid on that.
But if it does eventually lead somewhere, and something does come of it - then I’ll write another blog post about that, promise!
Until then, have a good time!