Movatterモバイル変換


[0]ホーム

URL:




Chapter 4: Namespaces

4.1: Namespaces

Imagine a math teacher who wants to develop an interactive math program. Forthis program functions likecos, sin, tan etc. are to be usedaccepting arguments in degrees rather than arguments inradians. Unfortunately, the function namecos is already in use, and thatfunction accepts radians as its arguments, rather than degrees.

Problems like these are usually solved by defining another name, e.g., thefunction namecosDegrees is defined.C++ offers an alternativesolution throughnamespaces. Namespaces can be considered asareas or regions in the code in which identifiers may be defined. Identifiersdefined in a namespace normally won't conflict with names already definedelsewhere (i.e., outside of their namespaces). So, a functioncos(expecting angles in degrees) could be defined in a namespaceDegrees. When callingcos from withinDegrees you would call thecos function expecting degrees, rather than the standardcos functionexpecting 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 standardC++identifier.

Within thedeclarative region, introduced in the above code example,functions, variables, structs, classes and even (nested) namespaces can bedefined or declared. Namespaces cannot be defined within a functionbody. However, it is possible to define a namespace using multiplenamespace declarations. Namespaces are `open' meaning thata namespaceCppAnnotations could be defined in a filefile1.cc andalso in a filefile2.cc. Entities defined in theCppAnnotationsnamespace of filesfile1.cc andfile2.cc are then united in oneCppAnnotations namespace region. For example:

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

Bothsin andcos are now defined in the sameCppAnnotations namespace.

Namespace entities can be defined outside of their namespaces. Thistopic is discussed in section4.1.4.1.

4.1.1.1: Declaring entities in namespaces

Instead ofdefining entities in a namespace, entities may also bedeclared in a namespace. This allows us to put all the declarations in a header file that canthereupon be included in sources using the entities defined in thenamespace. 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 ananonymous namespace restricts the visibility of the defined entities to thesource file defining the anonymous namespace.

Entities defined in the anonymous namespace are comparable toC'sstatic functions and variables. InC++ thestatic keyword canstill be used, but its preferred use is inclass definitions (seechapter7). In situations where inC static variables orfunctions would have been used the anonymous namespace should be used inC++.

The anonymous namespace is a closed namespace: it is notpossible to add entities to the same anonymous namespace using differentsource files.

4.1.2: Referring to entities

Given a namespace and its entities, thescope resolution operator can beused to refer to its entities. For example, the functioncos()defined in theCppAnnotations 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 thecos() function in theCppAnnotations namespace, especially so if the function is frequentlyused. In cases like these anabbreviated form can beused after specifying ausing declaration. Following

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

callingcos results in a call of thecos function defined in theCppAnnotations namespace. This implies that the standardcosfunction, accepting radians, is not automatically called anymore. To call thatlattercos function the plainscope 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';    }

Ausing declaration can have restricted scope. It can be used inside ablock. Theusing declaration prevents the definition of entities havingthe same name as the one used in theusing declaration. It is not possibleto specify ausing declaration for a variablevalue in some namespace,and to define (or declare) an identically named object in a block alsocontaining ausing 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 theusing declaration is theusing directive:
    using namespace CppAnnotations;

Following this directive,all entities defined in theCppAnnotations namespace are used as if they were declared byusingdeclarations.

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

If, e.g.,cos is defined in theCppAnnotations namespace,CppAnnotations::cos is going to be used whencos is called. However,ifcos isnot defined in theCppAnnotations namespace, thestandardcos function will be used. Theusing directive does notdocument as clearly as theusing declaration what entity will actually beused. Therefore use caution when applying theusing directive.

Namespace declarations are context sensitive: when ausing namespacedeclaration is specified inside a compound statement then the declaration isvalid until the compound statement's closing curly brace has beenencountered. In the next example a stringfirst is defined withoutexplicit specifyingstd::string, but once the compound statement has endedthe scope of theusing namespace std declaration has also ended, and sostd:: is required once again when definingsecond:

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

Ausing namespace directive cannot be used within thedeclaration block of a class- or enumeration-type. E.g., the following examplewon't compile:

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

4.1.2.2: `Koenig lookup'

