GCP прагма для добавления/удаления параметров компилятора в исходном файле

Я разработал кросс-платформенную библиотеку, которая обеспечивает справедливое использование type-punning в сокетной связи. Эта библиотека уже используется в нескольких проектах, некоторые из которых я, возможно, не знаю.

Использование этой библиотеки неправильно может привести к опасному поведению Undefined. Я хотел бы как можно лучше убедиться, что эта библиотека используется должным образом.

Помимо документации, конечно, под g++ лучшим способом, который я знаю, является использование опций -fstrict_aliasing и -Wstrict-aliasing.

Есть ли способ под GCC применять эти параметры на уровне исходного файла?

Другими словами, я хотел бы написать что-то вроде следующего:

MyFancyLib.h

#ifndef MY_FANCY_LIB_H
#define MY_FANCY_LIB_H

#pragma (something that pushes the current compiler options)
#pragma (something to set -fstrict_aliasing and -Wstrict-aliasing)

// ... my stuff ...

#pragma (something to pop the compiler options)

#endif

Есть ли способ?

Ответ 1

Давайте начнем с того, что я считаю ложной предпосылкой:

Неправильное использование этой библиотеки может привести к опасно неопределенному поведению. Я хотел бы в меру своих возможностей убедиться, что эта библиотека используется должным образом.

Если ваша библиотека выполняет штампование таким образом, что -fstrict-aliasing ломается, то она имеет неопределенное поведение в соответствии со стандартом C++ независимо от того, какие флаги компилятора переданы. Тот факт, что программа, похоже, работает на определенных компиляторах при компиляции с определенными флагами (в частности, -fno-strict-aliasing), это не меняет.

Поэтому лучшим решением будет сделать то, что сказал Флориан: измените код, чтобы он соответствовал спецификации языка C++. Пока вы не сделаете это, вы будете постоянно на тонком льду.

"Да, да", говорите вы, "но до тех пор, что я могу сделать, чтобы смягчить проблему?"

Я рекомендую включить проверку во время выполнения, используемую во время инициализации библиотеки, чтобы обнаружить условие компиляции таким образом, что это приведет к неправильной работе. Например:

// Given two pointers to the *same* address, return 1 if the compiler
// is behaving as if -fstrict-aliasing is specified, and 0 if not.
//
// Based on https://blog.regehr.org/archives/959 .
static int sae_helper(int *h, long *k)
{
  // Write a 1.
  *h = 1;

  // Overwrite it with all zeroes using a pointer with a different type.
  // With naive semantics, '*h' is now 0.  But when -fstrict-aliasing is
  // enabled, the compiler will think 'h' and 'k' point to different
  // memory locations ...
  *k = 0;

  // ... and therefore will optimize this read as 1.
  return *h;
}

int strict_aliasing_enabled()
{
  long k = 0;

  // Undefined behavior!  But we're only doing this because other
  // code in the library also has undefined behavior, and we want
  // to predict how that code will behave.
  return sae_helper((int*)&k, &k);
}

(Выше приведено C, а не C++ только для упрощения использования на обоих языках.)

Теперь в вашей процедуре инициализации вызовите strict_aliasing_enabled(), и если он вернет 1, немедленно выведите сообщение об ошибке, говорящее о том, что библиотека была скомпилирована неправильно. Это поможет защитить конечных пользователей от неправильного поведения и предупредить разработчиков клиентских программ о том, что им нужно исправить свою сборку.

Я проверил этот код с gcc-5.4.0 и clang-8.0.1. Когда передается -O2, strict_aliasing_enabled() возвращает 1. Когда передается -O2 -fno-strict-aliasing, эта функция возвращает 0.

Но позвольте мне еще раз подчеркнуть: мой код имеет неопределенное поведение! Нет (может быть) никакой гарантии, что это сработает. Соответствующий стандарту компилятор C++ может скомпилировать его в код, который возвращает 0, дает сбой или инициирует глобальную термоядерную войну! Что также относится к коду, который, по-видимому, уже используется в других местах библиотеки, если вам нужно, чтобы -fno-strict-aliasing вел себя так, как задумано.

Ответ 3

Если ваша библиотека является только библиотекой только для заголовка, я думаю, что единственный способ справиться с этим - исправить нарушения строгих алиасов. Если нарушения встречаются между вами, вы можете использовать обычные трюки с участием союзов или атрибут типа may_alias. Если ваша библиотека использует предопределенные типы sockaddr, это может быть затруднено.