Подтвердить что ты не робот

Как избежать анонимных объектов С++

У меня есть класс ScopedLock, который может автоматически освобождать блокировку при выходе из области видимости. Однако проблема заключается в следующем: иногда члены команды пишут неверный код блокировки, например

{
    ScopedLock(mutex);   // anonymous
    xxx;
}

Вышеприведенный код неверен, поскольку объект ScopedLock создается и разрушается немедленно, поэтому он не может заблокировать ожидаемую область (xxx). Я хочу, чтобы компилятор дал ошибку при попытке скомпилировать такой код. Это можно сделать?

Я выполнил поиск g++ параметров предупреждения, но не смог найти правильный.

4b9b3361

Ответ 1

Чтобы этого избежать, введите макрос, который делает это для вас, всегда используя одно и то же имя для шкафчика:

#define LOCK(mutex) ScopedLock _lock(mutex)

Затем используйте его следующим образом:

{
    LOCK(mutex);
    xxx;
}

В качестве альтернативы блок Java synchronize может быть смоделирован с использованием макроструктуры: во время цикла, выполняющегося всегда ровно один раз, я создаю такой блокиратор в инструкции инициализации for-loop, поэтому он уничтожается, когда оставляя цикл for.

Однако в нем есть некоторые подводные камни, неожиданное поведение оператора break является одним из примеров. Этот "взлом" представлен здесь.


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

Ответ 2

Нет, к сожалению нет способа сделать это, как я узнал в блоге в прошлом году.

В этом я сделал вывод:

Я предполагаю, что мораль этой истории - запомнить эту историю при использовании scoped_lock s.


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

Вы ищете способ программно поймать эту конкретную ошибку, когда она сделана, и ее нет.

Ответ 3

Я видел интересный трюк в одной базе кода, но он работает только в том случае, если ваш тип scoped_lock не является шаблоном (std:: scoped_lock is).

#define scoped_lock(x) static_assert(false, "you forgot the variable name")

Если вы правильно используете класс, у вас есть

scoped_lock lock(mutex);

и поскольку идентификатор scoped_lock не сопровождается открытым парнем, макрос не будет запускаться, и код останется таким, какой он есть. Если вы пишете \

scoped_lock(mutex);

макрос будет запускаться, и код будет заменен на

static_assert(false, "you forgot the variable name");

Это приведет к информативному сообщению.

Если вы используете квалифицированное имя

threads::scoped_lock(mutext);

то результат все равно не будет компилироваться, но сообщение будет не таким приятным.

Конечно, если ваша блокировка является шаблоном, плохой код

scoped_lock<mutex_type>(mutex);

который не будет запускать макрос.

Ответ 4

Вы можете использовать класс и удаленные функции с тем же именем. К сожалению, для этого требуется добавить ключевое слово "class" перед типом.

class Guard
{
public:
  explicit Guard(void)
  {
  }
};

static void Guard(void) = delete;

int main()
{
  // Guard(); // Won't compile
  // Guard g; // Won't compile
  class Guard g;
}

Ответ 5

AFAIK нет такого флага в gcc. Статический анализатор может лучше соответствовать вашим потребностям.

Ответ 6

замените его макросом

#define CON2(x,y) x##y
#define CON(x,y) CON2(x,y)
#define LOCK(x)  ScopedLock CON(unique_,__COUNTER__)(mutex)

Использование

{
  LOCK(mutex);
  //do stuff
}

Этот макрос будет генерировать уникальные имена для блокировок, позволяя блокировать другие мьютексы во внутренних областях