1
  • I want to use std::compare_exchange_strong for some std::atomic<int>
  • For compilation reasons (int &) I am forced to introduce int _OLD_VALUE = OLD_VALUE.
  • Is there a more elegant way to achieve this?
  • Here is my example
#include <atomic>
#include <stdio.h>
#define OLD_VALUE 16
#define NEW_VALUE 744
#define OTHER_VALUE 80
int main(int argc, char **argv)
{
    std::atomic<int> i(OTHER_VALUE);
    int _OLD_VALUE = OLD_VALUE;
    bool    status = i.compare_exchange_strong(_OLD_VALUE,NEW_VALUE);
    // bool status = i.compare_exchange_strong( OLD_VALUE,NEW_VALUE);
    if (status) { printf("good\n"); }
    return 0;
}

And here is the compilation error when I use the commented version:

main.cpp: In function ‘int main(int, char**)’:
main.cpp:11:65: error: cannot bind non-const lvalue reference of type ‘std::__atomic_base<int>::__int_type& {aka int&}’ to an rvalue of type ‘int’
     bool status = i.compare_exchange_strong( OLD_VALUE,NEW_VALUE);
                                                                 ^
In file included from /usr/include/c++/7/atomic:41:0,
                 from main.cpp:1:
/usr/include/c++/7/bits/atomic_base.h:496:7: note:   initializing argument 1 of ‘bool std::__atomic_base<_IntTp>::compare_exchange_strong(std::__atomic_base<_IntTp>::__int_type&, std::__atomic_base<_IntTp>::__int_type, std::memory_order) [with _ITp = int; std::__atomic_base<_IntTp>::__int_type = int; std::memory_order = std::memory_order]’
       compare_exchange_strong(__int_type& __i1, __int_type __i2,
       ^~~~~~~~~~~~~~~~~~~~~~~

OrenIshShalom
  • 5,974
  • 9
  • 37
  • 87
  • Ref. [Why is it allowed to pass R-Values by const reference but not by normal reference?](https://stackoverflow.com/questions/36102728/why-is-it-allowed-to-pass-r-values-by-const-reference-but-not-by-normal-referenc) – Sander De Dycker Sep 03 '20 at 08:36

4 Answers4

2

No. The reason being is that the previous value of the variable is exchanged so the expected value is overwritten if the compare mismatches.

To see what's going on under the hood, look at the GCC built-ins: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html

You will note there is a __atomic_exchange_n in the builtins for GCC (Linux) but that simply provides an exchange as opposed to a compare-and-swap. The Windows equivalent is InterlockedExchange : https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-interlockedexchange

For the sake of readability I would avoid using capitals in variable names and avoid leading underscores:

int oldValue = OLD_VALUE;
Den-Jason
  • 2,395
  • 1
  • 22
  • 17
1

The simplest way (and I suppose the only way) to achieve this is to just write simple wrapper:

bool cas_strong(std::atomic<int>& a, int cmp, int exc) {
  return a.compare_exchange_strong(cmp, exc);
}
bartop
  • 9,971
  • 1
  • 23
  • 54
0

compare_exchange_strong expects an int& to store the current value found in i. Here, you provide indirectly 16 (that is what your macro OLD_VALUE is replaced with at pre-processing), which is an integer compile-time constant, a.k.a. constexpr int&. This is not compatible with int&.

To provide an int&, you should better keep an int near your call of compare_exchange_strong:

std::atomic<int> i(OTHER_VALUE);
int old_value = OLD_VALUE;
bool status = i.compare_exchange_strong(old_value, NEW_VALUE);
if (status) { printf("good\n"); }
return 0;

Also, more generally, it is clearly more powerful if you used static constants instead of macros here. More on this in this other question: What is the difference between a macro and a const in C++?

Victor Paléologue
  • 2,025
  • 1
  • 17
  • 27
-2

Don't use macros to define the values:

#include <atomic>
#include <stdio.h>
int OLD_VALUE 16
int NEW_VALUE 744
int OTHER_VALUE 80
int main(int argc, char **argv)
{
    std::atomic<int> i(OTHER_VALUE);
    bool status = i.compare_exchange_strong( OLD_VALUE,NEW_VALUE);
    if (status) { printf("good\n"); }
    return 0;
}
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185