Chapter 4: Namespaces

4.1: Namespaces

Imagine a math teacher who wants to develop an interactive math program. For this program functions like cos, sin, tan etc. are to be used accepting arguments in degrees rather than arguments in radians. Unfortunately, the function name cos is already in use, and that function accepts radians as its arguments, rather than degrees.

Problems like these are usually solved by defining another name, e.g., the function name cosDegrees is defined. C++ offers an alternative solution through namespaces. Namespaces can be considered as areas or regions in the code in which identifiers may be defined. Identifiers defined in a namespace normally won't conflict with names already defined elsewhere (i.e., outside of their namespaces). So, a function cos (expecting angles in degrees) could be defined in a namespace Degrees. When calling cos from within Degrees you would call the cos function expecting degrees, rather than the standard cos function expecting radians.

4.1.1: Defining namespaces

Namespaces are defined according to the following syntax:
    namespace identifier
    {
        // declared or defined entities
        // (declarative region)
    }

The identifier used when defining a namespace is a standard C++ identifier.

Within the declarative region, introduced in the above code example, functions, variables, structs, classes and even (nested) namespaces can be defined or declared. Namespaces cannot be defined within a function body. However, it is possible to define a namespace using multiple namespace declarations. Namespaces are `open' meaning that a namespace CppAnnotations could be defined in a file file1.cc and also in a file file2.cc. Entities defined in the CppAnnotations namespace of files file1.cc and file2.cc are then united in one CppAnnotations namespace region. For example:

    // in file1.cc
    namespace CppAnnotations
    {
        double cos(double argInDegrees)
        {
            ...
        }
    }

    // in file2.cc
    namespace CppAnnotations
    {
        double sin(double argInDegrees)
        {
            ...
        }
    }

Both sin and cos are now defined in the same CppAnnotations namespace.

Namespace entities can be defined outside of their namespaces. This topic is discussed in section 4.1.4.1.

4.1.1.1: Declaring entities in namespaces

Instead of defining entities in a namespace, entities may also be declared in a namespace. This allows us to put all the declarations in a header file that can thereupon be included in sources using the entities defined in the namespace. Such a header file could contain, e.g.,
    namespace CppAnnotations
    {
        double cos(double degrees);
        double sin(double degrees);
    }

4.1.1.2: A closed namespace

Namespaces can be defined without a name. Such an anonymous namespace restricts the visibility of the defined entities to the source file defining the anonymous namespace.

Entities defined in the anonymous namespace are comparable to C's static functions and variables. In C++ the static keyword can still be used, but its preferred use is in class definitions (see chapter 7). In situations where in C static variables or functions would have been used the anonymous namespace should be used in C++.

The anonymous namespace is a closed namespace: it is not possible to add entities to the same anonymous namespace using different source files.

4.1.2: Referring to entities

Given a namespace and its entities, the scope resolution operator can be used to refer to its entities. For example, the function cos() defined in the CppAnnotations namespace may be used as follows:
    // assume CppAnnotations namespace is declared in the
    // following header file:
    #include <cppannotations>

    int main()
    {
        cout << "The cosine of 60 degrees is: " <<
                CppAnnotations::cos(60) << '\n';
    }

This is a rather cumbersome way to refer to the cos() function in the CppAnnotations namespace, especially so if the function is frequently used. In cases like these an abbreviated form can be used after specifying a using declaration. Following

    using CppAnnotations::cos;  // note: no function prototype,
                                // just the name of the entity
                                // is required.

calling cos results in a call of the cos function defined in the CppAnnotations namespace. This implies that the standard cos function, accepting radians, is not automatically called anymore. To call that latter cos function the plain scope resolution operator should be used:

    int main()
    {
        using CppAnnotations::cos;
        ...
        cout << cos(60)         // calls CppAnnotations::cos()
            << ::cos(1.5)       // call the standard cos() function
            << '\n';
    }

A using declaration can have restricted scope. It can be used inside a block. The using declaration prevents the definition of entities having the same name as the one used in the using declaration. It is not possible to specify a using declaration for a variable value in some namespace, and to define (or declare) an identically named object in a block also containing a using declaration. Example:

    int main()
    {
        using CppAnnotations::value;
        ...
        cout << value << '\n';  // uses CppAnnotations::value
        int value;              // error: value already declared.
    }

4.1.2.1: The `using' directive

A generalized alternative to the using declaration is the using directive:
    using namespace CppAnnotations;

Following this directive, all entities defined in the CppAnnotations namespace are used as if they were declared by using declarations.

While the using directive is a quick way to import all the names of a namespace (assuming the namespace has previously been declared or defined), it is at the same time a somewhat dirty way to do so, as it is less clear what entity is actually used in a particular block of code.

If, e.g., cos is defined in the CppAnnotations namespace, CppAnnotations::cos is going to be used when cos is called. However, if cos is not defined in the CppAnnotations namespace, the standard cos function will be used. The using directive does not document as clearly as the using declaration what entity will actually be used. Therefore use caution when applying the using directive.

