A look at C++14: Papers Part I

by Jens Weller

This is the first Part of n, or lets say many entries in this blog. In total I hope to be able to cover most papers in 3-4 blog posts, giving the reader an overview over the suggestions and changes for C++ at the coming C++ Committee Meeting in April. In total there are 98 Papers, so I will skip some, but try to get as much covered as possible. I'll skip papers with Meeting Minutes for sure, and try to focus on those focusing on C++11 or C++14 features. As the papers are ordered by there Number (N3522 being the first), I'll go top down, so every blog post will contain different papers from different fields of C++ Standardization. As N352-24 are Reports about Active Issues, Defects and Closed Issues I'll skip them for now, also I will not read through Meeting Minutes and such.

N3525 - Polymorphic Allocators

This paper proposes to make allocators independent from the type, hence make it possible to have polymorphic allocators in C++. Currently, allocators are used often as template arguments in the STL, which brings the problem, that a std::vector<int, myalloc> is being a totally different type then std::vector<int>. Also the paper quotes, that a lot of the current existing C++ Code leverages object oriented techniques, which aren't reachable via compile time polymorphic bound allocators. The author makes two considerations about allocators:

  1. The allocator used to construct a container should also be used to construct the elements within that container.
  2. An object’s type should be independent of the allocator it uses to obtain memory.

While the first point is already part of the C++ Standard, the second is not. Its the opposite, currently the type of a container depends also on its allocator for example. The author proposes a new namespace in std: std::polyalloc. The name might be subject to change, it shall contain the polymorphic allocator interface for C++. The namespace will contain a abstract base class memory_resource, with the 3 pure virtual methods allocate(), deallocate() and is_equal(). The template polymorphic_allocator<T> acts as a wrapper around a memory_resource pointer, which allows separation of the objects type and allocator. So two objects of the type list<int, polymorphic_allocator<int> > are the same type, but maybe use totally different allocators. Also polymorphic_allocator gives memory_resource a C++11 allocator interface. The namespace polyalloc will also contain template aliases for all STL Containers (except std::array). The paper also shows an example on how to make use of different allocators in a std::polyalloc::vector<string_type>, and hence comparing strings allocated with different allocators directly. Something that yet cannot be done in C++11.

N3526 - Uniform initialization for arrays and class aggregate types

C++11 brought initialization lists, which ease initialization of different and complex types. This paper proposes a relaxation of the rules for eliding braces from aggregate initialization, in order to make initialization of class aggregates and arrays more uniform. A short example demonstrates the problem:

struct aggr_ex_t {
      int x[2][2];
  };

  aggr_ex_t bad  = {{1, 2}, {3, 4}};      // Error: Too many initializers
  aggr_ex_t good = {{{1, 2}, {3, 4}}};

This problem was discovered, when a different paper was being prepared, proposing more then one dimension for std::array. std::array is as such also the perfect example of a class aggregate, as it should behave just like a normal array, also in initialization, which currently requires extra braces in C++11. This paper aims at changing the standard in a way, to get rid of the extra braces in initialization.

N3527 - std::optional

The C++ Standard library yet lacks a optional type such as boost::optional is. This paper proposes adding such a type to the standard. Its full title is "A proposal to add a utility class to represent optional objects", and its based on boost::optional. The paper is in its 3rd Revision, the latest changes are making optional<T> hashable for hashable T's, adding/renaming member functions (value, value_or). And also removed the reference implementation, stating that only the non-trivial parts of the implementation shall be provided. The paper names a few use cases for optional types as:

  • clearly showing which function objects are optional
  • indicating a null-state (without using rawpointers)
  • controlling the lifetime of scope/resource guards manually
  • skipping the expensive (default) construction of an object

This paper has dependencies, one of them is RValue References for *this (N2439), which currently is only implemented by Clang the author states. It also requires that Standard Library components move, forward and member functions of initializer_list are constexpr (N3471). This paper has been already part of the Working Draft of the Standard N3485.

N3530 - Leveraging OpenMP Infrastructure for language level parallelization

