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.
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.
namespace CppAnnotations { double cos(double degrees); double sin(double degrees); }
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.
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. }
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 };
`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. }
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:
namespace std
. This is not compiler enforced but is imposed upon user
code by the standard;
Using
declarations and directives should not be imposed upon
code written by third parties. In practice this means that using
directives and declarations should be banned from header files and should only
be used in source files (cf. section 7.11.1).
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:
int main() { CppAnnotations::value = 0; CppAnnotations::Virtual::pointer = 0; }
using namespace CppAnnotations
directive can be provided. Now
value
can be used without any prefix, but pointer
must be used
with the Virtual::
prefix:
using namespace CppAnnotations; int main() { value = 0; Virtual::pointer = 0; }
using namespace
directive for the full namespace chain can be
used. Now value
needs its CppAnnotations
prefix again, but
pointer
doesn't require a prefix anymore:
using namespace CppAnnotations::Virtual; int main() { CppAnnotations::value = 0; pointer = 0; }
using namespace
directives none of the
namespace prefixes are required anymore:
using namespace CppAnnotations; using namespace Virtual; int main() { value = 0; pointer = 0; }
using
declarations:
using CppAnnotations::value; using CppAnnotations::Virtual::pointer; int main() { value = 0; pointer = 0; }
using namespace
directives and using
declarations can also be used. E.g., a using namespace
directive
can be used for the CppAnnotations::Virtual
namespace, and a
using
declaration can be used for the CppAnnotations::value
variable:
using namespace CppAnnotations::Virtual; using CppAnnotations::value; int main() { value = 0; pointer = 0; }
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 }
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:
squares
is declared inside of the CppAnnotations::Virtual
namespace.
The definition outside of the namespace region requires us to use
the fully qualified name of the function and of its return type.
Inside the body of the function squares
we are within the
CppAnnotations::
Virtual
namespace, so inside the function fully
qualified names (e.g., for INT8
) are not required any more.
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; } }
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.
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 | ||||
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';
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:
rep
: the duration's numeric type (commonly int64_t
);
period
: the ratio
type (like kilo
). E.g.,
minutes::period::num
equals 60.
Constructors:
duration()
:duration(Type const &value)
:value
time units (Type
refers to
the duration's numeric type. E.g., when defining
minutes halfHour{ 30 }
the argument 30 is stored inside its
int64_t
data member).
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:
Type count() const
returns the value stored inside the duration
object. E.g., minutes{ 30 }.count()
returns 30. The following
members are static members;
duration::zero()
returns an (immutable) duration object whose
count
member returns 0. E.g., seconds::zero().count()
returns
0s
;
duration::min()
returns an (immutable) duration object whose
count
member returns the lowest value of its rep
type (i.e.,
std::numeric_limits<duration::rep>::min()
(cf. section
21.11));
duration::max()
returns an (immutable) duration object whose
count
member returns the maximum value of its rep
type.
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.
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:
Clock::duration
: the clock's time granularity type (by default
using nanoseconds
. E.g., system_clock::duration oneDay{ 24h }
;
Clock::period
: the clock's std::ratio
type. E.g.,
system_clock::period::den
;
Clock::rep
: the type storing amounts of time. By default using
int64_t
). E.g., system_clock::rep amount
;
Clock::time_point
: the type storing time points (covered in the
next section). By default using time_point<system_clock,
nanoseconds>
E.g., system_clock::time_point start
.
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:
system_clock
is the `wall clock', using the system's real time
clock;
steady_clock
is a clock whose time increases in parallel with the
increase of real time;
high_resolution_clock
is the computer's fastest clock (i.e., the
clock having the shortest timer-tick interval). In practice this is
the same clock as system_clock
.
file_clock
: used for time specifications of file-system
elements. File_clock
time specifications use a different
starting (epoch) time than the other clocks, but it's easy to
convert file_clock
times to/from other clock types.
In addition to the member now
the classes system_clock
and
high_resolution_clock
(referred to as Clock
below) provide two static
members:
std::time_t Clock::to_time_t(Clock::time_point const &tp)
std::time_t
value (the same type as returned by C's
time(2) function) converting a time_point
value to a
time_t
value;
Clock::time_point Clock::from_time_t(std::time_tvseconds)
time_point
corresponding to a
time_t
value.
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
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:
time_point()
:system_clock
it is January, 1, 1970, 00:00h, but
note that file_clock::time_point
has a different starting point
(see the example at the end of this section);
Operators:
time_point &operator+=(duration const &amount)
adds the amount of time represented by amount
to the current
time_point
object;
time_point &operator-=(duration const &amount)
subtracts
the amount of time represented by amount
from the
current time_point
object;
Type operator<<(std::ostream &, time_point const &)
inserts a textual
representation of the time_point's
UTC time into the ostream
.
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:
duration time_since_epoch() const
:duration
is the duration type used by the time point object for
which this member is called. To convert the returned duration
to a
size_t
value use time_since_epoch().count()
;
time_point min() const
:
a static member returning the time point's duration::min
value. Example:
cout << time_point<system_clock>::min().time_since_epoch().count() << '\n'; // shows -9223372036854775808
time_point max() const
:
a static member returning the time point's duration::max
value.
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.
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
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).
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:
error_code() noexcept
:system_category
error category. Value 0 is not considered an
error;
error_code(int ec, error_category const &cat) noexcept
:ec
(e.g., errno
, set by a failing function), and a const reference to
the applicable error category (provided by, e.g.,
std::system_category()
or
std::generic_category()
). Here is an example
defining an error_code
object:
error_code ec{ 5, system_category() };
error_code(ErrorCodeEnum value) noexcept
:template <class ErrorCodeEnum>
. It initializes the object
with the return value of make_error_code(value)
(see below). In
section 23.7 defining ErrorCodeEnums
is
covered. Note: ErrorCodeEnum
as such does not exist. It
is a mere placeholder for existing ErrorCodeEnum
enumerations;
Operators:
ErrorCodeEnum
are available;
operator std::errc() const noexcept
:errc
error-code value. For g++
these are
defined in the enum class errc
in the file
/usr/include/x86_64-linux-gnu/c++/14/bits/error_constants.h
;
explicit operator bool() const noexcept
:true
if the object's error value is unequal 0 (i.e., it
represents and error).
Members:
void assign(int val, error_category const &cat)
:ec.assign(0, generic_category())
;
error_category const &category() const noexcept
:
void clear() noexcept
:error_code's
value to 0 and its error category to
system_category
;
error_condition default_error_condition() const noexcept
:error_condition
);
string message() const
:category().message(ec.value())
);
int value() const noexcept
:Free functions:
error_code
objects can be compared for (in) equality and can
be ordered (using operator<
).
Ordering error_codes
associated with different error categories
has no meaning. But when the error categories are identical then they
are compared by their error code values (cf. this SG14 discussion
summary);
error_code make_error_code(errc value) noexcept
:error_code
object initialized with
static_cast<int>(value)
and generic_category()
. This function
converts an enum class errc
value to an error_code
.
Other error related enums may also be defined with which tailored
make_error_code
functions can be associated (cf. section
23.7;)
std::ostream &operator<<(std::ostream & os, error_code const &ec)
:return os << ec.category().name() << ':' << ec.value();
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.
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:
explicit file_status(file_type type = file_type::none,
perms permissions = perms::unknown)
Members:
perms permissions() const
returns the permissions of the file system entry represented by the
file_status
object.permissions
changing the permissions of a file system entry;
file_type type() const
returns the type of the file system entry to which the
file_status
object refers;
void type(file_type type)
changes the type of the file system entry to which the file_tatus
object refers.
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:
bool status_known(file_status const &status)
returns true
if status
refers to a determined status
(status
itself may indicate that the entity referred to by
status
does not exist). One way of receiving false
is by
passing it a default status object: status_known(file_status{})
;
bool is_WHATEVER(file_status status)
,
replacing WHATEVER
by the requested file type, returns true
if
status
refers to an entry of the specified type.
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'; } }
filesysten::path
commonly contain names of
file system entries.
Constructors:
path()
: the default constructor is initialized with an empty
path
;
path(InputIter begin, InputIter end)
:begin
to end
define the path's
name.
std::string
and char const *
.path
object doesn't have to refer to an existing file system
entry.path
constructors may contain (all optional):
E:
) or device indicator
(like //nfs
);
.
) indicates the current
directory and the `double dot filename' (..
) indicates the current
directory's parent directory;
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.)
path &operator/=(Type const &arg)
:arg
argument is separated from the path's
current content by a directory separator (unless the path is initially
empty as in cout << path{}.append("entry")
). See also the members
append
and concat
, below. The free operator /
accepts two
path
(promotable) arguments, returning a path
containing both
paths separated by a directory separator (e.g., lhs / rhs
returns
the path
object lhs/rhs
);
path &operator+=(Type const &arg)
:/=
, but no directory separator is used when
adding arg
to the current path
;
path
objects can be compared using the
(operators implied by the) ==
and <=>
operators. Path objects
are compared by lexicographical comparing their ascii-character
content;
ostream &operator<<(ostream &out, path const &path)
(stream
insertion) inserts path's
content, surrounded by double quotes,
into out
;
istream &operator>>(istream &in, path &path)
extracts
path's
content from in
. The extracted path name may optionally
be surrounded by double quotes. When inserting a previously extracted
path
object only one set of surrounding quotes are shown.
Accessors:
Accessors return specific path
components. If a path doesn't contain
the requested component then an empty path
is returned.
char const *c_str()
: the path's content is returned as an NTBS;
path extension()
returns the dot-extension of the path's last
component (including the dot);
path filename()
returns the last path-content of the current path
object. See also the stem()
accessor, below;
bool is_absolute()
: returns true
if the path
object contains
an absolute path specification;
bool is_relative()
: returns true
if the path
object contains
a relative path specification;
path parent_path()
returns the current path-content from which the
last element has been removed. Note that if the path
object
contains a filename's path (like "/usr/bin/zip"
) then
parent_path
removes /zip
and returns /usr/bin
, so not
zip's
parent directory, but its actual directory;
path relative_path()
: returns the path's content beyond
the path's root-directory component of the path
object. E.g., if
the path ulb{ "/usr/local/bin" }
is defined then
ulb.relative_path()
returns a path containing "usr/local/bin"
;
path root_directory()
: returns the root-directory component of the
path
object;
path root_name()
: returns the root-name's component of the path
object;
path root_path()
: returns the root-path component of the path
object;
path stem()
returns the last path-content of the current path
object from which the dot-extension hash been removed;
string()
: returns the path's content as a std::string
.wstring, u8string, u16string, , u32string, generic_string,
generic_wstring, generic_u8string, generic_u16string,
and
generic_u32string
;
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:
path &append(Type const &arg)
acts like the /=
operator;
path::iterator begin()
returns an iterator containing the first path
component; Dereferencing a path::iterator
returns a path
object.path::iterators
the individual
directories and finally filename components are returned. The
directory separators themselves are not returned when dereferencing
subsequent path::iterators
;
path::const_iterator begin() const
returns a iterator to the
immutable first path component; Dereferencing a
path::const_iterator
returns a path const
object;
void clear()
: the path's
content is erased;
int compare(Type const &other)
:other
. Other
can be a path
, a string-type or
an NTBS;
path &concat(Type const &arg)
acts like the +=
operator;
path::iterator end()
returns an iterator beyond the last path
component;
path::const_iterator end() const
returns an iterator beyond the
immutable last path component
path &remove_filename()
:/
. If
the path
doesn't contain a slash then the path
object's
cleared;
path &replace_extension(path const &replacement = path{} )
:replacement
. The extension is
removed if replacement
is empty. If the path
calling
replace_extension
has no extension then replacement
is added.
The replacement may optionally start with a dot. The path object's
extension receives only one dot;
path &replace_filename(path const &replacement)
:replacement
,
which itself may contain multiple path elements. If only a
root-directory is stored, then it is replaced by replacement
. The
member's behavior is undefined if the current path object is empty;
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:
path absolute(path const &src, [, error_code &ec])
:src
as an absolute path (i.e., starting at the
filesystem's root (and maybe disk) name). It can be called as, e.g.,
absolute("tmp/filename")
, returning the (absolute) current working
directory to which absolute's
argument is appended as a final
element, separated by a directory separator. Relative path indicators
(like ../
and ./
) are kept;
path canonical(path const &src [, error_code &ec])
:src's
canonical path. Src
must exist. Example:
canonical("/usr/local/bin/../../share/man"); // returns path{ "/usr/share/man" }
void copy(path const &src, path const &dest [, copy_options
opts [, error_code &ec]])
:src
must exist. By default copies src
to dest
if the
cp
program would also succeed. Use opts
to fine-tune
copy's
behavior.src
is a directory, and dest
does not exist, dest
is
created. Directories are recursively copied if copy options
recursive
or none
were specified;
bool copy_file(path const &src, path const &dest [,
copy_options opts [, error_code &ec]])
:src
must exist. Copies src
to dest
if the cp
program
would also succeed. Symbolic links are followed. The value true
is
returned if copying succeeded;
void copy_symlink(path const &src, path const &dest [,
error_code &ec])
:dest
as a copy of the symlink src
;
bool create_directories(path const &dest [,
error_code &ec])
:dest
, unless already existing. E.g.,
using argument "a/b/c"
and "a"
doesn't yet exist then
"a/b/c"
are all created. The value true
is returned if
dest
was actually created. If false
is returned ec
contains an error-code, which is zero (ec.value() == 0
) if
dest
already existed. See also create_directory
below;
bool create_directory(path const &dest [, path
const &existing] [, error_code &ec])
:dest's
parent directory must exist. This function creates
directory dest
if it does not yet exist. Nested subdirectories are
not created by create_directory
: using argument "a/b/c"
and
neither "a"
nor "a/b"
exist then this function fails. The
value true
is returned if dest
was actually created. If
false
is returned ec
contains an error-code, which is zero
(ec.value() == 0
) if dest
already existed. If existing
is
specified, then dest
receives the same attributes as
existing
;
void create_directory_symlink(path const
&dir, path const &link [, error_code &ec])
:create_symlink
(see below), but is used to create a
symbolic link to a directory;
void create_hard_link(path const &dest, path const
&link [, error_code &ec])
:link
to dest
. Dest
must exist;
void create_symlink(path const &dest, path const
&link [, error_code &ec])
:link
to dest
; dest
does not have to exist;
path current_path([error_code &ec])
, void
current_path(path const &toPath [, error_code &ec])
:toPath
. The returned path's last
character is not a slash, unless called from the root-directory;
bool equivalent(path const &path1, path const &path2 [,
error_code &ec])
:true
is returned if path1
and path2
refer to the same file
or directory, and have identical statuses. Both paths must exist;
bool exists(path const &dest [, error_code &ec])
,
exists(file_status status)
:true
is returned if dest
exists (actually: if
status(dest[, ec])
(see below) returns true
). Note: when
iterating over directories, the iterator usually provides the entries'
statuses. In those cases calling exists(iterator->status())
is
more efficient than calling exists(*iterator)
. When dest
is
the path to a symbolic reference then exists
returns whether the
link's destination exists or not (see also the functions status
and symlink_status
in section 4.3.4);
std::unintmax_t file_size(path const &dest [, error_code
&ec])
:
std::uintmax_t hard_link_count(path const &dest [,
error_code &ec])
:dest
;
file_clock::time_point
last_write_time(path const &dest [, error_code &ec])
,
void last_write_time(path const &dest,
file_clock::time_point newTime [, error_code &ec])
:dest's
last modification time;
the latter function changes dest's
last modification time to
newTime
.
last_write_time's
return type is defined through a using
alias
for chrono::time_point
(cf. section 4.2.4). The returned
time_point
is guaranteed to cover all file time values that may be
encountered in the current file system. The function
file_clock::to_sys
(see below) can be used to convert
file_clock
time points to system_clock
time_points;
void permissions(Path const &entry,
perms newPerms [, perm_options opts = perm_options::replace ]
[, error_code &ec])
:entry
. Each of the two last arguments
is optional. E.g., when specifying an error_code
as third argument
then opts
is implicitly specified as replace
;
path read_symlink(path const &src [, error_code &ec])
:src
must refer to a symbolic link or an error is generated. The
link's target is returned;
bool remove(path const &dest [, error_code &ec])
,
std::uintmax_t remove_all(path const &dest [,
error_code &ec])
:remove
removes the file, symlink, or empty directory
dest
, returning true
if dest
could be removed;
remove_all
removes dest
if it's a file (or symlink); and
recursively removes directory dest
, returning the number of
removed entries;
void rename(path const &src, path const &dest [, error_code
&ec])
:src
to dest
, as if using the standard mv(1)
command (if dest
exists it is overwritten);
void resize_file(path const &src, std::uintmax_t size [,
error_code &ec])
:src's
size is changed to size
as if using the standard
truncate(1) command;
space_info space(path const &src [, error_code &ec])
:src
is
located;
file_status status(path const &entry [, error_code &ec])
:entry's file_status
. If entry
is the name of a symbolic
link then the status of the link's destination is returned;
file_status symlink_status(path const &entry [,
error_code &ec])
:entry's
own file_status
;
path system_complete(path const &src[, error_code&
ec])
:src
, using current_path
as
its base;
path temp_directory_path([error_code& ec])
:TMPDIR
, TMP, TEMP
, or
TEMPDIR
. Otherwise, /tmp
is returned.
system_clock::time_point
file_clock::to_sys(file_clock::time_point timePoint)
:last_write_time
can be represented using the
system_clock's
epoch:
int main() { time_t seconds = system_clock::to_time_t( file_clock::to_sys(last_write_time("lastwritetime.cc")) ); cout << "lastwritetime.cc's last (UTC) write time: " << put_time(gmtime(&seconds), "%c") << '\n'; }
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:
ostream &operator<<(ostream &, directory_entry const &)
inserts the
object's path
into the ostream
;
==, <=>
) compare their std::path
data members;
path const &path() const
,
operator path const &() const
returns the current object's path name.
Members:
void assign(path const &dest)
:dest
(identical to
directory_entry's
assignment operator);
void replace_filename(path const &dest)
:dest
. If that element is empty (e.g., when the object's path ends
in a directory separator) then dest
is appended to the current
object's path;
filesystem::file_status status([error_code &ec])
:symlink_status
(see also section 4.3.2 and 4.3.2.1
below).
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):
none
(0): directory symlinks are skipped, denied permission to enter
a sub-directory generates an error;
follow_directory_symlink
(1): symlinks to sub-directories are
followed;
skip_permission_denied
(2): directories that cannot be entered are
silently skipped.
Constructors:
(recursive_)directory_iterator()
:(recursive_)directory_iterator's
iterator-range;
(recursive_)directory_iterator(path const &from [, error_code &ec])
:(recursive-)directory_iterator
using from
as the
starting directory of the iterator. All members of standard input
iterators (cf. section 18.2) are supported;
recursive_directory_iterator(path const &from, directory_options opts
[, error_code &ec])
:(recursive-)directory_iterator
fine-tuning its
behavior to the options specified by opts
.
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
int depth() const
:
void disable_recursion_pending()
:depth()
returns that specific depth this member must be
called each time before the iterator's increment operator is called;
recursive_directory_iterator &increment(error_code &ec)
:operator++
throws a filesystem_error
exception, while
increment
assigns the error to ec
;
directory_options options() const
:
void pop()
:
bool recursion_pending() const
:true
is returned if recursive processing of sub-directories of the
currently processed directory is allowed. If so, and the
the iterator points at a sub-directory, then processing
continues in that sub-directory at the iterator's next increment;
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 }
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'; }
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"; }