Namespace declarations are context sensitive: when a using namespace declaration is specified inside a compound statement then the declaration is valid until the compound statement's closing curly brace has been encountered. In the next example a string first is defined without explicit specifying std::string, but once the compound statement has ended the scope of the using namespace std declaration has also ended, and so std:: is required once again when defining second:

    #include <string>
    int main()
    {
        {
            using namespace std;
            string first;
        }
        std::string second;
    }

A using namespace directive cannot be used within the declaration block of a class- or enumeration-type. E.g., the following example won't compile:

    struct Namespace
    {
        using namespace std;      // won't compile
    };

4.1.2.2: `Koenig lookup'

If Koenig lookup were called the `Koenig principle', it could have been the title of a new Ludlum novel. However, it is not. Instead it refers to a C++ technicality.

`Koenig lookup' refers to the fact that if a function is called without specifying its namespace, then the namespaces of its argument types are used to determine the function's namespace. If the namespace in which the argument types are defined contains such a function, then that function is used. This procedure is called the `Koenig lookup'.

As an illustration consider the next example. The function FBB::fun(FBB::Value v) is defined in the FBB namespace. It can be called without explicitly mentioning its namespace:

    #include <iostream>

    namespace FBB
    {
        enum Value        // defines FBB::Value
        {
            FIRST
        };

        void fun(Value x)
        {
            std::cout << "fun called for " << x << '\n';
        }
    }

    int main()
    {
        fun(FBB::FIRST);    // Koenig lookup: no namespace
                            // for fun() specified
    }
    /*
        generated output:
    fun called for 0
    */
The compiler is rather smart when handling namespaces. If Value in the namespace FBB would have been defined as using Value = int then FBB::Value would be recognized as int, thus causing the Koenig lookup to fail.

As another example, consider the next program. Here two namespaces are involved, each defining their own fun function. There is no ambiguity, since the argument defines the namespace and FBB::fun is called:

    #include <iostream>

    namespace FBB
    {
        enum Value        // defines FBB::Value
        {
            FIRST
        };

        void fun(Value x)
        {
            std::cout << "FBB::fun() called for " << x << '\n';
        }
    }

    namespace ES
    {
        void fun(FBB::Value x)
        {
            std::cout << "ES::fun() called for " << x << '\n';
        }
    }

    int main()
    {
        fun(FBB::FIRST);    // No ambiguity: argument determines
                            // the namespace
    }
    /*
        generated output:
    FBB::fun() called for 0
    */

Here is an example in which there is an ambiguity: fun has two arguments, one from each namespace. The ambiguity must be resolved by the programmer:

    #include <iostream>

    namespace ES
    {
        enum Value        // defines ES::Value
        {
            FIRST
        };
    }

    namespace FBB
    {
        enum Value        // defines FBB::Value
        {
            FIRST
        };

        void fun(Value x, ES::Value y)
        {
            std::cout << "FBB::fun() called\n";
        }
    }

    namespace ES
    {
        void fun(FBB::Value x, Value y)
        {
            std::cout << "ES::fun() called\n";
        }
    }

    int main()
    {
        //  fun(FBB::FIRST, ES::FIRST); ambiguity: resolved by
        //                              explicitly mentioning
        //                              the namespace
        ES::fun(FBB::FIRST, ES::FIRST);
    }
    /*
        generated output:
    ES::fun() called
    */

An interesting subtlety with namespaces is that definitions in one namespace may break the code defined in another namespace. It shows that namespaces may affect each other and that namespaces may backfire if we're not aware of their peculiarities. Consider the following example:

    namespace FBB
    {
        struct Value
        {};

        void fun(int x);
        void gun(Value x);
    }

    namespace ES
    {
        void fun(int x)
        {
            fun(x);
        }
        void gun(FBB::Value x)
        {
            gun(x);
        }
    }
Whatever happens, the programmer'd better not use any of the functions defined in the ES namespace, since that would result in infinite recursion. However, that's not the point. The point is that the programmer won't even be given the opportunity to call ES::fun since the compilation fails.

Compilation fails for gun but not for fun. But why is that so? Why is ES::fun flawlessly compiling while ES::gun isn't? In ES::fun fun(x) is called. As x's type is not defined in a namespace the Koenig lookup does not apply and fun calls itself with infinite recursion.

With ES::gun the argument is defined in the FBB namespace. Consequently, the FBB::gun function is a possible candidate to be called. But ES::gun itself also is possible as ES::gun's prototype perfectly matches the call gun(x).

Now consider the situation where FBB::gun has not yet been declared. Then there is of course no ambiguity. The programmer responsible for the ES namespace is resting happily. Some time after that the programmer who's maintaining the FBB namespace decides it may be nice to add a function gun(Value x) to the FBB namespace. Now suddenly the code in the namespace ES breaks because of an addition in a completely other namespace (FBB). Namespaces clearly are not completely independent of each other and we should be aware of subtleties like the above. Later in the C++ Annotations (chapter 11) we'll return to this issue.

