将一些 Windows C++ 代码移植到 iOS 时,我需要提供 Win32 的
long InterlockedIncrement(long *p)
调用的实现。使用 <libkern/OSAtomic.h>
中定义的函数很容易做到这一点。
但是,我想知道是否可以仅使用 C++11 工具以与操作系统无关的方式编写它,主要是
<atomic>
。我想出了这个,但我不确定它是否能实现我想要的:
inline long InterlockedIncrement(long* p)
{
std::atomic<long&> atomicP(*p);
return ++atomicP;
}
这个有用吗?这样就够了吗?这两行不是原子的,但增量应该是原子的,这是这里的关键。
我发现的所有
<atomic>
的使用示例都不同,其中 std::atomic<T>
是直接定义和使用的。在这里,我想使用调用者通过地址传递给我的现有长变量。我找不到这样的例子。
编辑:Clang 3.2(在 Xcode 4.x 中)无法编译
++atomicP
,并出现错误“无法增加 std::atomic<long&>
类型的值”(也不是 atomicP += 1
)。
正确的方法是什么?
再次编辑:指针实现编译...
inline long InterlockedIncrement(long* p)
{
std::atomic<long*> atomicP(p);
return ++(*atomicP);
}
但是恐怕这不起作用,因为我不增加原子类型,而是增加指针指向的值,这不是原子的。
您的示例实现是每次从指针构造一个新的原子。这不是 std::atomic 的预期用途,我不相信它会按照您想要的方式工作。
据我所知,完成您想要做的事情的唯一方法(以独立于平台的方式消除对 InterlockedIncrement 的依赖)是用 std::atomic 替换您当前调用 Win32“互锁”调用的变量的所有声明它们的版本。然后,您可以删除互锁调用并使用常规值语义以原子方式修改变量。无论如何,这更具可读性(并且将来更易于维护)。
我知道您希望保留现有的(经过充分测试的)代码,但我认为您不能在您的情况下这样做。
__atomic_add_fetch
GCC 扩展
在GCC 4.8中,C++标准库通过GCC内置的
std::atomic::operator++
实现了__atomic_add_fetch
,所以你可以写:
inline long InterlockedIncrement(long* p)
{
return __atomic_add_fetch(p, 1, __ATOMIC_SEQ_CST);
}
我不确定 clang,但似乎有一些选项,例如
__c11_atomic_fetch_add
http://clang.llvm.org/docs/LanguageExtensions.html
正如其他人提到的,参数
p
本身必须是 std::atomic
才能仅使用标准库方法:将指针转换为原子并没有帮助,因为原子指针仅作用于指针,而不作用于它的对象指向。