The Number that Broke and Spoke – C++ Investigator
Tags: cpp, development, userdefinedliterals, legacycode
Published: 2021-07-23 14:30:35

---
title: The Number that Broke and Spoke – C++ Investigator
published: true
date: 2020-10-13 10:30:00 UTC
tags: cpp,development,UserDefinedLiterals,LegacyCode
canonical_url: https://cppsenioreas.wordpress.com/2020/10/13/the-number-that-broke-and-spoke-cpp-investigator/
---

<p><em>How many times you find yourself writing hard-coded numbers inside your code, while trying to make sure as much as you can to make these numbers' units visible? You probably found yourself mentioning the units as part of the variable name, or at the comment, praying that who ever gets to your code will understand it. After reading this article, you will have the most maintainable way of doing so.</em></p>

<h1>Numbers Attack</h1>

<p>It was 2 am when I got a really bad feeling. I knew that something just happened to that patch I created the day before. All I done was to add the following function:</p>

```cpp
void delay(float milliseconds) { ... }
```

<p>In the morning, I ran into the office and checked the recent changes. This is what I saw:</p>

```cpp
/*void delay_m(float minutes);
void delay_s(float seconds);
void delay_millis(float milliseconds);
void delay_micros(float microseconds);
void delay_ns(float nanoseconds);*/

// ... A class function ...
    float initial_delay_ns = 1000;
    delay_ns(initial_delay_ns);
    // ...
    float increased_delay_s = 1;
    delay_s(increased_delay_s);
    // ...
    auto current_delay_ns = initial_delay_ns + increased_delay_s * 1e9;
    auto wait_time_m = 2.65;
    delay_m(wait_time_m);
    // ...
    std::cout << "Total delay: " << current_delay_ns + wait_time_m * 60 * 1e9 << std::endl;
// ...
```

<p>Well, it can't stay this way. The code looks like an active crime scene.</p>

<h1>Bring Numbers to Justice</h1>

<p>Those numbers won't speak a single word without their lawyers- the variables. We need to find a way to make them talk even when their layers are out for business. I decided to hire a consultant - <a href="http://en.cppreference.com">cppreference</a>. After some days she sent me the following message:</p>

> <p>Hi there.<br>I heard some words about a way to mark your numbers, so you'll be able to treat them in any way you want to. This mark can leave them as they are and can even transform them into a different type.<br><br>Note: This ability also relevant for chars and chars array.</p><p>It's called User-defined literals.</p><p>Sincerely yours,<br>cppreference.</p>

____________________________________________________

<h1>User-defined literals</h1>

<p>This feature makes it possible to mark with a suffix numbers, chars and strings, so they'll be more informative and understandable. This mark will produce a different object, that we'll define.</p>

<p>Before we go deeper into syntax and rules, let's see how the above example would look with user-defined literals:</p>


```cpp
// void delay(std::chrono::nanoseconds) {}
// ...
    using namespace std::literals::chrono_literals;
    auto initial_delay = 1000ns;
    delay(initial_delay);
    // ...
    auto increased_delay = 1s;
    delay(increased_delay);
    // ...
    auto current_delay = initial_delay + increased_delay;
    auto wait_time = 3min;
    delay(std::chrono::duration_cast<std::chrono::nanoseconds>(wait_time));
    // ...
    std::cout << "Total delay: " << std::chrono::duration_cast<std::chrono::seconds>(current_delay + wait_time).count() << " seconds." << std::endl;
// ...
```

<h2>Syntax</h2>

<p>In order to create new user-defined literals, regardless what the standard offer to us, we have to use the following syntax:</p>

<p>[constexpr] [inline] <code>out_type</code> operator"" _<code>suffix</code>(<code>in_type</code>);</p>

<ul><li>out_type - Can be any type you need.</li><li>suffix - The characters you want (There are several rules which apply here, for the simplicity always start with a lowercase letter. for a further reading: <a href="https://en.cppreference.com/w/cpp/language/user_literal">cppreference - user literal</a>).</li><li>in_type - Only the following parameter lists are allowed:<ul><li><code>const char*</code></li><li><code>unsigned long long int</code></li><li><code>long double</code></li><li><code>char</code></li><li><code>wchar_t</code></li><li><code>char8_t</code> (sice C++20)</li><li><code>char16_t</code></li><li><code>char32_t</code></li><li><code>const char*</code>, <code>std::size_t</code></li><li><code>const wchar_t*</code>, <code>std::size_t</code></li><li><code>const char8_t*</code> , <code>std::size_t</code></li><li><code>const char16_t*</code> , <code>std::size_t</code></li><li><code>const char32_t*</code> , <code>std::size_t</code></li></ul></li><li>In case that the function is a template function, it must have an empty parameter list and it must be a non-type template with the following restrictions:</li></ul>