Koenig lookup is only used in the context of namespaces. If a function is defined outside of a namespace, defining a parameter of a type that's defined inside a namespace, and that namespace also defines a function with an identical signature, then the compiler reports an ambiguity when that function is called. Here is an example, assuming the abovementioned namespace FBB is also available:

    void gun(FBB::Value x);

    int main(int argc, char **argv)
    {
        gun(FBB::Value{});          // ambiguity: FBB::gun and ::gun can both
                                    // be called.
    }

4.1.3: The standard namespace

The std namespace is reserved by C++. The standard defines many entities that are part of the runtime available software (e.g., cout, cin, cerr); the templates defined in the Standard Template Library (cf. chapter 18); and the Generic Algorithms (cf. chapter 19) are defined in the std namespace.

Regarding the discussion in the previous section, using declarations may be used when referring to entities in the std namespace. For example, to use the std::cout stream, the code may declare this object as follows:

    #include <iostream>
    using std::cout;

Often, however, the identifiers defined in the std namespace can all be accepted without much thought. Because of that, one frequently encounters a using directive, allowing the programmer to omit a namespace prefix when referring to any of the entities defined in the namespace specified with the using directive. Instead of specifying using declarations the following using directive is frequently encountered: construction like

    #include <iostream>
    using namespace std;

Should a using directive, rather than using declarations be used? As a rule of thumb one might decide to stick to using declarations, up to the point where the list becomes impractically long, at which point a using directive could be considered.

Two restrictions apply to using directives and declarations:

4.1.4: Nesting namespaces and namespace aliasing

Namespaces can be nested. Here is an example:
    namespace CppAnnotations
    {
        int value;
        namespace Virtual
        {
            void *pointer;
        }
    }

The variable value is defined in the CppAnnotations namespace. Within the CppAnnotations namespace another namespace (Virtual) is nested. Within that latter namespace the variable pointer is defined. To refer to these variable the following options are available:

Following a using namespace directive all entities of that namespace can be used without any further prefix. If a single using namespace directive is used to refer to a nested namespace, then all entities of that nested namespace can be used without any further prefix. However, the entities defined in the more shallow namespace(s) still need the shallow namespace's name(s). Only after providing specific using namespace directives or using declarations namespace qualifications can be omitted.

When fully qualified names are preferred but a long name like

    CppAnnotations::Virtual::pointer

is considered too long, a namespace alias may be used:

    namespace CV = CppAnnotations::Virtual;

This defines CV as an alias for the full name. The variable pointer may now be accessed using:

    CV::pointer = 0;

A namespace alias can also be used in a using namespace directive or using declaration:

    namespace CV = CppAnnotations::Virtual;
    using namespace CV;

Nested namespace definitions

Starting with the C++17 standard, when nesting namespaces a nested namespace can directly be referred to using scope resolution operators. E.g.,

    namespace Outer::Middle::Inner
    { 
        // entities defined/declared here are defined/declared in the Inner
        // namespace, which is defined in the Middle namespace, which is
        // defined in the Outer namespace
    }

4.1.4.1: Defining entities outside of their namespaces

It is not strictly necessary to define members of namespaces inside a namespace region. But before an entity is defined outside of a namespace it must have been declared inside its namespace.

To define an entity outside of its namespace its name must be fully qualified by prefixing the member by its namespaces. The definition may be provided at the global level or at intermediate levels in the case of nested namespaces. This allows us to define an entity belonging to namespace A::B within the region of namespace A.

Assume the type int INT8[8] is defined in the CppAnnotations::Virtual namespace. Furthermore assume that it is our intent to define a function squares, inside the namespace
CppAnnotations::Virtual returning a pointer to CppAnnotations::Virtual::INT8.

Having defined the prerequisites within the CppAnnotations::Virtual namespace, our function could be defined as follows (cf. chapter 9 for coverage of the memory allocation operator new[]):

    namespace CppAnnotations
    {
        namespace Virtual
        {
            void *pointer;

            using INT8 = int[8];

            INT8 *squares()
            {
                INT8 *ip = new INT8[1];

                for (size_t idx = 0; idx != sizeof(INT8) / sizeof(int); ++idx)
                    (*ip)[idx] = (idx + 1) * (idx + 1);

                return ip;
            }
        }
    }

The function squares defines an array of one INT8 vector, and returns its address after initializing the vector by the squares of the first eight natural numbers.

Now the function squares can be defined outside of the CppAnnotations::Virtual namespace:

    namespace CppAnnotations
    {
        namespace Virtual
        {
            void *pointer;

            using INT8 = int[8];

            INT8 *squares();
        }
    }

    CppAnnotations::Virtual::INT8 *CppAnnotations::Virtual::squares()
    {
        INT8 *ip = new INT8[1];

        for (size_t idx = 0; idx != sizeof(INT8) / sizeof(int); ++idx)
            (*ip)[idx] = (idx + 1) * (idx + 1);

        return ip;
    }

In the above code fragment note the following:

Finally, note that the function could also have been defined in the CppAnnotations region. In that case the Virtual namespace would have been required when defining squares() and when specifying its return type, while the internals of the function would remain the same:

    namespace CppAnnotations
    {
        namespace Virtual
        {
            void *pointer;

            using INT8 = int[8];

            INT8 *squares();
        }

        Virtual::INT8 *Virtual::squares()
        {
            INT8 *ip = new INT8[1];

            for (size_t idx = 0; idx != sizeof(INT8) / sizeof(int); ++idx)
                (*ip)[idx] = (idx + 1) * (idx + 1);

            return ip;
        }
    }

