average

User-Defined Literals

User-defined literals are a unique feature in all mainstream programming languages. They empower you to combine values with units.

The syntax

Literals are explicit values in a program. This can be a boolean like true, the number 3 or 4.15; but this can also be the character ‘a’ or the C string “hallo”. Even the lambda function [](int a, int b){ return a+b; } is a function literal. With C++11, it’s possible to generate user-defined literals by adding a suffix to a built-in literal for integers, floating points, characters, and C strings.

User-defined literals must obey the following syntax: built-in literal + _ + suffix.

Usually, you use the suffix for a unit:

101000101_b
63_s
10345.5_dm
123.45_km
100_m
131094_cm
33_cent
"Hallo"_i18n

 

But what is the critical benefit of user-defined literals? The C++ compiler maps the user-defined literals to the corresponding literal operator. This literal operator has – of course – to be implemented by the programmer.

The magic

Let’s look at the user-defined literal 0101001000_b that represents a binary value. The compiler maps the user-defined literal 0101001000_b to the literal operator operator”” _b(long long int bin). A few special rules are still missing.

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

Be part of my mentoring programs:

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (starts March 2024)
  • Do you want to stay informed: Subscribe.

     

    • There has to be a space between the quotation marks  (“”) and the underscore with the suffix (_b).
    • You have the binary value (0101001000) in the variable bin.
    • The compilation will fail if the compiler doesn’t find the corresponding literal operator.

    We get with C++14 an alternative syntax for user-defined types. They differ from the C++11 syntax because it requires no space. Therefore, it is possible to use reserved keywords like _C as a suffix and a user-defined literal of the form 11_C. The compiler will map 11_C to the literal operator””_C(unsigned long long int).  The simple rule is now that you can use suffixes starting with an upper letter.

    User-defined literals are the killer feature in modern C++ if you want to write safety-critical software. Why? Thanks to automatically mapping the user-defined literal to the literal operator, you can implement type-safe arithmetic. The compiler takes care that you don’t add apples and pears. Example?

    How many meters do I drive on average per week? The question has occupied me for a long time.

    Typesafe calculation with distances

    Before I deal with the details, here is the main program.

     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
    // average.cpp
    
    #include <distance.h>
    #include <unit.h>
    
    using namespace Distance::Unit;
    
    int main(){
    
      std:: cout << std::endl;
    
      std::cout << "1.0_km: " << 1.0_km << std::endl;
      std::cout << "1.0_m: " << 1.0_m << std::endl;
      std::cout << "1.0_dm: " << 1.0_dm << std::endl;
      std::cout << "1.0_cm: " << 1.0_cm << std::endl;
      
      std::cout << std::endl;
    
      std::cout << "0.001 * 1.0_km: " << 0.001 * 1.0_km << std::endl;
      std::cout << "10 * 1_dm: " << 10 * 1.0_dm << std::endl;
      std::cout << "100 * 1.0cm: " << 100 * 1.0_cm << std::endl;
      std::cout << "1_km / 1000: " << 1.0_km / 1000 << std::endl;
    
      std::cout << std::endl;
      std::cout << "1.0_km + 2.0_dm +  3.0_dm + 4.0_cm: " << 1.0_km + 2.0_dm +  3.0_dm + 4.0_cm << std::endl;
      std::cout << std::endl;
      
      auto work= 63.0_km;
      auto workPerDay= 2 * work;
      auto abbrevationToWork= 5400.0_m;
      auto workout= 2 * 1600.0_m;
      auto shopping= 2 * 1200.0_m;
      
      auto distPerWeek1= 4*workPerDay-3*abbrevationToWork+ workout+ shopping;
      auto distPerWeek2= 4*workPerDay-3*abbrevationToWork+ 2*workout;
      auto distPerWeek3= 4*workout + 2*shopping;
      auto distPerWeek4= 5*workout + shopping;
    
      std::cout << "distPerWeek1: " << distPerWeek1 << std::endl;
      
      auto averageDistance= getAverageDistance({distPerWeek1,distPerWeek2,distPerWeek3,distPerWeek4});
      std::cout<< "averageDistance: " << averageDistance << std::endl;
      
      std::cout << std::endl;
    
    }
    

     

    The literal operators are implemented in the namespace Distance::unit. You should use namespaces for user-defined literals because name collisions are, for two reasons, very likely. First, the suffixes are usually very short; second, the suffixes usually stand for units that already established abbreviations. I used the suffixes km, m, dm, a, and cm in the program.

    Here is the output of the program. My unit for distances is a meter.

    average

    I display in lines 12 – 15 the various distances; I calculate in lines 19 – 22 the meter in various resolutions. The last test looks quite promising.
    1.0_km + 2.0_dm +  3.0_dm + 4.0_cm  is 1000.54 m (line 54). The compiler takes care of the calculations with all units.

    The key question remains. How many meters will I drive on average a week? For convenience, I define a few constants: work, workPerDay, abbrevationToWork, and shopping. These are my building blocks for the four weeks (lines 34 – 37). I went 493 km in the first week by car. The function getAverageDisttance (line 41) helps me to get the average. I have to invoke it with an initializer list. I drive 255900m on average per week. That needs to change! And that has changed. I’m now an independent trainer.

    Under the hood

    I ignored one fact. Where are the MyDistance objects defined? They are hidden in the program behind the automatic type deduction. Therefore, the explicit type for the variable work (line 28) is Distance::Distance. Line 28 is equivalent to Distance::MyDistance work= 63.0_km;

     

    arithmetik

     

    The following steps will automatically happen if I use 1.5_km + 105.1_m in the source code. The compiler maps at first the suffixes km and m to the corresponding literal operators; second, the compiler maps the + operator to the overloaded + operator of the MyDistance objects. Both steps can only work if the programmer implements the right operators in his contract. In this concrete case, he has to implement the literal operator and + operator. The black arrows in the graphic stand for the automatically performed mapping of the compiler. The red arrows stand for the functionality that the programmer has to implement.

    What’s still missing to make the graphic complete? Right! The meat behind the red arrows.

    Tasks of the programmer

    At first, to the known overloading of operators. I overloaded for the class MyDistance basic arithmetic (lines 15 – 28) and the output operator (lines 30 – 33). The operators are global functions and can use – thanks to their friendship – the internals of the class. I store in the private variable m the distance. The function getAverageDistance (lines 41 – 45) is applying the overloaded addition and division operator.

     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
    // distance.h
    
    #ifndef DISTANCE_H
    #define DISTANCE_H
    
    #include <iostream>
    #include <ostream>
    
    
    namespace Distance{
      class MyDistance{
        public:
          MyDistance(double i):m(i){}
    
          friend MyDistance operator+(const MyDistance& a, const MyDistance& b){
            return MyDistance(a.m + b.m);
          }
          friend MyDistance operator-(const MyDistance& a,const MyDistance& b){
            return MyDistance(a.m - b.m);
          }
    	 
    friend MyDistance operator*(double m, const MyDistance& a){ return MyDistance(m*a.m); } friend MyDistance operator/(const MyDistance& a, int n){ return MyDistance(a.m/n); } friend std::ostream& operator<< (std::ostream &out, const MyDistance& myDist){ out << myDist.m << " m"; return out; } private: double m; }; } Distance::MyDistance getAverageDistance(std::initializer_list<Distance::MyDistance> inList){ auto sum= Distance::MyDistance{0.0}; for (auto i: inList) sum = sum + i ; return sum/inList.size(); } #endif

     

    Shorter but more thrilling are the literal operators.

     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
    // unit.h
    
    #ifndef UNIT_H
    #define UNIT_H
    
    #include <distance.h>
    
    namespace Distance{
    
      namespace Unit{
        MyDistance operator "" _km(long double d){
          return MyDistance(1000*d);
        }
        MyDistance operator "" _m(long double m){
          return MyDistance(m);
        }
        MyDistance operator "" _dm(long double d){
          return MyDistance(d/10);
        }
        MyDistance operator "" _cm(long double c){
          return MyDistance(c/100);
        }
      }
    }
    
    #endif
    

     

    The literal operators take as an argument a long double and return a MyDistance object. MyDistance is automatically normalized to meters. And now? That was the whole functionality that the programmer has to provide.

    I ignored one big optimization potential in my program. Almost all operations can be performed at compile time; almost all objects can be instantiated at compile time. To make that happen, I must declare the operations and objects as constexpr.

    What’s next?

    You can define user-defined literals not only for floating-point numbers. You can do it for integers, characters, and C strings. In addition, C++ has two ways to do integers and floating-point numbers. One is called cooked, the other raw. I have a lot more to write about user-defined literals. Wait for the next post.

     

     

     

     

     

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, moon, Philipp Lenk, Hobsbawm, and Charles-Jianye Chen.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Seminars

    I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

    Standard Seminars (English/German)

    Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

    • C++ – The Core Language
    • C++ – The Standard Library
    • C++ – Compact
    • C++11 and C++14
    • Concurrency with Modern C++
    • Design Pattern and Architectural Pattern with C++
    • Embedded Programming with Modern C++
    • Generic Programming (Templates) with C++
    • Clean Code with Modern C++
    • C++20

    Online Seminars (German)

    Contact Me

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

    Your email address will not be published. Required fields are marked *