Assume an array of atomic variables and a class that regulates access to that array by overloading the class' []
-operator to return a reference to the atomic variable at position idx
:
class MyClass {
public:
MyClass()
{
//initalize every array element with nullptr
for (auto& it : array) {
it = nullptr;
}
}
std::atomic<int*>& operator[](const size_t idx)
{
//there is some more code here, doing basic safety checks,...
return array[idx];
}
private:
std::array<std::atomic<int*>, 1000> array;
}
We can access elements of array
like this:
MyClass foo();
int *a = foo[0];
int b = 3;
foo[1] = &b
Note that any access to such an element will by default be done using memory_order_seq_cst
. To change the enforced memory order, one could do:
int *a = foo[0].load(memory_order_acquire);
foo[1].store(&b, memory_order_release);
But how can I change implementation of the []
-operator such that memory_order_acquire
is used for all reads and memory_order_release
is used for all writes? The reason why I want to do this in the definition of the []
-operator is that there are a lot of accesses the elements of array
at a lot of different locations in the source and I do not want to spread the used memory ordering to all of them.
EDIT: As discussed in the comments, one could replace the []
-operator with a getter and setter. However, that would require all accesses to be replaced by the appropriate function; plus I am interested whether or not it is possible to do it the way I outlined above.
You can use an intermediate reference object that is the result of the operator[]
. This object then applies the load or store operation based on how the object is used in a future expression.
class MyClass {
struct Ref {
std::atomic<int *> &ref_;
Ref (std::atomic<int *> &r) : ref_(r) {}
operator int * () const {
return ref_.load(std::memory_order_acquire);
}
int * operator = (int *ptr) const {
ref_.store(ptr, std::memory_order_release);
return ptr;
}
};
public:
//...
Ref operator[](const size_t idx)
{
//there is some more code here, doing basic safety checks,...
return array[idx];
}
//...
};
Then, the conversion operator or the assignment operator will use the right memory order constraint:
MyClass foo;
int *a = foo[0]; // uses conversion operator, load(acquire)
int b = 3;
foo[1] = &b; // uses assignment operator, store(release)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With