4.2: The std::chrono namespace (handling time)

The C programming language offers tools like sleep(3) and select(2) to suspend program execution for a certain amount of time. And of course the family of time(3) functions for setting and displaying time

Sleep and select can be used for waiting, but as they were designed in an era when multi threading was unavailable, their usefulness is limited when used in multi threaded programs. Multi threading has become part of C++ (covered in detail in chapter 20), and additional time-related functions are available in the std::filesystem namespace, covered below in this chapter.

In multi threaded programs threads are frequently suspended, albeit usually for a very short time. E.g., when a thread wants to access a variable, but the variable is currently being updated by another thread, then the former thread should wait until the latter thread has completed the update. Updating a variable usually doesn't take much time, but if it takes an unexpectedly long time, then the former thread may want to be informed about that, so it can do something else while the latter thread is busy updating the variable. Interactions between threads like these cannot be realized with functions like sleep and select.

The std::chrono namespace bridges the gap between the traditionally available time-related functions and the time-related requirements of multi-threading and of the std::filesystem name space. All but the specific std::filesystem related time functionality is available after including the <chrono> header file. After including the <filesystem> header file the facilities of the std::filesystem are available.

Time can be measured in various resolutions: in Olympic Games time differences of hundreds of seconds may make the distinction between gold and silver medals, but when planning a vacation we might talk about it months before we go on vacation. Time resolutions are specified by objects of the class std::ratio, which (apart from including the <chrono> header file) is also available after including the <ratio> header file.

Different events usually last for different amounts of time (given a specific time resolution). Amounts of time are specified by objects of the class std::chrono::duration.

Events can also be characterized by their points in time: midnight, January 1, 1970 GMT is a point in time, as is 19:00, December 5, 2010. Points in time are specified by objects of the class std::chrono::time_point.

It's not just that resolutions, durations of events, and points in time of events may differ, but the devices (clocks) we use for specifying time also differ. In the past hour glasses were used (and sometimes they're still used when boiling eggs), but on the other hand we may use atomic clocks when measurements should be very precise. Four different types of clocks are available. The commonly used clock is std::chrono::system_clock, but in the context of the file system there's also a std::chrono::file_clock.

In the upcoming sections the details of the std::chrono namespace are covered. First we look at characteristics of time resolutions. How to handle amounts of time given their resolutions is covered next. The next section describes facilities for defining and handling time-points. The relationships between these types and the various clock-types are covered thereafter.

In this chapter the specification std::chrono:: is often omitted (in practice using namespace std followed by using namespace chrono is commonly used; [std::]chrono:: specifications are occasionally used to avoid ambiguities). Also, every now and then you'll encounter forward references to later chapters, like the reference to the chapter about multi-threading. These are hard to avoid, but studying those chapters at this point can safely be postponed without loss of continuity.

4.2.1: Time resolutions: std::ratio

Time resolutions (or units of time) are essential components of time specifications. Time resolutions are defined by objects of the class std::ratio.

Before the class ratio can be used, the <ratio> header file must be included. But the frquently included <chrono> header file already includes the ratio header file.

The class ratio has two template arguments. These are positive integral numbers surrounded by pointed brackets defining, respectively, the numerator and denominator of a fraction (by default the denominator equals 1). Examples:

    ratio<1>        - representing one; 
    ratio<60>       - representing 60
    ratio<1, 1000>  - representing 1/1000.

The class ratio defines two directly accessible static data members: num represents its numerator, den its denominator. A ratio definition by itself simply defines a certain amount. E.g., when executing the following program

    #include <ratio>
    #include <iostream>
    using namespace std;

    int main()
    {
        cout << ratio<5, 1000>::num << ',' << ratio<5, 1000>::den << '\n';
    }

1,200 is displayed, as that's the `amount' represented by ratio<5, 1000>: ratio simplifies its fraction whenever possible.

Several predefined ratio types exist. They are, like ratio itself, defined in the standard namespace and can be used instead of the more cumbersome ratio<x> or ratio<x, y> specifications:


yocto 10-24
zepto 10-21

atto 10-18 femto 10-15 pico 10-12
nano 10-9 micro 10-6 milli 10-3
centi 10-2 deci 10-1

deca 101 hecto 102 kilo 103
mega 106 giga 109 tera 1012
peta 1015 exa 1018

zetta 1021 yotta 1024

(note: the definitions of the types yocto, zepto, zetta and yotta use integral constants exceeding 64 bits. Although these constants are defined in C++, they are not available on 64 bit or smaller architectures.)

Time related ratios can very well be interpreted as fractions or multiple of seconds, with ratio<1, 1> representing a resolution of one second.

Here is an example showing how these abbreviations can be used:

        cout << milli::num << ',' << milli::den << '\n' <<
                kilo::num << ',' << kilo::den << '\n';

