Можно ли использовать сигнал внутри класса С++?

Я делаю что-то вроде этого:

#include <signal.h>

class myClass {
public: 
    void myFunction () 
    {
        signal(SIGIO,myHandler);
    }

    void myHandler (int signum)
    {
        /**
        * Handling code
        */
    }

}

Я работаю над Ubuntu, используя gcc.

Но он не будет компилироваться. Он жалуется на:

ошибка: аргумент с типом void (MyClass::)(int) не согласуется с void (*) (int)

Любые подсказки? Или, может быть, я просто не могу использовать сигнал внутри классов? Сигналы разрешены только в C?

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


person Pablo Herrero    schedule 05.12.2008    source источник
comment
Остерегайтесь того, что сигналы не являются потокобезопасными, поэтому вы можете не захотеть пересматривать это, если это необходимо.   -  person Robert Gould    schedule 05.12.2008
comment
Причина, по которой вам нужно использовать статический метод (как описано ниже), заключается в том, что для вызова метода вам нужен экземпляр класса (объект). Невозможно передать объект для сигнала, поэтому он должен быть обычным указателем функции (а не указателем метода).   -  person Martin York    schedule 05.12.2008
comment
@ Роберт Гулд: Верно; но не совсем по теме.   -  person Martin York    schedule 05.12.2008


Ответы (6)


Второй параметр signal должен быть указателем на функцию, принимающую int и возвращающую void. В signal вы передаете указатель на функцию member, принимающую int и возвращающую void (ее тип — void (myClass::*)(int)). Я вижу три возможности решить эту проблему:

1 - Ваш метод myHandler может быть статическим: это здорово, сделайте его статическим

class myClass 
{
  public:
    void myFunction () 
    {
        signal(SIGIO, myClass::myHandler);
    }

    static void myHandler (int signum)
    {
        // handling code
    }
};

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

class myClass 
{
  public:
    void myFunction () 
    {
        signal(SIGIO, myClass::static_myHandler);
    }

    void myHandler (int signum)
    {
        // handling code
    }

    static void static_myHandler(int signum)
    {
        instance.myHandler(signum);
    }

  private:
    static myClass instance;
};

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

class myClass
{
  public:
    void myFunction () // registers a handler
    {
        instances.push_back(this);
    }

    void myHandler (int signum)
    {
        // handling code
    }

    static void callHandlers (int signum) // calls the handlers
    {
        std::for_each(instances.begin(), 
                      instances.end(), 
                      std::bind2nd(std::mem_fun(&myClass::myHandler), signum));
    }
  private:
    static std::vector<myClass *> instances;
};

и где-то сделать один вызов

signal(SIGIO, myClass::callHandlers);

Но я думаю, что если вы в конечном итоге используете последнее решение, вам, вероятно, следует подумать об изменении дизайна вашего манипулятора :-)!

person Luc Touraille    schedule 05.12.2008
comment
Ваш второй пример не является стандартным С++. - person dalle; 05.12.2008
comment
Это потому, что я забыл статическое ключевое слово? Если да, то исправлено! - person Luc Touraille; 07.12.2008
comment
это ошибка, которую я получаю, когда следовал вашему второму методу error: 'instance' was not declared in this scope - person molecule; 24.08.2016
comment
@molecule: instance объявляется в конце класса в приватном разделе. - person Luc Touraille; 24.08.2016
comment
Я попробовал № 2, а также решение и получил ту же проблему с неопределенной ссылкой на `myClass::instance'. И я изменил myClass на класс, который использовал. Я знаю С++, но эта проблема ставит меня в тупик. - person Tim Herbert; 17.03.2020

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

Попробуй это:

class myClass {
  void myFunction () 
  {
    signal(SIGIO, myClass::myHandler);
  }

  static void myHandler (int signum)
  {
     // blabla
  }
};

Вам также следует прочитать ссылку, предоставленную Baget, параграф 33.2 часто задаваемых вопросов по C++.

person Jørn Jensen    schedule 05.12.2008

На самом деле обработчикам сигналов C++ не разрешается использовать какие-либо средства, отсутствующие как в C, так и в C++ (за исключением того, что в C++11 они могут использовать атомарные), и они должны использовать связь C. Цитирование проект C++11 n3242 раздел 18.10 «Другая поддержка среды выполнения» [support.runtime] (пункт 8),

Общее подмножество языков C и C++ состоит из всех объявлений, определений и выражений, которые могут появляться в правильно построенной программе на C++, а также в соответствующей программе на C. POF («простая старая функция») — это функция, которая использует только функции из этого общего подмножества и которая прямо или косвенно не использует какую-либо функцию, не являющуюся POF, за исключением того, что она может использовать функции, определенные в разделе 29, которые не являются функции-члены. Все обработчики сигналов должны иметь C-связь. POF, который можно использовать в качестве обработчика сигнала в соответствующей программе на C, не приводит к неопределенному поведению при использовании в качестве обработчика сигнала в программе на C++. Поведение любой другой функции, используемой в качестве обработчика сигнала в программе на C++, определяется реализацией.

(Статья 29 относится к атомарности.)

person SamB    schedule 31.01.2012

Кажется безумием, что вы можете публиковать новые ответы с меньшей репутацией, чем нужно, чтобы комментировать существующие, но вот так.

Ответ gekomad от 24 сентября 2015 года был тем, который я использовал для решения своей проблемы. Стоит отметить, что это будет работать совершенно очевидно только тогда, когда будет создан только один экземпляр myClass. в противном случае указатель статического объекта будет указывать на один из экземпляров (самый последний созданный), который может оказаться не нужным.

И, если это полезно кому-то еще, действительный URL-адрес 2018 года для вопроса часто задаваемых вопросов, связанного с парой ответов:

http://www.cs.technion.ac.il/users/yechiel/c++-faq/memfnptr-vs-fnptr.html

person Michael Firth    schedule 04.04.2018

Вы можете использовать шаблон singleton

    static MyClass &getInstance() {
    static MyClass instance;
    return instance;
person Abolfazl Abbasi    schedule 21.05.2021

person    schedule
comment
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и/или как этот код отвечает на вопрос, повышает его ценность в долгосрочной перспективе. - person NathanOliver; 24.09.2015