Before C++11 there was no threading or parallelization support officially in the C++ Standard, but other industry standards like OpenMP (or Cilk) emerged for this. With C++11 came std::thread, but other solutions like OpenMP have been around for some time. And OpenMP has its own standard, applying to C and C++, which will reach Version 4.0 this year. OpenMP is also widely implemented, so you can use it with many compilers. This paper proposes to leverage the 15 years of expierence from OpenMP for the C++ Standard.

But, this proposal is not about adding OpenMP to the standard, its more about adding OpenMP support to the standard, as the authors state:

"The core of this proposal is that the language standards could adopt the already available runtime OpenMP API and use keywords, or some other mechanism, instead of using #pragmaS for identifying parallel regions"

For example the keyword parallelfor(size_t i =0; i<100; ++i) could replace the OpenMP Macro "#pragma omp parallel for". The paper offers a long section discussing on how C++ could be extended to leverage the OpenMP features, without using the directive based approach favoured by OpenMP.

In summary, the paper proposes two approaches to parallelism, parallel for and parallel task, and discusses which things can be done at language level, to ease the access/use of them. One advantage clearly is, that the OpenMP Infrastructure and APIs already exist in all modern Compilern, hence does not need to be implemented. Which could lead to a more rapid adoption and support for parallelization in C++1y.

N3531 - User Defined Literals for Standard Library types

C++11 offers User Defined Literals (UDL), but yet the Standard Library lacks them. There are no UDLs defined for types from the Standard Library yet. And this is, what N3531 is about. C++11 reserves UDL names not starting with an underscore for the standard. But yet the Standard Library make no use of them, even as the UDL Papers for C++11 already had useful examples like h for std::chrono::hours. But, also there can be problems, as not all ISO units could be implemented, as for example F or l. Also abbreviations can have different meanings, s for std::string or s for seconds? In this case the paper proposes that an overload of operator"" s() would allow both usage scenarios.

The paper proposes UDLs for the following Standard Library types:

  • std::basic_string, suffix s in inline namespace std::literals::string_literals
  • std::complex, suffixes i, il, i_f in inline namespace std::literals::complex_literals
  • std::chrono::duration, suffixes h, min, s, ms, us, ns in inline namespace std::literals::chrono_literals

The paper shows a possible implementation interface for those literals.

N3532 - C++ Dynamic Arrays

The DynArray proposal wants to fill a certain niche, which yet is not covered by the standard: Dynamic Arrays, that know their size only at runtime. Currently you'd have to allocate such an array with new (or use std::vector), this proposal proposes a new container class std::dynarray, which allocates its memory on the stack when possible, or on the heap. std::dynarray is not able to grow, hence providing a fixed size array for its lifetime. std::dynarray throws a std::bad_array_length when the array size succeeds a certain limit, and std::bad_alloc when no memory could be allocated.

N3533 - C++ Concurrent Queues

A proposal for a concurrent Queues in C++. The Standard Library yet provides std::deque as a queue implementation, but std::deque is not thread safe. This proposal aims on providing a thread safe concurrent queue. It aims to move from reference-based operations to value-based operations.

The basic operations are:

  • void queue::push(const Element&)
  • void queue::push(Element&&)
  • Element queue::value_pop()

The first two place an element onto the queue, the last removes the Element from the queue using move rather than copying. These operations will wait when the queue is full or empty. The implementation provides non-waiting versions (try_push/try_pop) returning queue_op_status::(full|empty|success). The paper does not require the implementation to be lockfree, but proposes a method bool queue::is_lock_free() for indicating which kind of implementation is used.

N3534 - C++ Pipelines

This paper could answer the question why C++ needs Pipelines. Piping is known from the unix shell, where a sequence of programs feeds on the output of the predecessor, and generates the output for the next program in the chain. Pipelines can be an efficient solution for multi threaded programs, where each member of the pipeline is seen as its own task, running inside its own thread.

A simple example:

# Get all error messages in the log, filter out the test account, and format them:
cat log.txt | grep '^Error:' | grep -v 'test@example.com' |
  sed 's/^Error:.*Message: //' > output.txt

 It is proposed to add a library for pipelines to the C++ Standard, that such a pipeline could be implemented in C++ as such:

(pipeline::from(input_queue) |
  bind(grep, "^Error") |
  bind(vgrep, "test@example.com") |
  bind(sed, "'s/^Error:.*Message: //") |
  output_queue).run(&threadpool);

 The proposal defines a pipeline as:

"A pipeline is made up of functions that read data from an input queue, transform it in some way, and write it to an output queue."

The proposed Pipeline library could make use of the Concurrent Queues, and also needs a standardized threadpool to execute its tasks on.

N3535 - C++ Stream Mutexes

This paper proposes a standard mechanism for locking streams. Currently, you have to do the synchronization of streams between threads yourself, there is not standard locking mechanism especially for streams. A short example on how such a mechanism could look like:

std::ostringstream stream;
stream_mutex mstream(stream);
mstream << "1" << "2" << "3" << "4" << "5" << std::endl;

 All output/input operations are handled over the mutex, synchronizing is done by the mutex. All operations in a single expression chain are locked as one unit.

N3536 - C++ Sized Deallocation

C++11 introduced operator delete as a member function, which can be implemented with a size parameter. Still, C++ does not offer a global operator delete with such a parameter. In modern allocators, the practice to not store the size of the object near the object and to allocate such objects in size categories, is common. Deallocation requires to search for the right size category, in order to delete the object. The paper suggest to allow implementations and programmers to define global operator delete with a size argument. Which would be called preferably. This brings a few problems with legacy code and backward compatibility, especially code which overwrites the global operator delete, and yet can't offer the size version. Google has implemented this proposal successful with TCMalloc.

The suggested implementation would have this interface:

operator delete(void* ptr, std::size_t size) throw();
operator delete(void* ptr, std::size_t size, const std::nothrow_t&) throw();
operator delete[](void* ptr, std::size_t size) throw();
operator delete[](void* ptr, std::size_t size, const std::nothrow_t&) throw();

N3537 - Clarifying Memory Allocation

This paper aims at rephrasing the Memory Allocation part in the C++ Standard to a more detailed version. It proposes to replace existing mechanistic wording with wording more precisely focused on essential requirements.

N3538 - Pass by const Reference or Value

Passing an argument by const Reference or Value has the result that the original Value is not changed, yet its not the same, and the programmer has to choose one of them. This paper focuses on the problem of aliasing, when a output and input parameter is passed to a function by reference:

extern type ra1(const type& input);
extern type ra2(const type& input);

void rf1(type& output, const type& input) {
    output += ra1(input);
    output += ra2(input);
}

This will lead to a problem, if output and input reference the same object. In this case, ra2 will see a different input, then ra1. The paper discusses several solutions to this problem, and concludes that one is the possibility of introducing input types to C++: const type| input. The '|' being a new type qualifier, for input types. An alternative would be to introduce a new keyword, restrict, as C11 offers this.

N3542 - Proposal for Unbounded-Precision Integer type

The C++ Standard has currently no class or library for handling large integers. Some integers are so large, that they can't be handled with the standard integer types. There is already a number of libraries dealing with this problem, the author suggest to add this ability to the standard. This paper proposes two unbounded-precision integer types:

  • integer represents signed integer values
  • bits represents an unbounded set of bit values

In order to support interoperability, objects of either type can be constructed from values of any of the standard integer types. So code like this just works:

integer i = 30000;
integer j = 1000 * i;

bits b = 0xFF;
bits c = b & 0xAA;

This proposal has a long Issue list, with most Issues already being resolved. One remaining is that the user should be able to provide an allocator for the integer type, but this would complicate the interface, as the allocator would also bind to the type, hence turning to integer with different allocators into different types. Maybe the proposal for Polymorphic Allocators could solve this issue. Also it is proposed to add this ability over the header <seminumeric> to the standard.

N3543 - Priority Queue, Queue and Stack: Changes and Additions.

