ready for 12.3.0

This commit is contained in:
Frank B. Brokken 2023-04-27 14:43:11 +02:00
parent b075c82f0c
commit d231922245
13 changed files with 239 additions and 27 deletions

View file

@ -1,15 +1,20 @@
C++-annotations (12.3.0) WIP
C++-annotations (12.3.0)
* removed section 'Returning types nested under class templates'
(advancedtemplates/returnnested), sf since C++20
* added descriptions of the back_, front_ and inside insert_iterator
functions to the STL chapter.
* updated the Generic Algorithm chapter: merged the descriptions of very
similar algorithms (like 'copy' and 'copy_if'), added the descriptions
of several new algorithms, added a section about execution policies and
updated the descriptions of generic algorithms supporting execution
policies.
policies. Added a new section about handling raw memory.
* the memory chapter's section about 'placement new' contains a forward link
to the STL section about initializing raw memory.
* repaired typos
* during the 2022-2023 Academic Year many typo reports and suggestions for
updating sections were submitted by the C++ course participants Jeroen
Lammersma (jeroen at lammersma dot dev) and Channa Dias Perera (c dot dias
dot perera at student dot rug dot nl: thanks, gentlemen, for your valuable
contributions!
C++-annotations (12.2.0)

View file

@ -39,6 +39,8 @@ IFDEF(latex)(latexcommand(
\pagestyle{headings}
\pagenumbering{arabic}))()
COMMENT(WIP
COMMENT( 1 )
lchapter(Overview)(Overview Of The Chapters)
includefile(overview)
@ -111,10 +113,14 @@ COMMENT( 18 )
lchapter(STL)(The Standard Template Library)
includefile(stl)
END WIP)
COMMENT( 19 )
lchapter(GENERIC)(The STL Generic Algorithms)
includefile(generic)
COMMENT(WIP
COMMENT( 20 )
lchapter(THREADING)(Multi Threading)
includefile(threading)
@ -139,5 +145,7 @@ COMMENT( 25 )
lchapter(CONCRETE)(Concrete Examples)
includefile(concrete)
END WIP)
IFDEF(latex)(latexcommand(\cleardoublepage\phantomsection\printindex))()

View file

@ -17,7 +17,7 @@ includefile(generic/intro)
includefile(generic/allof)
lsubsect(BEGEND)(begin / end)
includefile(generic/begin)
includefile(generic/beginend)
lsubsect(BINSRCH)(binary_search)
includefile(generic/binarysearch)
@ -172,23 +172,9 @@ includefile(generic/intro)
lsubsect(TRANSRED)(transform_reduce)
includefile(generic/transformreduce)
COMMENT(
mention:
unititialized_copy, unititialized_copy_n unititialized_fill,
unititialized_fill_n unititialized_move unititialized_move_n
at the non-init functions
separate sections:
uninitialized_default_construct(_n)
uninitialized_value_construct(_n)
destroy(_n/_at)
construct_at
)
------------
lsubsect(UNINIT)(handling uninitialized memory)
includefile(generic/uninitialized)
lsubsect(UNIQUE)(unique)
includefile(generic/unique)

View file

@ -0,0 +1,26 @@
#include <memory>
#include <vector>
#include <iostream>
#include <string>
using namespace std;
int main()
{
char raw[4 * sizeof(string)]; // raw memory to receive strings
string *ptr = reinterpret_cast<string *>(raw); // pointer to strings
// construct 4 strings in raw
uninitialized_default_construct_n(ptr, 4);
destroy(ptr, ptr + 4); // call the strings' destructors
vector<string> vs(4, "string"); // move 4 strings to raw memory
uninitialized_move(vs.begin(), vs.end(), ptr);
cout << vs.front() << ", " << vs.back() << '\n' <<
ptr[0] << ", " << ptr[3] << '\n';
destroy(ptr, ptr + 4); // call the strings' destructors
}
// Displays:
// ,
// string, string

View file

@ -85,7 +85,7 @@ The annotations() distinguishes the following categories
link(copy)(COPY);
link(copy_backward)(COPYBACK);
link(copy_if)(COPY);
link(move; move_backward)(MOVE);
link(move; move_backward)(MOVEFWD);
link(partition_copy)(PARTCP);
link(partial_sort_copy)(PARTSORT);
link(remove_copy; remove_copy_if)(REMOVE);
@ -93,7 +93,7 @@ The annotations() distinguishes the following categories
link(reverse_copy)(REVERSE);
link(rotate_copy)(ROTATE);
link(sample)(SAMPLE);
link(shift_left; shift_right)(MOVE);
link(shift_left; shift_right)(MOVEFWD);
link(unique_copy)(UNIQUECP);
)
it() Counters: performing count operations:
@ -112,6 +112,7 @@ The annotations() distinguishes the following categories
link(fill; fill_n)(FILL);
link(generate; generate_n)(GEN);
link(iota)(IOTA);
link(uninitialized (raw) memory)(UNINIT);
)
it() Limiters: determining boundaries of data:
quote(

View file

@ -1,4 +1,4 @@
hi(move) hi(move_backward)
hi(move) hi(move_backward) hi(shift_left) hi(shift_right)
itemization(
it() Header file: tt(<algorithm>)
it() Function prototype:

View file

@ -13,7 +13,7 @@
)
it() Description:
This algorithm acts like tt(accumulate)
(cf. link(accumulate)(ACCUMULATE)), but the algorithm requires that the used
(cf. link(accumulate)(ACCU)), but the algorithm requires that the used
operator is both associative and commutative: regrouping and rearranging the
elements in any order may not affect the final outcome. E.g., the numeric
addition operator satisfies both requirements.

View file

@ -0,0 +1,115 @@
Section ref(PLACEMENT) covers the placement new operator. The placement new
operator is used to install values or objects in 'raw memory', i.e., memory
that is already available, but hasn't yet been initialized for the intended
object types.
As covered before, when calling something like tt(auto ptr = new string{
"hello" }) the string is constructed in memory specifically allocated to
contain the object, and the object type's constructor initializes the object
in that memory. Likewise, when calling tt(delete ptr) the string's destructor
is called, followed by returning the memory allocated by tt(new) to the common
pool.
When using placement new the memory to contain the object is already
available, and the construction tt(auto ptr = new (storageAddress) string{
"hello" }) is used to merely construct the string at the location specified by
tt(storageAddress). That string can then (as usual) be accessed via tt(ptr),
but tt(delete ptr) cannot be used, since the memory at tt(storageAddress) was
already available before invoking the placement new operator. Therefore, in
these cases the remarkable situation is encountered where the object's
destructor must explicitly be called (using tt(ptr->~string())) and using
tt(delete ptr) is completely wrong, causing a memory error which aborts the
program.
Several generic algorithms, all supporting execution policies, are available
simplifying the use of tt(placement new). To use these algorithm the
tthi(memory) header file must be included.
hi(placement new: generic algorithms)hi(uninitialized... algorithms)
Facilities are available to copy, fill, initialize, and move objects to/in
uninitialized (raw) memory, as well as facilities to delete the objects stored
in raw memory. Here is an overvieuw of the available facilities (cf.
url(cppreference)(https://en.cppreference.com) for more details
about the algorithms handling uninitialized memory):
itemization(
itt(uninitialized_copy([ExecPol,]
ForwardIterator first, ForwardIterator last,
ForwardIterator dest);)nl()
copies the elements in the rangett(first, last) to the raw memory
starting at tt(dest), returning the location beyond the last copied
element.
itt(uninitialized_copy_n([ExecPol,]
ForwardIterator first, size_t nObjects,
ForwardIterator dest);)nl()
same as the previous algorithm, but copies tt(nObjects).
itt(uninitialized_default_construct([ExecPol,]
ForwardIterator first, ForwardIterator last);)nl()
installs default constructed values at the raw memory locations reached
by the iterator range rangett(first, last). The algorithm requires
that the types referred to by the iterators are either trivial types
(like built-in types) or define tt(value_type) returning their type
names. When using trivial types the installed do not assume that the
installed values are 0-initialized.
itt(uninitialized_default_construct_n([ExecPol,]
ForwardIterator first, size_t nObjects);)nl()
same as the previous algorithm, but installs tt(nObjects) in the
uninitialized memory.
itt(uninitialized_fill([ExecPol,]
ForwardIterator first, ForwardIterator last,
Type const &value);)nl()
like the first algorithm, but installing copies of tt(value) in the
uninitialized memory.
itt(uninitialized_fill([ExecPol,]
ForwardIterator first, size_t nObjects,Type const &value);)nl()
same as the previous algorithm, but copies tt(value) to the
tt(nObjects) subsequent locations in the uninitialized memory.
itt( uninitialized_move([ExecPol,]
ForwardIterator first, ForwardIterator last,
ForwardIterator dest);)nl()
same as the first algorithm, but the elements in the rangett(first,
last) are moved to the raw memory.
itt(uninitialized_move_n([ExecPol,]
ForwardIterator first, size_t nObjects,
ForwardIterator dest);)nl()
same as the previous algorithm, but tt(nObjects) are moved.
itt(uninitialized_value_construct([ExecPol,]
ForwardIterator first, ForwardIterator last);)nl()
same as tt(uninitialized_default_construct), but requires that the
types referred to by the iterators define tt(value_type) returning
their type names.
itt(uninitialized_value_construct_n([ExecPol,]
ForwardIterator first, size_t nObjects);)nl()
same as the previous algorithm, but installs tt(nObjects) in the
uninitialized memory.
)
The algorithm hi(construct_at) tt(Type *construct_at(Type *raw, Args
&&...args)) constructs an object of type tt(Type) in the raw memory at
tt(raw), passing tt(args...) to tt(Type's) constructor.
To delete the objects installed in raw memory the following facilities are
available:
itemization(
itt(void destroy([ExecPol,] ForwardIterator first,
ForwardIterator last);)nl()
assuming the the types to which tt(first) refers: it calls
tt(iterator->~Type()) for all elements in the range rangett(first,
last).
itt(void destroy([ExecPol,] ForwardIterator first, size_t nObjects);)nl()
same as the previous algorithm, but calls the destructors of
tt(nObjects) objects.
itt(void destroy_at(Type *raw);)nl()
calls the destructor of the object installed at tt(raw) using placement
new. If the tt(raw) pointer points to an array of placement new
allocated objects then the destructors of the elements of the array
are called.
)
Here is an example:
verbinclude(-as4 examples/uninitialized.cc)

View file

@ -98,3 +98,6 @@ placement tt(new) in combination with a statically allocated
buffer all the objects' destructors must be called explicitly, as in the
following example:
verbinclude(//CODE examples/placement2.cc)
Several standard template library functions are available for handling
unitialized (raw) memory. See section ref(UNINIT) for a description.

View file

@ -33,12 +33,16 @@ includefile(threading/mutex)
lsect(LOCKS)(Locks and lock handling)
includefile(threading/locks)
subsect(Deadlocks)
lsubsect(DEADLOCKS)(Deadlocks)
includefile(threading/deadlocks)
subsect(Shared locks)
includefile(threading/sharedlock)
subsect(Scoped locks)
includefile(threading/scopedlock)
sect(Event handling (condition variables))
includefile(threading/events)

View file

@ -0,0 +1,41 @@
#include <iostream>
#include <thread>
#include <mutex>
#include <unistd.h>
using namespace std;
//code
int value;
mutex valueMutex;
mutex coutMutex;
void fun1()
{
scoped_lock sl{ coutMutex, valueMutex };
cout << "fun 1 locks cout\n";
sleep(1);
cout << "fun 1 locks value\n";
}
void fun2()
{
scoped_lock sl{ valueMutex, coutMutex };
cout << "fun 2 locks value\n";
sleep(1);
cout << "fun 2 locks cout\n";
}
int main()
{
thread t1(fun1);
fun2();
t1.join();
}
// Displays:
// fun 2 locks value
// fun 2 locks cout
// fun 1 locks cout
// fun 1 locks value
//=

View file

@ -0,0 +1,19 @@
Deadlocks can be avoided using the principles described in the previous
section. However, instead of placing the responsibility for avoiding deadlocks
on the shoulders of the software engineer, an alternative approach is
available: a ti(scoped_lock) can be used to lock multiple semaphores at once,
where the tt(scoped_lock) ensures that deadlocks are avoided.
The tt(scoped_lock) also has a default constructor, performing no actions, so
it's up to the software engineer to define tt(scoped_lock) objects with at
least one tt(mutex). Before using tt(scoped_lock) objects the tthi(mutex)
header file must be included. Adapting the example from section
ref(DEADLOCKS): both functions define a tt(scoped_lock) (note that the order
in which the mutexes are specified isn't relevant), and deadlocks are do not
occur:
verbinclude(-ns4 //code examples/lock.cc)
Thus, instead of using tt(lock_guard) objects, tt(scoped_lock) objects can be
used. It's a matter of taste whether tt(lock_guards) or tt(scoped_locks)
should be preferred when only one mutex is used. Maybe tt(scoped_lock) should
be preferred, since it always works....

View file

@ -3,6 +3,10 @@ changes (and occasionally also for the third field of the version number). At
a major version upgrade the entries of the previous major version are kept,
and entries referring to older releases are removed.
itemization(
it() Version 12.3.0 updates and reorganizes the coverage of the generic
algorithms, fixes many typos and unclarities in the Annotations' text,
removed superfluous sections since C++20, and adds an overview of
facilities to handle objects constructed in raw memory.
it() Version 12.2.0 takes into account that tt(std::iterator) is
deprecated. Section ref(ITERATORCONS) was rewritten; section
ref(OPERATORINDEX) was updated (tt(operator[] const) should return