4.2.2: Amounts of time: std::chrono::duration

Amounts of time are specified by objects of the class std::chrono::duration.

Before using the class duration the <chrono> header file must be included.

The class duration has two template arguments. A numeric type (int64_t is normally used) defining the type holding the duration's amount of time, and a time-resolution ratio (called its resolution).

Often the following predefined duration types are used:


predefined:   duration type

nanoseconds   duration<int64_t, nano>
microseconds   duration<int64_t, micro>
milliseconds   duration<int64_t, milli>
seconds   duration<int64_t>
minutes   duration<int64_t, ratio<60>>
hours   duration<int64_t, ratio<3600>>

E.g., to define a duration of 30 minutes use minutes halfHour{ 30 }.

The suffixes h, min, s, ms, us, ns are available for integral values, creating the corresponding duration times. E.g., minutes min = 1h stores 60 minutes in min.

Sub-types:

Constructors:

Copy- and move-constructors (cf. chapter 9) are available.

Operators:

Duration types support assignment, negation (e.g., halfHour = -halfHour), addition and subtraction of duration values, and also multiplication, division and modulo computations by numeric factors. Compound assignment operators are also available. E.g.,

    minutes time = 2 * halfHour;    // time: 60 minutes
    time += minutes{ 30 };          // time: 90 minutes 

Members:

Conversions:

The precision of left-hand side arguments of assignment operators must be greater than or equal to the precision of the right-hand side arguments (the left-hand argument may not lose precision). When using binary arithmetic operators the resulting duration type has a precision equal to the finer of the two precisions. E.g.,

    cout << (1min + 1h).count() << '\n';  // shows: 61
    hours hr{ 1 };
    halfHour += hr;                 // OK
    // hr += halfHours;             // won't compile 

Durations can be converted by the std::chrono::duration_cast<destination>(source) function template, where destination is the destination's duration type and source is an available destination object, but duration_cast truncates the destination value when destination's precision is less than source's precision. E.g., duration_cast<minutes>(seconds{ 90 }) returns minutes{ 1 }.

When the left-hand side argument's precision is less than the right-hand side's precision some decision must be made about how to handle the loss of precision. The following function template can be used to convert a duration to a less precise type, returning a double value, which can be, e.g., truncated or rounded.

    template <typename To, typename From>
    double durationCast(From from)
    {
        return static_cast<double>(from.count()) * 
                            To::period::den * From::period::num /
                            (To::period::num * From::period::den);
    } 

returning a double value (1.5 when called as durationCast<minutes>(seconds{ 90 })), leaving the decision how to use the returned double value to the caller.

4.2.3: Clocks measuring time

Clocks are used to measure time. C++ has several predefined clock types, defined in the std::chrono namespace.

Before using the chrono clocks the <chrono> header file must be included.

`Clock' clock-types are used to define points in time and define the following sub-types:

All clocks have a member now returning the clock type's time_point corresponding to the current time (relative to the clock's epoch). It is a static member which can be used like this: system_clock::time_point tp = system_clock::now().

Four clock types are defined in the chrono namespace:

In addition to the member now the classes system_clock and high_resolution_clock (referred to as Clock below) provide two static members:

For example:

    system_clock::time_point now = system_clock::now();
    time_t value = system_clock::to_time_t(now);
    system_clock::time_point now2 = system_clock::from_time_t(value);

    // now2 != now since time_t uses seconds, not nanoseconds    

4.2.4: Points in time: std::chrono::time_point

Points in time are specified by objects of the class std::chrono::time_point.

Before using the class time_point the <chrono> header file must be included.

Like duration the class time_point requires two template arguments: A clock type and a duration type. The standard clock types define their own time_point types using nanoseconds as their duration type (time_point by default uses nanoseconds). The following time point type definitions are therefore identical:

    time_point<standard_clock, nanoseconds>
    time_point<standard_clock>
    standard_clock::time_point 

Constructors:

Copy- and move-constructors as well as copy- and move-assignment operators (cf. chapter 9) are available

Operators:

The compound operators are also available as binary arithmetic operators using a time_point const & and a duration const & operand (in any order). Example:

    system_clock::now() + seconds{ 5 };

Members:

Conversions:

All predefined clocks use nanoseconds for their duration types. Converting to different duration types is covered in the previous section (4.2.2). Alternatively, the members to_time_t of the system_clock and the high_resolution_clock can be used to convert their time_point values to time_t. Such time_t values are commonly used to convert time to text, e.g., using the manipulator put_time (cf. section 6.3.2). The put_time manipulator must be provided with the address of a std::tm object, which can be obtained from a std::time_t value. See figure 3 for a visual representation of the involved elements.

Figure 3: Time according to C++

Use the following blueprint to insert a system_clock::time point value into a std::ostream (cf. section 6.4.4):

    int main()
    {
        auto now = system_clock::now();         // get a time_point
        cout << now << '\n';                    // now's UTC time
                                                // convert to a time_t
        time_t timeT = system_clock::to_time_t(now);
        tm *local = localtime(&timeT);          // get a tm *
        cout << put_time(local, "%c") << '\n';  // show the time
    }

To convert a time_point to another std::chrono clock's time_point the template DestClock::time_point std::chrono::clock_cast<DestClock>(TimePoint tp) can be used. The argument tp is an available time_point, and DestClock specifies the clock type of the returned time_point. E.g., the following cout statement displays four identical times:

    auto fNow = file_clock::now();
    auto sNow = file_clock::to_sys(fnow);

    cout << fNow << '\n' <<
        sNow << '\n' <<
        clock_cast<system_clock>(fNow) << '\n' <<
        clock_cast<file_clock>(sNow) << '\n'; 

So, where is the starting point of the file_clock clock?
Since time_point's default cconstructor is initialized to its clock's starting point it can be obtained this way:

    cout << file_clock::time_point{}<< '\n';
    // shows:  2174-01-01 00:00:00.000000000 

4.3: The std::filesystem namespace

Computers commonly store information that must survive reboots in their file systems. Traditionally, to manipulate the file system the C programming language offers functions performing the required system calls. Such functions (like rename(2), truncate(2), opendir(2), and realpath(3)) are of course also available in C++, but their signatures and way of use are often less attractive as they usually expect char const * parameters and may use static buffers or memory allocation based on malloc(3) and free(3).

Since 2003 the Boost library offers wrappers around these functions, offering interfaces to those system calls that are more C++-like.

Currently C++ directly supports these functions in the std::filesystem namespace. These facilities can be used after including the <filesystem> header file.

The filesystem namespace is extensive: it contains more than 10 different classes, and more than 30 free functions. To refer to the identifiers defined in the std::filesystem namespace their fully qualified names (e.g., std::filesystem::path) can be used. Alternatively, after specifying `using namespace std::filesystem;' the identifiers can be used without further qualifications. Namespace specifications like `namespace fs = std::filesystem;' are also encountered, allowing specifications like fs::path.