<p>- The template is a non-type template parameter pack with element type char:</p>

```cpp
template <char...> out_type operator"" _suffix();
```

<p>- Since C++20 it can be also a non-type template parameter of class type (in which case it is known as a <em>string literal operator template</em>):</p>

```cpp
struct A { constexpr A(const char *); };
template<A a> A operator"" _a();
```

<h2>Standard Library Main Literals</h2>

<h3><strong>How To Use?</strong></h3>

<p>In order to use the standard user-defined literals, we have to use <code>using namespace std::<literals namespace>;</code>. Without it the syntax would look like this:</p>

```cpp
auto str = std::string_literals::operator""s("aaa", 3);
// Instead Of: auto str = "aaa"s;
```

<p>Usually, we don't get to see this kind of syntax, even when we don't use <code>using namespace ...</code> which usually considered as a bad practice, so why do we need it here?</p>

<p>The answer for this question is ADL (Argument-dependent lookup), which I won't be able to cover on this article. Make a long story short: The compiler can identify a function namespace if one of the parameters that sent to this function comes from the same namespace (e.g. <code>std::cout</code>).</p>

<h3><strong>String Literals</strong></h3>

<p><code>using namespace std::string_literals;</code></p>

<p>The user-defined literals we get from this namespace are <code>std::string_literals::operator""s</code> which accepts the following parameters:</p>

<ul><li>const char* __str, size_t __len</li><li>const wchar_t* __str, size_t __len</li><li>const char8_t* __str, size_t __len</li><li>const char16_t* __str, size_t __len</li><li>const char32_t* __str, size_t __len</li></ul>

<p>Each on of them will return a <code>std::basic_string</code> object of the specified type. For example:</p>

```cpp
auto str = "Hello String Literals"s;
```

<p>Will make str of type <code>std::basic_string<char></code> which is the same as <code>std::string</code>. This one is a very common usage of user-defined literals.</p>

<h3><strong>Chrono Literals</strong></h3>

<p><code>using namespace std::chrono_literals;</code></p>

<p>Chrono literals return a <code>std::chrono::duration</code> object. It supplies the following user-defined literals: <code>h</code> [hours], <code>min</code> [minutes], <code>s</code> [seconds], <code>ms</code> [milliseconds], <code>us</code> [microseconds], <code>ns</code> [nanoseconds], <code>y</code> [A specific year in the range: (-32767, 32767)], <code>d</code> [Representing a day of a month in the calendar (legal only for values lower than 256)].</p>

<p>A usage example you can find in the first user-defined literals example in this article.</p>

<h3><strong>Complex Literals</strong></h3>

<p><code>using namespace std::literals::complex_literals</code></p>

<p>Complex literals will return a given number as an imaginary part with real part initialized to zero. Available user-defined literals:</p>

<ul><li><code>i</code> - returns <code>std::complex<double>(0, arg);</code></li><li><code>if</code> - returns <code>std::complex<float>(0, arg);</code></li><li><code>il</code> - returns <code>std::complex<long double>(0, arg);</code></li></ul>

```cpp
int main() {
    using namespace std::complex_literals;
    std::complex<double> c = 1.0 + 1i; // std::complex<double>(1.0, 1.0)
    std::complex<float> z = 3.0f + 4.0if; // std::complex<float>(3.0, 4.0)
}
```

<h2>User-Defined Literals Example</h2>

<h3><strong>Angles Example</strong></h3>

```cpp
class angle {
public:
    struct degrees {};
    struct radians {};
    constexpr angle(float deg, degrees) { _deg = deg; }
    constexpr angle(float rad, radians) { _deg = rad * 180 / M_PI; }
    [[nodiscard]] constexpr float get_degrees() const { return _deg; }
    [[nodiscard]] constexpr float get_radians() const { return _deg * M_PI / 180; }

private:
    float _deg;
};

namespace literals {
    constexpr angle operator"" _deg(long double deg) {
        return angle(deg, angle::degrees{});
    }

    constexpr angle operator"" _deg(unsigned long long int deg) {
        return angle(deg, angle::degrees{});
    }

    constexpr angle operator"" _rad(long double rad) {
        return angle(rad, angle::radians{});
    }

    constexpr angle operator"" _rad(unsigned long long int rad) {
        return angle(rad, angle::radians{});
    }
}

using namespace literals;

int main() {
    constexpr auto deg = 3.14159265_rad;
    constexpr auto rad = 360_deg;
    static_assert(deg.get_degrees() == 180);
    static_assert(rad.get_radians() == (float)(M_PI * 2));
    std::cout << deg.get_degrees() << std::endl; // 180
    std::cout << deg.get_radians() << std::endl; // 3.14..
    std::cout << rad.get_degrees() << std::endl; // 360
    std::cout << rad.get_radians() << std::endl; // 6.28..
    return EXIT_SUCCESS;
}
```