Currently in C++11 std::priority_queue, std::stack and std::queue are implemented as template adaptors providing limited functionality. This proposal aims at replacing the template adaptors with container classes, and make the template adaptors deprecated. Also, the authors would like to add different alternative implementation of heap classes to the standard, to provide a broader set of containers. This proposal is inspired by the boost::heap library.

N3545 - An Incremental Improvement to integral_constant

The template class integral_constant serves the purpose to be a type wrapper for compile time constant values. It is also the base class of the C++ type traits. This paper aims at improving this concept and proposes to add an operator() to the integral_constant class, returning the value of its data member. Implementing this new operator() with constexpr will allow its use at compile time. This would allow the derived classes and traits from integral_constant to be used as a function object:

std::is_arithmetic::value// per TR1 & C++11
static_cast(std::is_arithmetic{})// per C++11
std::is_arithmetic{}()// as proposed

N3546 - TransformationTraits Redux

"This paper proposes to augment C++11’s TransformationTraits with a number of template aliases whose use dramatically simplifies the traits’ most common applications."

A TransformationTrait is a template, that takes a type to transform as its template parameter plus optional addional parameters that help to define the modification. It exposes the modified type as TransformationTrait<TypeToModify, ...>::type. TransformationTraits are for example:

  • add_const
  • remove_reference
  • make_signed/make_unsigned
  • remove_extent/remove_all_extent
  • add_pointer/remove_pointer
  • enable_if

The paper suggests, that turning those TransformationTraits into template aliases will make the use of them easier and less error prone to the programmer.

N3547 - Three <random> related Proposals

I heared you like proposals... This paper makes 3 proposals, which are all related to <random>.

Those three are:

  • adding a function template to <algorithm>
  • adding a few novice-friendly functions to <random>
  • to deprecate some related legacy interfaces

Starting with <algorithm>, it was proposed to add random_sample and random_sample_n to the Standard Library in C++11. But then considered to propose them for TR2 instead, as they might not be understand well enough for Standardization back then. This paper now proposes to unify random_sample and random_sample_n as sample.

The novice-friendly functions considered adding to <random> are:

  • global_urng() - returns an implementation defined global Universal Random Number Generator.
  • randomize() - sets the above global URNG object to an (ideally) unpredictable state
  • pick_a_number(from, thru) - returns an int number in range[from,thru]
  • pick_a_number(from, upto) - returns a double number in the open range[from,upto)

It is also proposed to give the algorithm shuffle and the above proposed sample a default argument of type UniversalRandomNumberGenerator&&, with the default value returned by global_urng(). Also the proposal aims for the deprecation of rand(), srand() and RAND_MAX from <cstdlib>, plus random_shuffle() from <algorithm>, in order to provide a more precise and user friendly interface for randomness in C++.

N3548 - Conditionally-supported Special Math Functions for C++14

First a fact: There only two proposals mentioning C++14 in its title, this is one of them. Which does not mean, that they are the only ones aiming for C++14... So, this paper is about Special Math Functions, which could be conditionally supported for C++14. The paper claims, that adding those functions to the C++ Standard would help the numeric computing communities to adopt C++. Also it states, that these functions are not only useful for the scientific or engineering domain, but are less commonly used in other domains.

There is the need for some explanation (which is also stated in the paper), Special Match Functions were already in discussion for TR1, but left out. The reason was, that it was seen as a burden to the compiler vendors to implement those Special Math Functions. This is also valid for C++11. Today there exists a ISO Standard for Mathematical Special Functions, which could now be added to C++14. The paper proposes now, to add this standard as conditionally-supported. Where conditionally-supported is defined as:

"program construct that an implementation is not required to support" [DuT12]

This solves the problem of implementation for the compiler vendors, but still lets Special Math Function become a part of the C++ Standard.

 

And this is the end of Part 1. In total 18 papers covered for now, I plan to publish Part 2 by next week. I hope to be able to cover all papers in 3-4 Parts in total.

Link to Part 2!

 

Go back

Follow Meeting C++

tl_files/mcpp/yt.pngtl_files/mcpp/gplus-50.pngtl_files/mcpp/twitter.pngtl_files/mcpp/facebook.png