Functions in the filesystem namespace may fail. When functions cannot perform their assigned tasks they may throw exceptions (cf. chapter 10) or they may assign values to error_code objects that are passed as arguments to those functions (see section 4.3.1 below).

4.3.1: The class 'error_code'

Objects of the class std::error_code (note: not std::filesystem::error_code!) encapsulate error values, and associated error categories (cf. section 10.9; error_code can be used after including the <system_error> header, but it is also available after including the <filesystem> header file). Traditionally error values are available as values assigned to the global int errno variable. By convention, when errno's value equals zero there's no error. This convention was adopted by error_code.

Error codes can be defined for many conceptually different situations. Those situations are characterized by their own error categories.

Error categories are used to associate error_code objects with the errors that are defined by those categories. Default available error categories may use values like EADDRINUSE (or the equivalent enum class errc value address_in_use) but new types of error categories, tailored to other contexts, can also be defined. The enum class errc is defined in the <bits/error_constants.h> header file. Error categories and how to define them is covered near the end of the C++ Annotations (section 23.7.1).

Constructors:

Copy- and move-constructors are available.

Operators:

Members:

Free functions:

Many filesystem functions define an optional last error_code &ec parameter. Those functions have noexcept specifications. If those functions cannot complete their tasks, then ec is set to the appropriate error code, calling ec.clear() if no error was encountered. If no ec argument is provided then those functions throw a filesystem_error exception if they cannot complete their tasks.

4.3.2: Types and permissions of file system elements: file_status

File system entries (represented by path objects, cf. section 4.3.3 below), have attributes: permissions (e.g., the owner may modify an entry), and types (like files, directories, and soft-links). Those attributes are defined in the class std::filesystem::file_status.

Types:

Defined in the (std::filesystem) enum class file_type.


std::filesystem::file_type
Type
Meaning

not_found (= -1)
the file system entry was not found
(not considered an error)
none
the file status has not yet been evaluated
or an error occurred when evaluating the status
regular
a regular file
directory
a directory
symlink
a symbolic link
block
a block device
character
a character device
fifo
a named pipe
socket
a socket file
unknown
an unknown file type

Permissions:

Defined in the (std::filesystem) enum class perms.

The enumeration's symbols were defined to make their meanings more descriptive than the constants defined in the <sys/stat.h> header file, but their values are identical. The enum class perms supports all bitwise operators.


std::filesystem::perms
Symbol   Value sys/stat.h
Meaning

none
0000
   ---
No permission bits were set
owner_read
0400
S_IRUSR
File owner has read permission
owner_write
0200
S_IWUSR
File owner has write permission
owner_exec
0100
S_IXUSR
File owner has execute/search
permissions
owner_all
0700
S_IRWXU
File owner has read, write, and
execute/search permissions
group_read
0040
S_IRGRP
The file's group has read permission
group_write
0020
S_IWGRP
The file's group has write permission
group_exec
0010
S_IXGRP
The file's group has execute/search
permissions
group_all
0070
S_IRWXG
The file's group has read, write, and
execute/search permissions
others_read
0004
S_IROTH
Other users have read permission
others_write
0002
S_IWOTH
Other users have write permission
others_exec
0001
S_IXOTH
Other users have execute/search
permissions
others_all
0007
S_IRWXO
Other users have read, write, and
execute/search permissions
all
0777
   ---
