3

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.

38
  • 6
    Are you trying to use a c++ feature in c? That is never a good approach. I don't really understand your question. Commented Oct 5, 2015 at 22:02
  • 3
    I have a feeling that the volatile keyword does not do what you assume it does. Commented Oct 5, 2015 at 22:02
  • 5
    C11 has stdatomic.h, like C++11. You should not use some obscure library. And you might be confusing C and C++. They are two different languages. Commented Oct 5, 2015 at 22:04
  • 3
    MSVC is no standard compliant compiler. You should not use it for modern C anyway. Commented Oct 5, 2015 at 22:14
  • 1
    @Olaf - What's so hard about "compile C++ code with C++ compiler, compile C code with C compiler, have C code call C++ code via an extern "C" interface, and link using C++ compiler"? Or you can compile a DLL or shared object using a C++ compiler, making sure the library records its dynamic dependenc(ies) on the proper C++ runtime libraries. Commented Oct 5, 2015 at 22:25

2 Answers 2

5

Why this question is invalid

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.

A different way to solve the problem

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.

Code

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.

__sync version

extern "C" { int do_atomic_int_add(volatile int* i, int v) { //return (*i + v); return __sync_add_and_fetch(i,v); } } 

__atomic version

extern "C" { int do_atomic_int_add(volatile int* i, int v) { //return (*i + v); return __atomic_add_fetch(i,v, __ATOMIC_RELAXED); } } 

OpenMP version

extern "C" { int do_atomic_int_add(volatile int* i, int v) { int r; #pragma omp atomic update { r = (*i + v); } return r; } } 
Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.