IfKoenig lookup were called the `Koenig principle', it could have beenthe title of a newLudlum novel. However, it is not. Instead it refers toaC++ technicality.

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

As an illustration consider the next example. The functionFBB::fun(FBB::Value v) is defined in theFBB namespace. Itcan 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. IfValue in thenamespace FBB would have been defined asusing Value = int thenFBB::Value would be recognized asint, thus causing the Koenig lookupto fail.

As another example, consider the next program. Here two namespaces areinvolved, each defining their ownfun function. There is noambiguity, since the argument defines the namespace andFBB::fun iscalled:

    #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 thereis an ambiguity:fun has twoarguments, one from each namespace. The ambiguity must be resolved by theprogrammer:

    #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 onenamespace may break the code defined in another namespace. It shows thatnamespaces may affect each other and that namespaces may backfire if we're notaware 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 functionsdefined in theES namespace, since that would result in infiniterecursion. However, that's not the point. The point is that the programmerwon't even be given the opportunity to callES::fun since the compilationfails.

Compilation fails forgun but not forfun. But why is that so? WhyisES::fun flawlessly compiling whileES::gun isn't? InES::funfun(x) is called. Asx's type is not defined in a namespace the Koeniglookup does not apply andfun calls itself with infinite recursion.

WithES::gun the argument is defined in theFBBnamespace. Consequently, theFBB::gun function is a possible candidate tobe called. ButES::gun itself also is possible asES::gun's prototypeperfectly matches the callgun(x).

Now consider the situation whereFBB::gun has not yet beendeclared. Then there is of course no ambiguity. The programmer responsible fortheES namespace is resting happily. Some time after that the programmerwho's maintaining theFBB namespace decides it may be nice to add afunctiongun(Value x) to theFBB namespace. Now suddenly the code inthe namespaceES breaks because of an addition in a completely othernamespace (FBB). Namespaces clearly are not completely independent of eachother and we should be aware of subtleties like the above. Later in theC++ Annotations (chapter11) we'll return to this issue.

Koenig lookup is only used in the context of namespaces. If a functionis defined outside of a namespace, defining a parameter of a type that'sdefined inside a namespace, and that namespace also defines a function with anidentical signature, then the compiler reports an ambiguity when that functionis called. Here is an example, assuming the abovementioned namespaceFBBis 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

Thestd namespace is reserved byC++. The standard defines manyentities that are part of the runtime available software (e.g.,cout, cin,cerr); the templates defined in theStandard Template Library (cf.chapter18); and theGeneric Algorithms (cf. chapter19)are defined in thestd namespace.

Regarding the discussion in the previous section,usingdeclarations may be used when referring to entities in thestd namespace.For example, to use thestd::coutstream, the code may declare this object as follows:

    #include <iostream>    using std::cout;

Often, however, the identifiers defined in thestd namespace can allbe accepted without much thought. Because of that, one frequently encounters ausing directive, allowing the programmer to omit a namespace prefix whenreferring to any of the entities defined in the namespace specified with theusing directive. Instead of specifyingusing declarations thefollowingusing directive is frequently encountered:construction like

    #include <iostream>    using namespace std;

Should ausing directive, rather thanusing declarations be used?As arule of thumb one might decide to stick tousing declarations, upto the point where the list becomes impractically long, at which point ausing directive could be considered.

Two restrictions apply tousing directives anddeclarations:

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 variablevalue is defined in theCppAnnotationsnamespace. Within theCppAnnotations namespace another namespace(Virtual) is nested. Within that latter namespace the variablepointer is defined. To refer to thesevariable the following options are available:

Following ausing namespace directive all entities of that namespacecan be used without any further prefix. If a singleusing namespacedirective is used to refer to a nested namespace, then all entities of thatnested namespace can be used without any further prefix. However, the entitiesdefined in the more shallow namespace(s) still need the shallow namespace'sname(s). Only after providing specificusing namespace directives orusing declarations namespace qualifications can be omitted.

When fully qualified names are preferred but a long name like

    CppAnnotations::Virtual::pointer

is considered too long, anamespace alias may be used:

    namespace CV = CppAnnotations::Virtual;

This definesCV as analias for the full name. Thevariablepointer may now be accessed using:

    CV::pointer = 0;

A namespace alias can also be used in ausing namespace directive orusing declaration:

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

Nested namespace definitions

Starting with the C++17 standard, when nesting namespaces a nestednamespace 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 todefine members of namespaces inside anamespace region. But before an entity is definedoutside of a namespaceit must have been declaredinside its namespace.

To define an entity outside of its namespace its name must befullyqualified by prefixing the member by its namespaces. The definition may beprovided at the global level or at intermediate levels in the case of nestednamespaces. This allows us to define an entity belonging to namespaceA::Bwithin the region of namespaceA.

Assume the typeint INT8[8] is defined in theCppAnnotations::Virtualnamespace. Furthermore assume that it is our intent to define a functionsquares, inside the namespace
CppAnnotations::Virtual returning apointer toCppAnnotations::Virtual::INT8.

Having defined the prerequisites within theCppAnnotations::Virtualnamespace, our function could be defined as follows (cf. chapter9for coverage of the memory allocation operatornew[]):

    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 functionsquares defines an array of oneINT8 vector, andreturns its address after initializing the vector by the squares of the firsteight natural numbers.

Now the functionsquares can be defined outside of theCppAnnotations::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 theCppAnnotations region. In that case theVirtual namespace would havebeen required when definingsquares() 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)

TheC programming language offers tools likesleep(3) andselect(2) to suspend program execution for a certain amount of time. Andof course the family oftime(3) functions for setting and displaying time

Sleep andselect can be used for waiting, but as they were designed inan era when multi threading was unavailable, their usefulness islimited when used in multi threaded programs. Multi threading has become partofC++ (covered in detail in chapter20), and additionaltime-related functions are available in thestd::filesystem namespace,covered below in this chapter.

In multi threaded programs threads are frequently suspended, albeit usuallyfor a very short time. E.g., when a thread wants to access a variable, but thevariable is currently being updated by another thread, then the former threadshould wait until the latter thread has completed the update. Updating avariable usually doesn't take much time, but if it takes an unexpectedly longtime, then the former thread may want to be informed about that, so it can dosomething else while the latter thread is busy updating thevariable. Interactions between threads like these cannot be realized withfunctions likesleep andselect.

Thestd::chrono namespace bridges the gap between thetraditionally available time-related functions and the time-relatedrequirements of multi-threading and of thestd::filesystem namespace. All but the specificstd::filesystem related time functionality isavailable after including the<chrono> header file. After including the<filesystem> header file the facilities of thestd::filesystem areavailable.

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

Different events usually last for different amounts of time (given a specifictime resolution). Amounts of time are specified by objects of the classstd::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 arespecified by objects of the classstd::chrono::time_point.

It's not just that resolutions, durations of events, and points in time ofevents may differ, but the devices (clocks) we use for specifying time alsodiffer. In the pasthour glasses were used (and sometimes they're stillused when boiling eggs), but on the other hand we may use atomic clocks whenmeasurements should be very precise. Four different types of clocks areavailable. The commonly used clock isstd::chrono::system_clock, but inthe context of the file system there's also astd::chrono::file_clock.

In the upcoming sections the details of thestd::chrono namespace arecovered. First we look at characteristics of time resolutions. How tohandle amounts of time given their resolutions is covered next. The nextsection describes facilities for defining and handling time-points. Therelationships between these types and the various clock-types are coveredthereafter.

In this chapter the specificationstd::chrono:: is often omitted (inpracticeusing namespace std followed byusing namespace chrono iscommonly used;[std::]chrono:: specifications are occasionally used toavoid ambiguities). Also, every now and then you'll encounterforwardreferences to later chapters, like the reference to the chapter aboutmulti-threading. These are hard to avoid, but studying those chaptersat this point can safely be postponed without loss of continuity.

4.2.1: Time resolutions: std::ratio

Time resolutions (orunits of time) are essential components of timespecifications. Time resolutions are defined by objects of the classstd::ratio.

Before the classratio can be used, the<ratio> header file must beincluded. But the frquently included<chrono> header file already includestheratio header file.

The classratio has twotemplate arguments. These are positiveintegral numbers surrounded by pointed brackets defining, respectively, thenumerator and denominator of a fraction (by default the denominator equals1). Examples:

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

The classratio defines two directly accessible static datamembers:num represents its numerator,denits denominator. Aratio definition by itself simply defines a certainamount. 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 byratio<5, 1000>:ratio simplifies its fraction whenever possible.

Several predefinedratio types exist. They are, likeratio itself, defined in the standard namespace and can be used instead ofthe more cumbersomeratio<x> orratio<x, y> specifications:


yocto 10-24
zepto 10-21

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

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

zetta 1021yotta 1024

(note: the definitions of the typesyocto, zepto, zetta andyottause integral constants exceeding 64 bits. Although these constants are definedinC++, they are not available on 64 bit or smaller architectures.)

Time related ratios can very well be interpreted as fractions or multiple ofseconds, withratio<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 classstd::chrono::duration.

Before using the classduration the<chrono> header file must beincluded.

The classduration has two template arguments. A numeric type (int64_tis normally used) defining the type holding the duration's amount of time, anda time-resolutionratio (called itsresolution).

Often the following predefinedduration types are used:

predefined: duration typenanoseconds 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 useminutes halfHour{ 30 }.

The suffixesh, min, s, ms, us, ns are available for integral values,creating the correspondingduration times. E.g.,minutes min =1h stores 60 minutes inmin.

Sub-types:

Constructors:

Copy- and move-constructors (cf. chapter9) are available.

Operators:

Duration types support assignment, negation (e.g.,halfHour =-halfHour), addition and subtraction of duration values, and alsomultiplication, division and modulo computations by numeric factors. Compoundassignment 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 begreater than or equal to the precision of the right-hand side arguments(the left-hand argument may not lose precision). When using binaryarithmetic operators the resulting duration type has a precision equal to thefiner 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 thestd::chrono::duration_cast<destination>(source) function template, wheredestination is the destination'sduration type andsource is anavailabledestination object, butduration_cast truncates thedestination value whendestination's precision is less thansource'sprecision. E.g.,duration_cast<minutes>(seconds{ 90 }) returnsminutes{ 1 }.

When the left-hand side argument's precisionis less than the right-handside's precision some decision must be made about how to handle the loss ofprecision. The following function template can be used to convert a durationto a less precise type, returning adouble 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 adouble value (1.5 when called asdurationCast<minutes>(seconds{ 90 })), leaving the decision how to use thereturneddouble value to the caller.

4.2.3: Clocks measuring time

Clocks are used to measure time.C++ has several predefinedclocktypes, defined in thestd::chrono namespace.

Before using thechrono clocks the<chrono> header file must beincluded.

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

All clocks have a membernow returning the clock type'stime_point corresponding to the current time (relative to the clock'sepoch). 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 membernow the classessystem_clock andhigh_resolution_clock (referred to asClock below) provide two staticmembers:

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 classstd::chrono::time_point.

Before using the classtime_point the<chrono> header file must beincluded.

Likeduration the classtime_point requires two template arguments: Aclock type and a duration type. The standard clock types define their owntime_point types usingnanoseconds as their duration type (time_point by default usesnanoseconds). Thefollowing 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. chapter9) are available

Operators:

The compound operators are also available as binary arithmetic operators usingatime_point const & and aduration const & operand (in anyorder). Example:

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

Members:

Conversions:

All predefined clocks use nanoseconds for their duration types. Converting todifferentduration types is covered in the previous section(4.2.2). Alternatively, the membersto_time_t of thesystem_clock and thehigh_resolution_clock can be used to converttheirtime_point values totime_t. Suchtime_t values are commonlyused to convert time to text, e.g., using themanipulatorput_time(cf. section6.3.2). Theput_time manipulator must be providedwith the address of astd::tm object, which can be obtained from astd::time_t value. See figure3 for a visual representation ofthe involved elements.

Figure 3: Time according toC++

Use the following blueprint to insert asystem_clock::time pointvalue into astd::ostream (cf. section6.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 atime_point to anotherstd::chrono clock'stime_pointthe templateDestClock::time_point std::chrono::clock_cast<DestClock>(TimePoint tp) can be used. The argumenttp is an availabletime_point, andDestClock specifies the clock type of the returnedtime_point. E.g.,the followingcout 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 thefile_clock clock?
Sincetime_point's default cconstructor is initialized to its clock'sstarting 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 filesystems. Traditionally, to manipulate the file system theC programminglanguage offers functions performing the required system calls. Suchfunctions (likerename(2),truncate(2),opendir(2), andrealpath(3)) are of course also available inC++, but their signaturesand way of use are often less attractive as they usually expectchar const* parameters and may use static buffers or memory allocation based onmalloc(3) andfree(3).

Since 2003 theBoost library offerswrappers around these functions,offering interfaces to those system calls that are moreC++-like.

CurrentlyC++ directly supports these functions in thestd::filesystem namespace. These facilities can be usedafter including the<filesystem> header file.

Thefilesystem namespace is extensive: it contains more than 10 differentclasses, and more than 30 free functions. To refer to the identifiers definedin thestd::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 withoutfurther qualifications. Namespace specifications like `namespace fs =std::filesystem;' are also encountered, allowing specifications likefs::path.

