Reputation: 5882
I have a C library that defined a bunch of platform specific macros for atomic operations. How can I use std::atomic
as the implementation for this?
For example the C code has:
#define mylib_atomic_int_add(_pi, _val) do_atomic_int_add(_pi, _val)
int number = 0;
mylib_atomic_int_add(&number, 7);
And the C++ platform abstraction layer (i.e a static library compiled as C++11 that is linked with the C code) has:
extern "C"
{
int do_atomic_int_add(volatile int* i, volatile int v)
{
return *i + v;
}
}
Obviously this isn't safe because it isn't atomic, since the int is declared in the C code std::atomic
can't be used there, but is there some way to use it within do_atomic_int_add()
? like return std::atomic<int>::add(*i, v);
?
Edit:
I don't know why no one understands what I'm trying to do. Its basically how do I atomically increment an int in C++ which has been passed over from a C function.
Upvotes: 3
Views: 2492
Reputation: 5642
C++11 atomics require you to use C++11 atomic types for atomic operations. You can step outside the standard by casting from non-atomic to atomic types, but this is not portable code, even if it often works. It's not even valid from an entirely C++ code, let alone a mixture of C and C++.
Because of this limitation, you will not be able to write a C++ atomics library with an interface to C, because you cannot declare C++11 atomic types in C code.
Hans Boehm, one of the foremost authorities on programming language concurrency features, has a proposal for C++0y titled N4013: Atomic operations on non-atomic data that explains some of the issues here.
You can solve this entirely within C, which is the safest and most portable solution.
The most common way to do atomics in C and C++ prior to the introduction of native language types in C11 and C++11 is with compiler intrinsics. There are two variants of these:
Both of these are supported by GCC, Clang and Intel compilers as well as other compilers (IBM for sure, Cray mostly but with at least one bug, and probably PGI). I don't use Windows but I know MSVC has some sort of compiler intrinsics for atomics.
In addition, there are at least two different open-source implementations of C atomics with permissive licenses:
Additionally, OpenMP 3+ gives you the ability to use atomics from C89 code, but I don't know if MSVC supports it sufficiently.
Finally, C11 atomics will solve your problem beautifully in an entirely ISO standard way. However, compiler support is rather limited right now. I have used these features with GCC 5+ and late-model Clang, and perhaps Intel 16, but I do not expect MSVC to ever support them.
I removed volatile
from the value, since it does nothing. The extern "C"
is not required except to turn off C++ name-mangling, as none of the implementations use C++. Because you didn't specify a memory ordering requirement, I assume relaxed (unordered) atomics are sufficient.
extern "C"
{
int do_atomic_int_add(volatile int* i, int v)
{
//return (*i + v);
return __sync_add_and_fetch(i,v);
}
}
extern "C"
{
int do_atomic_int_add(volatile int* i, int v)
{
//return (*i + v);
return __atomic_add_fetch(i,v, __ATOMIC_RELAXED);
}
}
extern "C"
{
int do_atomic_int_add(volatile int* i, int v)
{
int r;
#pragma omp atomic update
{ r = (*i + v); }
return r;
}
}
Upvotes: 4
Reputation: 179819
Formally, the problem is that std::atomic<int>
is a type whose objects can be modified in such a way that .load()
will only return values that are .store()
d. C doesn't call .load
so it can't use the int
directly.
Of course, you can define extern "C" int atomic_load(struct atomic_int*, enum std_memory_order)
with the obvious implementation. You have to essentially wrap std::atomic_int
in a C-compatible struct and forward all methods.
Upvotes: 1