Jakub Neruda · @jakub_neruda
187 followers · 586 posts · Server techhub.social

Tip 21 of - Dangers of Enums

C++ and C# have a caveat in their enum types. Each enum has an underlying storage type which limits a maximum number of values and how much space it takes in memory. However, it also works the other way - it says how many values the enum handles.

If you define an enum with just three values, if it has an underlying type of uint8, it handles 256 distinct values. You can take any integer up to 255, cast it to that enum type, and get a "valid" enum value. When branching on an enum, you need to keep in mind that you might get an undefined enum value, especially if it was deserialized from the wire.

To add insult to injury, compilers won't warn you by default. Only the pattern matching does for C#. C++ can warn you if you don't cover a defined enum value in a switch statement, but you must not use the default case - preventing you from correctly handling ill-formed values.

#tuesdaycodingtips #programming #cplusplus

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
184 followers · 580 posts · Server techhub.social

Tip 20 of - Be explicit!

If you choose to add a conversion operator for your class in C++, be sure to mark it as "explicit" or you can get nasty side-effects that will be hard to debug. For example, having an implicit conversion operator to bool will make the operator less-than "work" for your class, meaning that class can be used as a key for std::map and suddenly, all hell breaks loose.

Similarly, it is good practice to mark single param constructors "explicit" to prevent implicit conversions from trivial data types or to possibly track places where your object is copied.

#tuesdaycodingtips #programming #cplusplus #cpp

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
141 followers · 512 posts · Server techhub.social

Tip 19 of - [[nodiscard]] annotation

Since C++17, one can use [[nodiscard]] annotation to mark API functions whose return values should not be discarded without consideration. This usually applies to methods that make no sense to call without evaluating their return value (like predicate functions such as 'empty' or factory function). The compiler will issue a warning if the return value is not handled.

But beware! Sometimes it might be tempting to return some scope guard that will maintain postconditions automatically at the end of the scope using RAII (such as committing transactions). However, a user of your API can pipe the result of your nodiscard function into a std::ignore helper which causes the temporary to destruct immediately, defeating the idea of end-of-scope cleanup.

#tuesdaycodingtips #cplusplus #programming #cpp

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
106 followers · 502 posts · Server techhub.social

Tip 18 of - Generating random numbers in C++

Classic C way of generating random numbers is to set the current time as a seed and then modulo results of the rand function to get somewhat random numbers (modulo can introduce bias based on the implementation of rand).

C++11 introduces the concept of engines, adaptors, and distributions. Engines are seedable and give you random numbers similar to rand() calls. However, each engine is implemented differently, impacting its speed and memory footprint. Adapters tamper with RNG output, such as shuffling the order of output values or discarding them.

Distributions transform the output from the generators to get values in certain ranges and probabilistic properties, such as the middle value being the most probable (normal distribution). Distributions are more nuanced than just using the modulo operator so their outputs are more suitable for cryptography use-cases.

#tuesdaycodingtips #cpp #cplusplus #programming

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
106 followers · 490 posts · Server techhub.social

Tip 17 of - RAII for C-like APIs

Resource Acquisition is Initialization (RAII) is an awfully named principle saying that your class should be fully usable after construction and should clean itself up in the destructor (in other words, no (de)init methods). This principle is common in modern programming languages and is also adopted in modern C++ codebases.

But what if you work with C-like API like fopen? It returns a pointer you need to manually free before it goes out of scope. Otherwise, you leak memory and open file handles. This is where smart pointers come to the rescue as you can wrap raw pointers and give them custom callbacks to execute when they leave the scope.

#tuesdaycodingtips #programming #cplusplus

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
105 followers · 485 posts · Server techhub.social

Tip 16 of - Creating objects in existing memory

C++ allows you to scrap your OS' memory allocators, get yourself a huge chunk of memory and write your own allocators (they can be used with the standard library). It's a hardcore optimization, but here's a related tip that might be handy in normal projects as well.

When using the operator new, one can provide it with an already allocated memory, so it runs a constructor for a particular type in there. After that, reinterpret that memory as a pointer to your desired object and use it normally. However, deletion is a bit more complicated, as you have to call the destructor on that object manually (because delete must be called on the original memory, not the object).

I used this trick when implementing a resource manager that needs to hold any user data type (that is still default-constructible in my case): github.com/nerudaj/dgm-lib/blo

#tuesdaycodingtips #programming #cplusplus #cpp

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
103 followers · 464 posts · Server techhub.social

Tip 15 of - Mathematical constants in C++