Functions in thefilesystem namespace may fail. When functions cannotperform their assigned tasks they may throw exceptions (cf. chapter10) or they may assign values toerror_code objects thatare passed as arguments to those functions (see section4.3.1 below).

4.3.1: The class 'error_code'

Objects of the classstd::error_code (note:notstd::filesystem::error_code!) encapsulateerror values, andassociatederror categories (cf. section10.9;error_codecan be used after including the<system_error> header, but it is alsoavailable after including the<filesystem> header file). Traditionallyerror values are available as values assigned to the globalinterrno variable. By convention, whenerrno's value equals zero there's noerror. This convention was adopted byerror_code.

Error codes can be defined for many conceptually different situations. Thosesituations are characterized by their ownerror categories.

Error categories are used to associateerror_code objects with the errorsthat are defined by those categories. Default available error categories mayuse values likeEADDRINUSE (or the equivalentenum class errc valueaddress_in_use) but new types of error categories, tailored to othercontexts, can also be defined. Theenum class errc is defined in the<bits/error_constants.h> header file. Error categories and how to definethem is covered near the end of theC++ Annotations (section23.7.1).

Constructors:

Copy- and move-constructors are available.

Operators:

Members:

Free functions:

Manyfilesystem functions define an optional lasterror_code &ecparameter. Those functions havenoexcept specifications. If thosefunctions cannot complete their tasks, thenec is set to the appropriateerror code, callingec.clear() if no error was encountered. If noecargument is provided then those functions throw afilesystem_errorexception if they cannot complete their tasks.

