[cpp-threads] Some concerns with N2324

Roger Orr rogero at howzatt.demon.co.uk
Sun Jul 15 23:19:31 BST 2007


The BSI C++ panel met last week and we have a few concerns over the revised
N2324.  I have attempted to summarise our issues below.

Our main concern with the paper is with the structs, and in particular the
C++ specific parts.
We feel there is a danger that, by attempting to make them easy to use,
it is too easy to abuse the atomic types.

(1) Implicit construction from, and conversion to, native type.

We fear that having conversion in both directions implicit is dangerous.

  atomic_int a1;
  a1 = a1; // won't compile, as copy assignment is marked 'delete'
  [assuming N2326]
  a1 = a1 + 1; // will compile (I believe) but is not atomic
  // rhs a1 converts to int, 1 is added and then operator=( int ) is used

We weren't sure whether we wanted a conversion operator to native type.
If so we'd like this conversion operator explicit
[subject to N2333 being adopted]

We also prefer the ctor to be explicit.

(2) We were unsure whether we liked operator++/--

(a) the unconventional return value
Prefix operator++/operator-- traditionally returns a reference to self.
As mentioned in the paper this is not appropriate for atomics.

However, we wondered if this might lead to problems - for example

void v( std::atomic_int const & a );

void func( std::atomic & shared_atomic )
{
  std::atomic_int shared_atomic;
  v( ++shared_atomic );
}

v() is now using a temporary, created from the value returned from ++,
rather than 'shared_atomic' itself.  This might be a hard problem to
resolve when some of the functions are templates.

(b) missing optimisation opportunity (perhaps)

Some platforms have special instructions for inc/dec, but the paper has
no inc/dec methods - the operators are simply wrappers for calls to 
fetch_add/fetch_sub with an argument of 1.  Would there be benefit in
having some explicit operations for these cases?

(3) We were worried by swap()

int main()
{
  atomic_int a1(1);
  atomic_int a2(2);

  a1.swap( a2 );
  std::cout << a1 << '/' << a2 << std::endl;
}

What do you *think* this should output?
We get 2/2 and think this is a problem; we would like this not to compile.

(4) Should we prefer a type to an enumeration?

We wondered whether the 'memory_order' could to be a set of typed objects,
like with new(nothrow), rather than an enumeration.
However this might cause problems with the C API.
Or would this be a possible application of strong enum (N2213)?

(5) We were unhappy today with public derivation of atomic<> specialisations
from the underlying type.
For instance:  template<> struct atomic< int > : atomic_int

We'd prefer containment to inheritance, with something like std::string's
c_str() member function to access the member data.

(6) We remain a little worried about the design whereby the same named
struct
has different semantics in C and C++.

Eg in C pretty well all you can do with an atomic_int is to pass it by
address to free functions.
In C++ you can call member functions, assign to it, etc.

This sort of 'dual-personality' type seems to be without a precedent in the
standard and we fear this may make the atomics harder to teach and use since
code valid in C is invalid in C++

Regards,
Roger.




More information about the cpp-threads mailing list