2037. atomic free functions incorrectly specified

Section: 32.2 [atomics.syn] Status: Resolved Submitter: Pete Becker Opened: 2011-03-01 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [atomics.syn].

View all issues with Resolved status.

Discussion:

In earlier specifications of atomics the template specialization atomic<integer> was derived from atomic_integer (e.g. atomic<int> was derived from atomic_int), and the working draft required free functions such as

int atomic_load(const atomic_int*)

for each of the atomic_integer types. This worked fine with normal function overloading.

For the post-Batavia working draft, N3193 removed the requirement that atomic<integer> be derived from atomic_integer and replaced the free functions taking pointers to atomic_integer with template functions taking atomic_type*, such as

template <class T> T atomic_load(const atomic_type*);

and a code comment explaining that atomic_type can be either atomic<T> or a named base class of atomic<T>. The latter possibility is supposed to allow existing implementations based on the previous specification to continue to conform.

From history, this allowance seems to imply that functions like atomic_load can be non-template free functions, as they were before. The explicit requirements do not allow this, and, by requiring that they be templates, make them far more complicated. As the specification is currently written, code that uses an implementation that uses a base class would have to provide an explicit template type:

atomic<int> my_atomic_int;
atomic_load<int>(&my_atomic_int);

That type argument isn't needed when atomic_type is atomic<T>, but cautious users would always provide it to make their code portable across different implementations of the standard library.

One possibility for the implementor would be to do some template meta-programming to infer the type T when there are no function parameters of type T, but without running afoul of the prohibition on adding parameters with default values (20.5.5.4 [global.functions]/3).

So the promise that implementations of the previous specification continue to conform has not been met. The specification of these free functions should be rewritten to support library code written to the previous specification or the vacuous promise should be removed.

[2011-03-08: Lawrence comments and drafts wording:]

One of the goals is to permit atomics code to compile under both C and C++. Adding explicit template arguments would defeat that goal.

The intent was to permit the normal function overloads for atomic_int when atomic_int is distinct from atomic<int>. That intent was not reflected in the wording.

Proposed Resolution

