User-Defined Literals

Contents[Show]

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 den 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 have to 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 key 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 have a 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.

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

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 suffix and use 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 is the killer feature in modern C++, if you want to write safety critical software. Why? Thanks to the automatic mapping of 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 in average per week? The question has occupied me for a long time.

Type safe 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 tow reasons very likely. First, the suffixes are usually very short; second, the suffixes usually stand for units which already established abbreviations. I used in the program the suffixes km, m, dm und cm.

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

average

I display in the lines 12 - 15 the various distances; I calculate in the 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 meter will I drive in average a week? For convenience I define a few constants: work, workPerDay, abbrevationToWork, and shopping. These are my building blocks for the 4 weeks (line 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 in 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. The line 28 is equivalent to Distance::MyDistance work= 63.0_km;

 

arithmetik

 

If I use 1.5_km + 105.1_m in the source code, the following steps will automatically happen. The compiler maps at first the suffixes km and m to the corresponding literal operators; at 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 as part of his contract. This means in this concrete case that 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 (line 15 - 28) and the output operator (line 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 (line 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 argument a long double and return a MyDistance object. MyDistance is automatically normalized to meters. And now? That's was the whole functionality that programmer has to provide.

I totally 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 have to declare the operations and objects as constexpr respectively. I will present this feature in the post constant expression.

What's next?

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

 

 

 

 

 

 

 

 

title page smalltitle page small Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library".   Get your e-book. Support my blog.

 

Add comment


My Newest E-Book

Latest comments

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 559

All 255864

Currently are 361 guests and no members online