<h3><strong>Dates Example</strong></h3>

<p>Based on dates example from <a href="https://cppsenioreas.wordpress.com/2020/08/23/become-a-compile-time-coder/">Become a Compile-Time Coder</a>.</p>

```cpp
struct date_offset {
    int d;
    int m;
    int y;

    constexpr date_offset(int days, int months, int years) : d(days), m(months), y(years) {}

    [[nodiscard]] constexpr date_offset operator+(date_offset ref) const {
        return date_offset(d + ref.d, m + ref.m, y + ref.y);
    }

    [[nodiscard]] constexpr date_offset operator-(date_offset ref) const {
        return *this + date_offset(-ref.d, -ref.m, -ref.y);
    }
};

class date {
public:
    constexpr date(int day, int month, int year)
            : d(day), m(month), y(year) {
        self_balance();
    }

    [[nodiscard]] constexpr date offset(date_offset offset_data) const {
        const auto after_years_offset = date(d, m, y + offset_data.y);
        const auto after_month_offset = date(d, m + offset_data.m, after_years_offset.get_year());
        return date(after_month_offset.get_day() + offset_data.d, after_month_offset.get_month(), after_month_offset.get_year());
    }

    [[nodiscard]] constexpr date operator+(date_offset offset_data) const {
        return offset(offset_data);
    }

    [[nodiscard]] constexpr unsigned short get_day() const { return d; }
    [[nodiscard]] constexpr unsigned short get_month() const { return m; }
    [[nodiscard]] constexpr unsigned short get_year() const { return y; }

private:
    int d, m, y;

    constexpr void self_balance() {
        unsigned short days_in_month;
        unsigned short days_in_prev_month = 31;
        bool is_change_detected = false;
        if (m == 2) {
            if (!(y % 4)) {
                days_in_month = 29;
            } else {
                days_in_month = 28;
            }
        } else {
            if (m <= 7 &amp;&amp; m % 2 || m >= 8 &amp;&amp; m % 2 == 0) {
                days_in_month = 31;
                if (m != 8) {
                    days_in_prev_month = 30;
                }
            } else {
                days_in_month = 30;
            }
        }

        if (d > days_in_month) {
            d -= days_in_month;
            m++;
            is_change_detected = true;
        } else if (d < 1) {
            d += days_in_prev_month;
            m--;
            is_change_detected = true;
        }

        if (m > 12) {
            m = 1;
            y++;
            is_change_detected = true;
        } else if (m < 1) {
            m = 12 - m;
            y--;
            is_change_detected = true;
        }

        if (is_change_detected) self_balance();
    }
};

namespace literals {
    inline namespace dates_literals {
        constexpr date_offset operator"" _d(unsigned long long int days) {
            return date_offset(days, 0, 0);
        }

        constexpr date_offset operator"" _m(unsigned long long int months) {
            return date_offset(0, months, 0);
        }

        constexpr date_offset operator"" _y(unsigned long long int years) {
            return date_offset(0, 0, years);
        }
    }
}

using namespace literals;

int main() {
    constexpr date my_date(23, 8, 2020);
    constexpr auto new_date = my_date + 8_d + 3_m + 5_y;
    std::cout << new_date.get_day() << " / " << new_date.get_month() << " / " << new_date.get_year() << std::endl;
    static_assert(new_date.get_day() == 1 && new_date.get_month() == 12 && new_date.get_year() == 2025);
    return EXIT_SUCCESS;
}
```

________________________________________________

<h1>Summarize</h1>

<p>A number without a literal is just a number without any meaning, and it makes the code really hard to maintain in the long term. We can use user-defined literals in order to control conversions between units, and this way we can significantly reduce our code complexity and reduce the chances for units conversion mistakes.</p>

_This article originally published on my personal blog: [C++ Senioreas](https://cppsenioreas.wordpress.com/2020/10/13/the-number-that-broke-and-spoke-cpp-investigator/)_

<p>Full examples repository: <a href="https://github.com/coralkashri/cppsenioreas-user-defined-literals">cppsenioreas-user-defined-literals</a></p>