Я хотел атомарно сравнить и поменять местами переменную pid_t. Я читал в стандарте, что это тип int.
Я знаю, что atomic_compare_exchange_strong_explicit() может управлять вещами самостоятельно. Все, что мне нужно сделать, это установить тип как _Atomic(pid_t).
Но поскольку я работаю над macOS и хочу сделать ее совместимой со старой библиотекой OSX, а именно libkern/OSAtomic.h, мне нужно знать тип и размер типа для CAS.
Как и в случае с size_t, я мог бы просто сделать так:
# ifdef __LP64__
# define CAS_size_t(old, new, mem) \
OSAtomicCompareAndSwap64((int64_t) (*old), (int64_t) (new), (volatile int64_t *) (mem))
# else
# define CAS_size_t(old, new, mem) \
OSAtomicCompareAndSwap32((int32_t) (*old), (int32_t) (new), (volatile int32_t *) (mem))
# endif
Но в случае pid_t я не уверен, потому что даже если __LP64__
не определено, то это может быть int64_t, int32_t, int16_t или что-то в этом роде?
Вероятно, sizeof(pid_t)
не будет работать в условии #if right. Он предварительно обработан?
Точно, я забыл, что
У вас есть С11? Вы можете пойти с решением _Generic. Или просто передайте sizeof(var)
функции... Почему бы не просто #define CAS_size_t(old, new, mem) do { if (sizeof(*old) == sizeof(int64_t)) { call_64(...); } else if (sizeof(*old) == sizeof(int32_t)) { call_32(...); } else assert(0); } while(0)
. В любом случае проверка, вероятно, будет оптимизирована компилятором.
Что ж, для C11 stdatomic.h уже является решением, но главная цель — сделать его и обратно совместимым. Вероятно, мне нужно придерживаться функции в этом случае. Также спасибо, что поделились этой _Generic
вещью, я не знал об этом.
@KamilCuk, ты прав. Спасибо и за это, раньше вообще не приходило в голову.
Просто проверьте размер значения внутри макроса:
#define OSAtomicCompareAndSwap(old, new, mem) do{ \
if (sizeof(*old) == sizeof(int32_t)) { \
OSAtomicCompareAndSwap32((int32_t)(*old), (int32_t) (new), (volatile int32_t *) (mem)); \
} else if (sizeof(*old) == sizeof(int64_t)) { \
OSAtomicCompareAndSwap64((int64_t)(*old), (int64_t) (new), (volatile int64_t *) (mem)); \
} else assert(0); }while(0)
В любом случае проверки должны быть оптимизированы компилятором. Это не проверка типов (для этого нам понадобится C++-ish typeid), проверяется только размер.
Если вам нужно вернуть значение, если вы считаете, что вам нужно передать другую переменную:
#define OSAtomicCompareAndSwap(ret, old, new, mem) do{ \
if (sizeof(*old) == sizeof(int32_t)) { \
ret = OSAtomicCompareAndSwap32((int32_t)(*old), (int32_t) (new), (volatile int32_t *) (mem)); \
} else if (sizeof(*old) == sizeof(int64_t)) { \
ret = OSAtomicCompareAndSwap64((int64_t)(*old), (int64_t) (new), (volatile int64_t *) (mem)); \
} else assert(0); }while(0)
или экс. передать указатель на переменную, которая будет связана с memcpy результатом или чем-то подобным. Или вы можете использовать расширение gcc выражение оператора.
С выражением оператора, которое может выглядеть так:
#define OSAtomicCompareAndSwap(old, new, mem) __extension__({ \
int64_t ret = 0; \
if (sizeof(*old) == sizeof(int32_t)) { \
ret = OSAtomicCompareAndSwap32((int32_t)(*old), (int32_t) (new), (volatile int32_t *) (mem)); \
} else if (sizeof(*old) == sizeof(int64_t)) { \
ret = OSAtomicCompareAndSwap64((int64_t)(*old), (int64_t) (new), (volatile int64_t *) (mem)); \
} else { \
assert(0); \
} \
ret; \
})
Спасибо чувак. Это расширение statement expression
наверняка мне очень поможет, приятно знать так много в одном посте.
вы должны определить макросы без завершающего ';' потому что вы бы написали ";" в любом случае после вызова макроса. В том виде, в котором он есть сейчас, это не будет компилироваться: if ( .. ) OSAtomicCompareAndSwap(old,new,mem);else ...
Все, что вы знаете, это то, что это некоторый целочисленный тип со знаком, способный представлять идентификатор процесса. У вас могут быть случаи
#if
, основанные на значенииsizeof(pid_t)
, и выдавать ошибку компиляции в случае#else
.