Since C++20, you can include header <numbers> and use many commonly used, precomputed mathematical constants like Pi, Phi, square root of 2 and 3, natural logarithms of 2 and 10, and many more! Even better, you can choose their precision by specifying the underlying type via a template.

And since C++17, the <cmath> header contains some special math functions I've never heard of like cylindrical Bessel functions, associated Legendre polynomials, or Riemann zeta function.

Constants are here: en.cppreference.com/w/cpp/nume
Special functions are here: en.cppreference.com/w/cpp/nume

#cpp #tuesdaycodingtips #cplusplus #programming

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
99 followers · 437 posts · Server techhub.social

Tip 14 of - Pattern matching in C#

Since C# 7, you can use pattern matching to write simple and safer code. First, it allows you to return a value from a switch expression, saving you the hassle of creating a temporary on a separate line. Second, it can match not only on discrete values but expressions as well and the compiler can warn you if some expression is redundant (relational patterns). Third, it can match on more than one value through records, lists, etc. As a cherry on top, pattern matching also encapsulates type and null matching.

Note that when pattern matching, the _ symbol is the discard pattern, matching everything that reaches it. Also, switches do not by default warn the user if you forget a branch (for example on enum). Pattern matching does detect and warns on such occasions.

More on pattern matching: learn.microsoft.com/en-us/dotn

#tuesdaycodingtips #dotnet #programming

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
88 followers · 421 posts · Server techhub.social

Tip 13 of - Deleting problematic overloads

Suppose you have a C++ function for opening a file stream that accepts a path to a file and a string (view) with open-mode specifications (like fopen). The issue is, even though your API is strongly typed, users can still swap the arguments by accident and don't get a compile time error, due to implicit conversions from string-like constants.

Luckily, there is an easy hack with which you can disable implicit conversions for one particular function by explicitly deleting all overloads (via auto template) and allowing just one particular version. The coolest thing is that even the return value doesn't have to match!

#tuesdaycodingtips #programming #cplusplus

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
88 followers · 400 posts · Server techhub.social

Tip 12 of - Stacktrace

C++23 brings a new feature that I always wanted since I've seen it in C# - stacktrace.

When combined with exceptions, it can immensely help with the analysis of user error reports, when you cannot simply attach a debugger. The code snippet shows how to create an exception class that can automatically capture stack trace at the point where it was constructed.

The API even allows you to iterate over each stack entry, but honestly - who needs that?

Support in trunk Clang/GCC is wonky, MSVC works okay.

#tuesdaycodingtips #cplusplus #programming

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
86 followers · 383 posts · Server techhub.social

Tip 11 of - Null-coalescing operator

Some languages, like C# or Javascript, have a special operator ?? which is a specialization over the normal ternary operator. It either returns the value of the left operand or, if it is null, it returns the value of the right operand.

Furthermore, the ??= operator allows you to only assign to a variable if it is null. Syntax among languages might differ, so check out this overview: en.wikipedia.org/wiki/Null_coa

#tuesdaycodingtips #programming #dotnet #javascript

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
84 followers · 356 posts · Server techhub.social

Tip 10 of - CTest

CMake can do many things and instrumenting your test suite is one of them. Use add_test to tell CMake it should run a particular command with given parameters. If it returned zero, it succeeded. You can use it to run a test-runner binary or to make a matrix of input parameters that are expected not to crash the program.

In order for add_test to do anything, one has to also call enable_testing(). Running the tests is just a matter of running CTest from the command line. Just note that CTest requires you to specify a build configuration if you have more than one.

#tuesdaycodingtips #cplusplus #cmake #programming

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
84 followers · 353 posts · Server techhub.social

Tip 9 of - Source location

If you want to log a filename and line number prior to C++20, you need to use the likes of __FILE__ and __LINE__ macros. To use those easily, you have to create a wrapper macro for logging, which usually leads to an ugly macro bonanza.

C++20 introduces std::source_location, a regular object that can be constructed as a default value of a logging function parameter and passed along as the user needs.

#tuesdaycodingtips #cplusplus #programming

Last updated 1 year ago

Jakub Neruda · @jakub_neruda
80 followers · 340 posts · Server techhub.social

Tip 7 of - Exposing internal bits of .NET projects

C# has a number of visibility keywords and their combinations. A peculiar one is 'internal' which restricts the visibility of the symbols to the current assembly. However, that can be tweaked.

You can manually specify the names of assemblies to which the 'internal' bits will be visible. Why is this useful? Because you can expose otherwise private parts of your code to assemblies like unit tests and test code with ease. No more 'friend' shenanigans like in C++ unit tests!

