LINUX.ORG.RU

Как сделать реализацию виртуального метода статическим?

 , ,


0

2

Есть у меня базовый класс AbstractChannel. От него наследуется три класса: FileChannel, TcpChannel, ComChannel. Выглядит он примерно вот так:

class AbstractChannel : public QObject
{
    Q_OBJECT

public:
    AbstractChannel(QObject *parent = 0);
    virtual ~AbstractChannel();

    virtual int getChar(num_t Port);
    virtual bool isFileDef(num_t Port);

    virtual void writeProc(const void *ptrs, size_t size, num_t Port);
    virtual bool isRw(num_t Port);
    virtual TCHAN_Type channelType(num_t Port);
};

Создавался AbstractChannel для того, чтобы можно было передать указатель на производные классы FileChannel/TcpChannel/ComChannel в некий класс парсера.

Проблема в том, что парсер использует все вышеперечисленные методы как методы обратного вызова. Ну, к примеру, если парсеру нужно получить данные из потока, он вызывает метод getChar(). И поэтому все эти методы должны быть статическими.

Однако, если в классах FileChannel/TcpChannel/ComChannel сделать реализацию этих методов со спецификатором static, то будет ошибка:
error: ‘static int FileChannel::getChar(num_t)’ cannot be declared
since ‘virtual int AbstractChannel::getChar(num_t)’ declared in base class
     virtual int getChar(num_t Port);
                 ^~~~~~~

Да и если прописать static в сам базовый класс, то тоже будет ошибка что использование virtual static недопустимо.

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

★★★★★

Ответ на: комментарий от Xintrea

Потому что либа для С, а не для С++ с сахаром. Попробуй использовать обертку


AbstractChannel *channel1_ptr = nullptr;

static int channel1_getChar(arg) {
  return channel1_ptr->getChar(arg);
}


int main() {
  channel1_ptr = new FileChannel();
  Parser->GetChanApi().onCHAN_Getc = &channel1_getChar;
}
Коряво, но работает на ура :)

Jetty ★★★★★
()
Последнее исправление: Jetty (всего исправлений: 1)
Ответ на: комментарий от Xintrea

Для начала, вам с «писателем» парсера нужно определиться, для какого языка пишется парсер — для С или для С++ ))

Если же парсеро-писатель окажется упертым и будет настаивать на интерфейсе в виде структуры с указателями на статичные функции, то не надо городить весь этот огород с абстрактным классом и его наследниками — можно реализовать несколько совершенно разных классов с соответствующими статичными методами, плюс, определить отдельно интерфейсную структуру для парсера. Ну и в каждом потоковом классе предусмотреть метод инициализации интерфейсной структуры (или, к примеру, ручками её заполнять).

vinvlad ★★
()
Ответ на: комментарий от vinvlad

Для начала, вам с «писателем» парсера нужно определиться, для какого языка пишется парсер — для С или для С++ ))

А какая разница? Если в callback не передаётся указатель на пользовательские данные, то и на голых сях придётся делать изврат с глобальной переменной. Налицо косяк архитектуры.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

А какая разница? Если в callback не передаётся указатель на пользовательские данные, то и на голых сях придётся делать изврат с глобальной переменной. Налицо косяк архитектуры.

Если всё пишется на C++, то писателю парсера логичнее было бы принимать в качестве интерфейса указатель на тот самый абстрактный класс, а не указатель на структуру - это более правильное и элегантное решение. Ну и пользовательские данные при вызове callback-ов не нужны.

Собственно, пользовательские данные необязательны и в случае C-шной интерфейсной структуры, если инициализировать её указателями на методы разных потоковых классов. Каждый потоковый класс - это совершенно отдельная реализация и он может хранить всё необходимое внутри себя в статичных переменных (всё это доступно и его соответствующим методам).

Проблема в том, что писатель парсера мыслит чисто в C-шном стиле (и интерфейс соответствующий придумал), а пользователь парсера (ТС) норовит мыслить только в терминах плюсовых классов с общим абстрактным родителем.

vinvlad ★★
()
Ответ на: комментарий от ox55ff

зачем же глобальная, достаточно скоупом файла обойтись :)

Jetty ★★★★★
()
Ответ на: комментарий от iSage

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

А объект, к которому привязан метод, который должен дёрнуться из этой твоей сишной функции, ты будешь в астрале хранить?

korvin_ ★★★★★
()
Ответ на: комментарий от korvin_

Его можно хранить где угодно. Внутри другого класса, например. Глобальной в этом случае будет только std::function

iSage ★★★★
()
Ответ на: комментарий от invy

Тут в треде уже написали, что можно трамплин сделать.

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

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от i-rinat

Ну там коллбеки с пользовательскими данными есть. Например, парсер нашел в потоке метку времени - вызвал соответствующую функцию пользователя, передав в нее время. Нашел в потоке значащие данные - вызвал соответствующую функцию пользователя, передав в нее эти данные.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea
typedef int (*getChar)(num_t) GetCharPtr;
class Abstract{
    virtual GetCharPtr RealgetChar() = 0;
}

class FileChannel: public Abstract{[br]
    virtual GetCharPtr RealgetChar();
}
/*....*/
GetCharPtr FileChannel::RealgetChar(){/*....*/}

/*....*/

Parser->GetChanApi().onCHAN_Getc = channel->RealgetChar();
anonymous
()
Ответ на: комментарий от utf8nowhere

Куда тимлид смотрит? Он вам уже руки должен был отрубить.

Ребята, советовать ТАКОЕ для ПРОДАКШЕНА!

anonymous
()

Так как библиотека суть быдлокод, соответственно, чтоб её заюзать нужно накатать свой быдлокод.

static в классе это фактически глобальная функция.

1. Если у тебя место общения с данной «библиотекой» только в одном месте, то заводишь глобальный метод и глобальную переменную и переключаешь её в нужный момент на свои реализации канала.

2. На каждую твою реализацию канала пишешь структуру со статическими методами и её возвращаешь в своём канале для общения с данной «библиотекой».

Но фактически для работы с данной «библиотекой» нужно программировать на с.

vtVitus ★★★★★
()
Ответ на: комментарий от Jetty

+1 только завернуть в статический класс вместо глобальной переменной статический сеттер

dimzon
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.