4.3.2: Types and permissions of file system elements: file_status

File system entries (represented bypath objects, cf. section4.3.3below), have attributes: permissions (e.g., the owner may modify an entry),and types (like files, directories, and soft-links). Those attributes aredefined in the classstd::filesystem::file_status.

Types:

Defined in the(std::filesystem) enum class 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

std::filesystem::file_type


Permissions:

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

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

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.

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


Perm_options:

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

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

std::filesystem::perm_options


Constructors:

Copy and move constructors and assignment operators are avaialble;

Members:

4.3.2.1: The status of file system entries

Thestd::path free functionsstatus andsymlink_status(cf. section4.3.3.2) return thefile_status of file system entries.

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

These followingis_WHATEVER functions are available:

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

is_WHATEVER(file_status)


Here is a small program showing how file statuses can be obtained anddisplayed (cf. sections4.3.3 and (for themap) section12.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 classfilesysten::path commonly contain names offile system entries.

Constructors:

Copy- and move constructors are available, also using argument typesstd::string andchar const *.
Apath object doesn't have to refer to an existing file systementry.
Arguments ofpath constructors may contain (all optional):

The constructors also define a lastformat ftmp = auto_formatparameter, for which in practice almost never an argument has to be provided(for its details seecppreference.)

4.3.3.1: Accessors, modifiers and operators

Operators:

Accessors:

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

Except for the family ofstring() and theis_... accessors, there are alsobool has_... members returningtrue if thepath contains the specified component (e.g.,has_extension returnstrue if thepath 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.Thecopy_options enum values are used to fine-tune the behavior offilesystem functions copying file system elements.

Symbol
Value
Meaning
none
0
default: copy likecp

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

std::filesystem::copy_options

   
   
   
   
   
   
   
   
   

Functions:

4.3.4: Handling directories: directory_entry

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

In thestd::filesystem namespace the elements of directories are objectsof the classdirectory_entry, containing names and statuses of the entriesof that directory.

Constructors:

The classdirectory_entry supports all standard constructors (andassignment operators) and also a constructor expecting apath:

    directory_entry(path const &entry);

Objects of the classdirectory_entry can be constructed by name, and donot have to refer to existing entries in the file system.

Operators:

Members:

4.3.4.1: Visiting directory entries: (recursive_)directory_iterator

Thefilesystem namespace has two classes simplifying directoryprocessing: objects of the classdirectory_iterator are (input) iteratorsiterating over the entries of directories; and objects of the classrecursive_directory_iterator are (input) iterators recursively visitingall entries of directories.location

enum class directory_options

Theenum class directory_options defines values thatare used to fine-tune the behavior ofrecursive_directory_iteratorobjects, supporting bitwise operators (the values of its symbols are shownbetween parentheses):

Constructors:

Copy, and move constructors are available.

These iterators point todirectory_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-loopsand in standardfor andwhile statements.

Additional members of recursive_directory_iterators

Here's a small program displaying all elements andall 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 beimplemented `by hand'. E.g., a breadth-first strategy first visits all thenon-directory entries and then visits the sub-directories, as illustrated inthe next example by processingthe directories stored inlevel in turn(initially it merely contains the starting directory).

`Processing a directory' means that its non-directory entries are directlyprocessed storing the names of sub-directories innext. Onceall entries atlevel have been processed the names of the next levelsub-directories are available (innext). By assigningnext tolevel all directories at the next level are processed. When reaching themost deeply nested sub-directories itsnext is empty and thewhilestatement 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 existingpath lives in a file system, Sizes of file systemstypically are quite large, but there is a limit to their sizes.

The size of file systems, the number of bytes that is currently being usedand the remaining number of bytes is made available by the functionspace(path const &entry [,error_code &ec]), returning the information about the file system containingentry in a PODstruct space_info.

If theerror_code argument is provided then it is cleared if no erroroccurs, and set to the operating system's error code if an error hasoccurred. If an error occurs and theerror_code argument was not providedthen afilesystem_error exception is thrown, receivingpath as itsfirst argument and the operating system's error code as itserror_codeargument.

The returnedspace_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 ofthe typeuintmax_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

Thestd::filesystem namespace offers its own exception typefilesystem_error (see also chapter10). Its constructor hasthe following signature (the bracketed parameters are optional):
    filesystem_error(string const &what,                     [path const &path1, [path const &path2,]]                     error_code ec);

Asfilesystem facilities are closely related to standard systemfunctions,errc error code enumeration values can be used to obtainerror_codes to pass tofilesystem_error, as illustrated by thefollowing 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";    }




[8]ページ先頭

©2009-2025 Movatter.jp