Explicitly permit free functions.

  1. Edit within the header <atomic> synopsis 32.2 [atomics.syn] as follows:

    // 29.6.1, general operations on atomic types
    // In the following declarations, atomic_type is either
    // atomic<T> or a named base class for T from
    // Table 145 or inferred from
    // Table 146.
    // In the following declarations, atomic-type is either 
    // atomic<T> or a named base class for T from
    // Table 145 or inferred from
    // Table 146.
    // If it is atomic<T>, then the declaration is a template
    // declaration prefixed with template <class T>
    template <class T>
    bool atomic_is_lock_free(const volatile atomic_typeatomic-type*);
    template <class T>
    bool atomic_is_lock_free(const atomic_typeatomic-type*);
    template <class T>
    void atomic_init(volatile atomic_typeatomic-type*, T);
    template <class T>
    void atomic_init(atomic_typeatomic-type*, T);
    template <class T>
    void atomic_store(volatile atomic_typeatomic-type*, T);
    template <class T>
    void atomic_store(atomic_typeatomic-type*, T);
    template <class T>
    void atomic_store_explicit(volatile atomic_typeatomic-type*, T, memory_order);
    template <class T>
    void atomic_store_explicit(atomic_typeatomic-type*, T, memory_order);
    template <class T>
    T atomic_load(const volatile atomic_typeatomic-type*);
    template <class T>
    T atomic_load(const atomic_typeatomic-type*);
    template <class T>
    T atomic_load_explicit(const volatile atomic_typeatomic-type*, memory_order);
    template <class T>
    T atomic_load_explicit(const atomic_typeatomic-type*, memory_order);
    template <class T>
    T atomic_exchange(volatile atomic_typeatomic-type*, T);
    template <class T>
    T atomic_exchange(atomic_typeatomic-type*, T);
    template <class T>
    T atomic_exchange_explicit(volatile atomic_typeatomic-type*, T, memory_order);
    template <class T>
    T atomic_exchange_explicit(atomic_typeatomic-type*, T, memory_order);
    template <class T>
    bool atomic_compare_exchange_weak(volatile atomic_typeatomic-type*, T*, T);
    template <class T>
    bool atomic_compare_exchange_weak(atomic_typeatomic-type*, T*, T);
    template <class T>
    bool atomic_compare_exchange_strong(volatile atomic_typeatomic-type*, T*, T);
    template <class T>
    bool atomic_compare_exchange_strong(atomic_typeatomic-type*, T*, T);
    template <class T>
    bool atomic_compare_exchange_weak_explicit(volatile atomic_typeatomic-type*, T*, T,
      memory_order, memory_order);
    template <class T>
    bool atomic_compare_exchange_weak_explicit(atomic_typeatomic-type*, T*, T.
      memory_order, memory_order);
    template <class T>
    bool atomic_compare)exchange_strong_explicit(volatile atomic_typeatomic-type*, T*, T,
      memory_order, memory_order);
    template <class T>
    bool atomic_compare_exchange_strong_explicit(atomic_typeatomic-type*, T*, T,
      memory_order, memory_order);
      
    // 29.6.2, templated operations on atomic types
    // In the following declarations, atomic_type is either
    // atomic<T> or a named base class for T from
    // Table 145 or inferred from
    // Table 146.
    template <class T>
    T atomic_fetch_add(volatile atomic-typeatomic<T>*, T);
    template <class T>
    T atomic_fetch_add(atomic-typeatomic<T>*, T);
    template <class T>
    T atomic_fetch_add_explicit(volatile atomic-typeatomic<T>*, T, memory_order);
    template <class T>
    T atomic_fetch_add_explicit(atomic-typeatomic<T>*, T, memory_order);
    template <class T>
    T atomic_fetch_sub(volatile atomic-typeatomic<T>*, T);
    template <class T>
    T atomic_fetch_sub(atomic-typeatomic<T>*, T);
    template <class T>
    T atomic_fetch_sub_explicit(volatile atomic-typeatomic<T>*, T, memory_order);
    template <class T>
    T atomic_fetch_sub_explicit(atomic-typeatomic<T>*, T, memory_order);
    template <class T>
    T atomic_fetch_and(volatile atomic-typeatomic<T>*, T);
    template <class T>
    T atomic_fetch_and(atomic-typeatomic<T>*, T);
    template <class T>
    T atomic_fetch_and_explicit(volatile atomic-typeatomic<T>*, T, memory_order);
    template <class T>
    T atomic_fetch_and_explicit(atomic-typeatomic<T>*, T, memory_order);
    template <class T>
    T atomic_fetch_or(volatile atomic-typeatomic<T>*, T);
    template <class T>
    T atomic_fetch_or(atomic-typeatomic<T>*, T);
    template <class T>
    T atomic_fetch_or_explicit(volatile atomic-typeatomic<T>*, T, memory_order);
    template <class T>
    T atomic_fetch_or_explicit(atomic-typeatomic<T>*, T, memory_order);
    template <class T>
    T atomic_fetch_xor(volatile atomic-typeatomic<T>*, T);
    template <class T>
    T atomic_fetch_xor(atomic-typeatomic<T>*, T);
    template <class T>
    T atomic_fetch_xor_explicit(volatile atomic-typeatomic<T>*, T, memory_order);
    template <class T>
    T atomic_fetch_xor_explicit(atomic-typeatomic<T>*, T, memory_order);
    
    // 29.6.3, arithmetic operations on atomic types
    // In the following declarations, atomic-integral is either 
    // atomic<T> or a named base class for T from 
    // Table 145 or inferred from 
    // Table 146.
    // If it is atomic<T>,
    // then the declaration is a template specialization declaration prefixed with
    // template <>
    template <>
    integral atomic_fetch_add(volatile atomic-integral*, integral);
    template <>
    integral atomic_fetch_add(atomic-integral*, integral);
    template <>
    integral atomic_fetch_add_explicit(volatile atomic-integral*, integral, memory_order);
    template <>
    integral atomic_fetch_add_explicit(atomic-integral*, integral, memory_order);
    template <>
    integral atomic_fetch_sub(volatile atomic-integral*, integral);
    template <>
    integral atomic_fetch_sub(atomic-integral*, integral);
    template <>
    integral atomic_fetch_sub_explicit(volatile atomic-integral*, integral, memory_order);
    template <>
    integral atomic_fetch_sub_explicit(atomic-integral*, integral, memory_order);
    template <>
    integral atomic_fetch_and(volatile atomic-integral*, integral);
    template <>
    integral atomic_fetch_and(atomic-integral*, integral);
    template <>
    integral atomic_fetch_and_explicit(volatile atomic-integral*, integral, memory_order);
    template <>
    integral atomic_fetch_and_explicit(atomic-integral*, integral, memory_order);
    template <>
    integral atomic_fetch_or(volatile atomic-integral*, integral);
    template <>
    integral atomic_fetch_or(atomic-integral*, integral);
    template <>
    integral atomic_fetch_or_explicit(atomic-integral*, integral, memory_order);
    template <>
    integral atomic_fetch_or_explicit(atomic-integral*, integral, memory_order);
    template <>
    integral atomic_fetch_xor(volatile atomic-integral*, integral);
    template <>
    integral atomic_fetch_xor(atomic-integral*, integral);
    template <>
    integral atomic_fetch_xor_explicit(volatile atomic-integral*, integral, memory_order);
    template <>
    integral atomic_fetch_xor_explicit(atomic-integral*, integral, memory_order);
    
  2. Edit [atomics.types.operations.general] paragraph 1+2 as follows:

    -1- The implementation shall provide the functions and function templates identified as "general operations on atomic types" in 32.2 [atomics.syn].

    -2- In the declarations of these functions and function templates, the name atomic-type refers to either atomic<T> or to a named base class for T from Table 145 or inferred from Table 146.

  3. In [atomics.types.operations.templ] delete paragraph 2:

    -1- The implementation shall declare but not define the function templates identified as "templated operations on atomic types" in 32.2 [atomics.syn].

    -2- In the declarations of these templates, the name atomic-type refers to either atomic<T> or to a named base class for T from Table 145 or inferred from Table 146.

  4. Edit [atomics.types.operations.arith] paragraph 1+2 as follows:

    -1- The implementation shall provide the functions and function template specializations identified as "arithmetic operations on atomic types" in 32.2 [atomics.syn].

    -2- In the declarations of these functions and function template specializations, the name integral refers to an integral type and the name atomic-integral refers to either atomic<integral> or to a named base class for integral from Table 145 or inferred from Table 146.

Proposed resolution:

Resolved 2011-03 Madrid meeting by paper N3278