Ways how to configure this evolved over the years and the most up-to-date approach seems to be in configuring the project file directly (with the latest MSVC and .NET 5+).

#tuesdaycodingtips #dotnet #softwareengineering

Last updated 2 years ago

Jakub Neruda · @jakub_neruda
81 followers · 313 posts · Server techhub.social

In today's , let's talk about an "explicit template instantiation" with tip 6.

Templated code in C++ must be defined in the header files (or a function within the same cpp file). This is because whenever a templated function is called, the compiler generates a new implementation using the concrete type the function was called with. If you split the definition from the declaration, you need to tell it what implementations to generate and link.

That's what explicit template instantiation is for. It is useful when you're writing generic code for a well-known subset of types, like string manipulation. It cannot be used for totally generic types because you need to be able to list all types the template will be used with (otherwise the user will get a linker error).

#tuesdaycodingtips #cplusplus #programming

Last updated 2 years ago

Jakub Neruda · @jakub_neruda
79 followers · 307 posts · Server techhub.social

I wanted to post tip 5 of my but it got too verbose, so I can't put it here.

Full tip can be found here: linkedin.com/posts/jakub-nerud

TL;DR Summary:
Use the "modern language style" to initialize objects on stack. Use the "Java style" for initialization of atrributes of classes in their declarations.

Use normal braces for regular constructors and curly braces for initializer-list constructors (see the std::vector example for explanation).

This will clearly state your intent, it will avoid the "Most vexing parse" problem and you will disambiguate regular constructors from initializer-list ones.

#tuesdaycodingtips #programming #tips #cpp

Last updated 2 years ago

Jakub Neruda · @jakub_neruda
79 followers · 306 posts · Server techhub.social

Almost didn't make it for this tuesday's . Here goes tip 4, as niche as it might be:

Do you use cmake and also need to improve your C++ compile times? There's a technique called precompiled headers. Usually, it is somewhat obtuse to set up, MSVC tends to force you to include the precompiled header in every single .cpp file.

With cmake, you can use a single command to instantly boost your compile times with zero invasiveness. On my hobby project, I reduced compile time down from 3m30s to just 30s (in single threaded CI, I went from 7m to 3m30s).

Just remember to only include headers that don't change often, presumably never. Good candidates are std headers and third party header-only dependencies.

#tuesdaycodingtips #programming #cplusplus

Last updated 2 years ago

Jakub Neruda · @jakub_neruda
73 followers · 264 posts · Server techhub.social

Another Tuesday, another ! Tip 3:

Since C++17, we can use "structured bindings". These allow you to perform tuple, array, or even structure unpacking. Such unpacking is commonly helpful when iterating over maps, so you don't have to define an alias for key and value manually. It is equally useful when a function returns a tuple (and only proves you should prefer returning structs over tuples).

Unpacking struct members and arrays is just a cherry on top. You won't use it much, but it is there should you ever need it!

#tuesdaycodingtips #cplusplus #programming

Last updated 2 years ago

Jakub Neruda · @jakub_neruda
72 followers · 257 posts · Server techhub.social

Continuing my new thing called with tip 2:

What if you have a template class and you want to give it extra methods for certain specializations?

For example, you might want to make a templated initialization guard and you want it to be as non-intrusive as possible. So you want it to support arithmetic operations if the underlying type is an arithmetic type.

Pre C++20, you could leverage std::enable_if. However, such code is clunky and unreadable.

With C++20, you can use concepts and the 'requires' keyword to clean up the syntax significantly, either by in-place definition of type properties or by defining a dedicated concept.

With C++20 constructs, the error messages are also improved.

#tuesdaycodingtips #cplusplus #programming #cpp

Last updated 2 years ago

Jakub Neruda · @jakub_neruda
66 followers · 218 posts · Server techhub.social

Trying a new thing here - a bi-weekly , mainly aimed at coding in C++. Here's the first tip:

Concepts are a C++20 feature that greatly simplifies templated code and allows you to build powerful type trait restrictions.

For example, you can create a function that accepts a concrete type that implements a certain interface, allowing the compiler to optimize the virtual table away. Or you can define static interfaces where an object is required to have certain methods or even attributes, even if it doesn't implement any virtual interface. Or you can restrict the function to be called only with a small subset of well-known types.

Concepts do not replace dynamic polymorphism as you cannot do everything in compile-time. Still, it can open new optimization opportunities and help you write in-place construction functions like std::make_unique without an excessive amount of unreadable templates.

#tuesdaycodingtips #cplusplus #programming #coding

Last updated 2 years ago