Tip 96 of #TuesdayCodingTips - Reference equality in C#

Languages other than C++ are also prone to having "fun" footguns. Take C#'s reference semantics for example. All I wanted was a dictionary with a file hash as a key. Since built-in cryptographic tools produce a `byte[]` object containing the hash, we can use that as a key for the dictionary right away, right?

The code compiles and unit tests pass, but integration testing fails. Why? Because C# differentiates between value equality (for example, in structs and records, where objects are compared attribute-wise) and reference equality, where only pointers to those objects are compared.

The fix is simple - just provide a custom equality comparator. It is an annoying footgun nonetheless.

#csharp #tips

Tip 95 of #TuesdayCodingTips - Copy & swap idiom

Rule of 5 says that if you need to implement one of the copy/move constructors/assignment operators or a destructor, you shall implement all of them to avoid resource management issues. However, this often leads to duplicated code and subtle issues, such as a class being move-assigned onto itself.

Copy & swap idiom helps deduplicate the code by implementing assignment operators in terms of creating a temporary copy of the source object and swapping its contents with the target object.

On top of base copy & swap, one can even merge the two assignment operator methods into a single one, further reducing the amount of code needed. Unfortunately, this òften comes at the cost of the move-assignment operation no longer being `noexcept`, thus preventing many standard containers from using the most effective relocation strategies.

#cpp #tips #programming

Tip 93 of #TuesdayCodingTips - Constrained auto

Tired of convoluted templates in your C++? Want to make them more legible?

C++20 introduced concepts - a powerful feature that can restrict your templated APIs to only such arguments that fulfil a particular requirement, such as having a specific method, being an enum value, etc.

While it makes template metaprogramming easier and diagnostics clearer, the code will increase in verbosity. This is where the "constrained auto" feature can come in really handy.

Since `auto` in a function parameter list is the same thing as a `template<class T>`, you can use it to reduce the visual noise. Furthermore, you can prefix the `auto` with any concept of your liking, immediately restricting the API in arguably cleaner and more legible fashion.

#cpp #programming #tips

Tip 92 of #TuesdayCodingTips - Pinning interop memory in C#

When interoping C# with, for example, C++, one can send a pointer to the managed memory directly to the native code and work with it without the need to copy it into the local address space. For as long as the code operates within a single process, the memory space is shared.

However, there is a gotcha you need to keep in mind. First, you need to ensure that the garbage collector won’t delete that memory by keeping it referenced in the managed code. Second, even if you do, GC might decide to reorganize the memory around to reduce fragmentation and improve allocation times.

You can prevent this behavior on a particular pointer by protecting it with a “pinned” GC allocation — technically not allocating new memory, but rather protecting existing memory from being shuffled around.

Just remember to "free" the pointer once it is no longer used in native code, so the memory can be reused for something else.

#tips #csharp #programming #cpp

Tip 91 of #TuesdayCodingTips - String or StringBuilder?

If you're a C++ programmer, you can easily troll yourself while working with C#'s `string` objects. In C++, they are optimized vectors of bytes and can be dynamically resized at will through concatenation. If you want to avoid extra allocations, you can, in certain cases, use `string_view` for optimizations.

In C#, strings are immutable, and even if they appear to support the same operations, the underlying implementation always creates a brand new object instead of modifying the existing one. This is a strong guarantee for thread safety, but it hurts performance and local reasoning.

If you wish to rapidly modify a particular string, you shall use a `StringBuilder` class. Its API allows for modifications of the underlying object, making it faster for use cases like parser implementations. Just remember to always protect it with a lock in a multi-threaded environment!
#tips #programming #csharp

Tip 90 of #TuesdayCodingTips - Is enum value defined?

In both C++ and C#, enums are mere named numerical constants. Even if they appear to be distinct types, and switch statements can check whether you covered all names within an enum, that's simply not enough.

Anybody can take a random number and cast it to the enum's type, effectively crafting something that appears legit, but may fall through non-defaulted switch statements. While in C++ you don't have a foolproof defense against this (perhaps with C++26 reflection, we will), in C# you can easily check enum validity with `Enum.IsDefined` method.

It simply checks whether the current value of an enum has a name associated with it and if yes, it returns true. This is great, especially for the cases where values associated with underlying names aren't always incremented by one.

#tips #csharp #programming

Tip 89 of #TuesdayCodingTips - Semantic versioning

When creating a library, semantic versioning is a really useful versioning scheme to use.

It's a promise to your users that changes bumping the minor/patch version numbers are backward-compatible. As such, much of the software relies on these numbers to figure out whether a particular version requirement can be satisfied by higher-version binaries.

For example, Unix SONAME links provide a stable way to (typically) reference a major version of a library, while the pointed-to file can be updated to newer revisions.

If you only specify the major (or major.minor) version number, CMake Find* scripts will find the newest compatible package. Various wildcards in cargo, npm, and other package managers follow the same principles.

Just be sure to read the full FAQ at http://semver.org and know that adhering to this versioning scheme is hard, but your users will love you for it... (1/2)

#tips #programming #semver

Semantic Versioning 2.0.0

Semantic Versioning spec and website

Semantic Versioning

Tip 88 of #TuesdayCodingTips - Enum-discriminated unions with nlohmann::json

JSON is a fairly trivial format and as such, its only way of supporting polymorphic data is an enum-discriminated approach.

In other words, you might have an array of objects where each object has a 'type' property and all other properties are determined by the value of the type. Circle might have radius, rectangle has dimensions, both have position.

But how would you parse that in C++ with `nlohmann::json` and a minimal boilerplate? While the library doesn't have a native support for `std::variant`, let alone recognizing this pattern, it allows you to extend it easily with a custom `adl_serializer` specialization.

Remaining objects can still be handled by appropriate macros provided by the library to reduce further boilerplate.

Compiler explorer link: https://godbolt.org/z/KsW4dhj5j

#cpp #tips #programming

They say learning by example is the best way to learn. I'd like to share one such example of API design process based on a recent discussion with one of my colleagues: https://medium.com/@nerudaj/a-case-study-on-api-design-f22a8665cf2d

If you follow my #TuesdayCodingTips, this showcases a practical application of many of them.

And if you dislike clean code for whatever reason, this article is an example of how to overengineer your code in three simple steps I guess :D

#CleanCode #SoftwareArchitecture #SoftwareEngineering

A case study on API design - Jakub Neruda - Medium

They say learning by example is the best way to learn. Today, I want to share one such example of API design process on a concrete piece of code. This innocent-looking function declaration was a part…

Medium

Tip 87 of #TuesdayCodingTips - Emplace into a vector of variants

`emplace_*` is an alternative to methods like `insert` or `push_*` in standard C++ containers. It allows you to construct the inserted object directly inside the container, passing all required parameters down to it, eliminating a needless copy/move along the way.

But what if you have a `std::vector<std::variant<Ts>>`? The interface is not flexible enough to control how to construct the variant object. There is however a hidden workaround.

Suppose your variant can be constructed from a cheap-to-construct type (trivial type, `std::monostate`, or similar). In that case, you can use that to initialize the new element and then invoke `std::variant::emplace` to override the element's memory with another type, presumably one that is expensive to copy or move.

Compiler Explorer link: https://godbolt.org/z/Yec1hvKzP

#tips #programming #cpp