All users have read, write, and
execute/search permissions
set_uid
04000
S_ISUID
Set user ID to file owner user ID on
execution
set_gid
02000
S_ISGID
Set group ID to file's user group ID on
execution
sticky_bit
01000
S_ISVTX
POSIX XSI specifies that when set on a
directory only file owners may delete
files even if the directory is writeable
by others (used, e.g., with /tmp)
mask
07777
   ---
All valid permission bits.

Perm_options:

The (std::file_system) enum class perm_options specifies the way file system entries are modified


std::filesystem::perm_options
Value
Meaning

replace
current permisions are replaced new permissions
remove
the specified permissions are removed from the
file system entry's current permissions
nofollow
when the current file system entry refers
to a symbolic link the permissions of the
symbolic link itself are updated

Constructors:

Copy and move constructors and assignment operators are avaialble;

Members:

4.3.2.1: The status of file system entries

The std::path free functions status and symlink_status (cf. section 4.3.3.2) return the file_status of file system entries.

The following functions provide specific information about the status of file system entries:

These following is_WHATEVER functions are available:


is_WHATEVER(file_status)
Function
Meaning

is_block_file
'entry' is a block device
is_character_file
'entry' is a character device
is_directory
'entry' is a directory
is_empty
'entry' is an empty file or directory
is_fifo
'entry' is a named pipe
is_other
'entry' is not a directory,
regular file or symlink
is_regular_file
'entry' is a regular file
is_socket
'entry' is a named socket
is_symlink
'entry' is a symbolic link

Here is a small program showing how file statuses can be obtained and displayed (cf. sections 4.3.3 and (for the map) section 12.3.7):

    namespace
    {
        std::unordered_map<file_type, char const *> statusMap =
        {
            { file_type::not_found, "an unknown file" },
            { file_type::none,      "not yet or erroneously evaluated "
                                                                "file type" },
            { file_type::regular,   "a regular file" },
            { file_type::directory, "a directory" },
            { file_type::symlink,   "a symbolic link" },
            { file_type::block,     "a block device" },
            { file_type::character, "a character device" },
            { file_type::fifo,      "a named pipe" },
            { file_type::socket,    "a socket file" },
            { file_type::unknown,   "an unknown file type" }
        };
    }
    
    int main()
    {
        cout << oct;
    
        string line;
        while (true)
        {
            cout << "enter the name of a file system entry: ";
            if (not getline(cin, line) or line.empty())
                break;
    
            path entry{ line };
    
            error_code ec;
            file_status stat = status(entry, ec);
    
            if (not status_known(stat))
            {
                cout << "status of " << entry << " is unknown. "
                                                "Ec = " << ec << '\n';
                continue;
            }
    
            cout << "status of " << entry << ": type = " <<
                     statusMap[stat.type()] <<
                     ", permissions: " <<
                        static_cast<size_t>(stat.permissions()) << '\n';
        }
    }

4.3.3: Names of file system entries: path

Objects of the class filesysten::path commonly contain names of file system entries.

Constructors:

Copy- and move constructors are available, also using argument types std::string and char const *.
A path object doesn't have to refer to an existing file system entry.
Arguments of path constructors may contain (all optional):

The constructors also define a last format ftmp = auto_format parameter, for which in practice almost never an argument has to be provided (for its details see cppreference.)

4.3.3.1: Accessors, modifiers and operators

Operators:

Accessors:

Accessors return specific path components. If a path doesn't contain the requested component then an empty path is returned.

Except for the family of string() and the is_... accessors, there are also bool has_... members returning true if the path contains the specified component (e.g., has_extension returns true if the path contains an extension).

Member functions:

4.3.3.2: Free functions

Copy_options:

Defined in the (std::filesystem) enum class copy_options.

The enumeration supports bitwise operators. The copy_options enum values are used to fine-tune the behavior of filesystem functions copying file system elements.


std::filesystem::copy_options
Symbol
Value
Meaning

none
0
   
default: copy like cp
overwrite_existing
2
   
replace the destination file
update_existing
4
   
replace the destination file only if it is
older than the file being copied
recursive
8
   
recursively copy subdirectories and their
content
copy_symlinks
16
   
copy symlinks as symlinks
skip_symlinks
32
   
ignore symlinks
directories_only
64
   
copy the directory structure, but do not copy
any non-directory files
create_symlinks
128
   
create symlinks instead of copying files.
The source path must be an absolute path unless
the destination path is in the current directory
create_hard_links
256
   
instead of copying files create hard links
resolving to the same files as the original

Functions:

4.3.4: Handling directories: directory_entry

The file system is a recursive data structure. Its top-level entry is a directory (the root directory) containing plain directory entries (files, (soft) links, named sockets, etc.) and possibly also (sub)directory entries referring to nested directories which in turn may contiain plain- and (sub)directory entries.

In the std::filesystem namespace the elements of directories are objects of the class directory_entry, containing names and statuses of the entries of that directory.

Constructors:

The class directory_entry supports all standard constructors (and assignment operators) and also a constructor expecting a path:

    directory_entry(path const &entry);

Objects of the class directory_entry can be constructed by name, and do not have to refer to existing entries in the file system.

Operators:

Members:

4.3.4.1: Visiting directory entries: (recursive_)directory_iterator

The filesystem namespace has two classes simplifying directory processing: objects of the class directory_iterator are (input) iterators iterating over the entries of directories; and objects of the class recursive_directory_iterator are (input) iterators recursively visiting all entries of directories.location

enum class directory_options

The enum class directory_options defines values that are used to fine-tune the behavior of recursive_directory_iterator objects, supporting bitwise operators (the values of its symbols are shown between parentheses):

Constructors:

Copy, and move constructors are available.

These iterators point to directory_entry objects referring to entries in the computer's file system. E.g.,

    cout << *directory_iterator{ "/home" } << '\n'; 
                // shows the first entry under /home 

(Recursive_)directory_iterators can be used with range-based for-loops and in standard for and while statements.

Additional members of recursive_directory_iterators

Here's a small program displaying all elements and all immediate sub-directories of a directory:

    int main()
    {
        recursive_directory_iterator base{ "/var/log",
                                      directory_options::skip_permission_denied };
    
        for (auto entry = base, endIt = end(base); entry != endIt; ++entry)
        {
            cout << entry.depth() << ": " << *entry << '\n';
            if (entry.depth() == 1)
                entry.disable_recursion_pending();
        }
    }

The above program handles entries as they come. Other strategies must be implemented `by hand'. E.g., a breadth-first strategy first visits all the non-directory entries and then visits the sub-directories, as illustrated in the next example by processingthe directories stored in level in turn (initially it merely contains the starting directory).

`Processing a directory' means that its non-directory entries are directly processed storing the names of sub-directories in next. Once all entries at level have been processed the names of the next level sub-directories are available (in next). By assigning next to level all directories at the next level are processed. When reaching the most deeply nested sub-directories its next is empty and the while statement ends:

    void breadth(path const &dir)           // starting dir.
    {
        vector<path> level{ dir };          // currently processed level
    
        while (not level.empty())           // process all its dirs.
        {
            vector<path> next;              // dirs of the next level
    
            for (auto const &dir: level)    // visit all dirs at this level
            {
                cout << "At " << dir << '\n';
                                            // at each dir: visit all entries
                for (auto const &entry: directory_iterator{ dir })
                {
                    if (entry.is_directory())   // store all dirs at the current
                        next.push_back(entry);  //  level
                    else                        // or process its non-dir entry
                        cout << "   entry: " << entry << '\n';
                }
            }
    
            level = next;                   // continue at the next level,
        }                                   // which eventually won't exist
    }

4.3.5: Information about the space of file systems: space_info

Every existing path lives in a file system, Sizes of file systems typically are quite large, but there is a limit to their sizes.

The size of file systems, the number of bytes that is currently being used and the remaining number of bytes is made available by the function space(path const &entry [, error_code &ec]), returning the information about the file system containing entry in a POD struct space_info.

If the error_code argument is provided then it is cleared if no error occurs, and set to the operating system's error code if an error has occurred. If an error occurs and the error_code argument was not provided then a filesystem_error exception is thrown, receiving path as its first argument and the operating system's error code as its error_code argument.

The returned space_info has three fields:

    uintmax_t capacity;     // total size in bytes
    uintmax_t free;         // number of free bytes on the file system
    uintmax_t available;    // free bytes for a non-privileged process

If a field cannot be determined it is set to -1 (i.e., the max. value of the type uintmax_t).

The function can be used this way:

    int main()
    {
        path tmp{ "/tmp" };
    
        auto pod = space(tmp);
    
        cout << "The filesystem containing /tmp has a capacity of " <<
                                                pod.capacity << " bytes,\n"
            "i.e., " << pod.capacity / (1024 * 1024) << " MB.\n"
            "# free bytes: " << pod.free << "\n"
            "# available:  " << pod.available << "\n"
            "free + available:  " << pod.free + pod.available << '\n';
    }

4.3.6: File system exceptions: filesystem_error

The std::filesystem namespace offers its own exception type filesystem_error (see also chapter 10). Its constructor has the following signature (the bracketed parameters are optional):
    filesystem_error(string const &what, 
                    [path const &path1, [path const &path2,]] 
                    error_code ec);

As filesystem facilities are closely related to standard system functions, errc error code enumeration values can be used to obtain error_codes to pass to filesystem_error, as illustrated by the following program:

    int main()
    try
    {
        try
        {
            throw filesystem_error{ "exception encountered", "p1", "p2",
                                        make_error_code(errc::address_in_use) };
        }
        catch (filesystem_error const &fse)
        {
            cerr << "what:  " << fse.what() << "\n"
                    "path1: " << fse.path1() << "\n"
                    "path2: " << fse.path2() << "\n"
                    "code:  " << fse.code() << '\n';
    
            throw;
        }
    }
    catch (exception const &ec)
    {
        cerr << "\n"
                "plain exception's what: " << ec.what() << "\n\n";
    }