LINUX.ORG.RU

Не могу сделать метод обратного вызова. Что-то странное со статическими данными

 , , ,


0

3

Пишу небольшую обертку для старого легаси.

Задача: сделать класс с методом, который будет использоваться как коллбек.

Класс примерно такой:

class FileChannel
{
public:
    FileChannel();

    static int getChar(num_t Port);
    bool open(const char *fileName);
    void close();

protected:

    FILE *file;
};

Метод getChar() статический, потому что только такие методы можно использовать как функции обратного вызова.

Реализация getChar() простая:
int FileChannel::getChar(num_t Port)
{
   ... некая логика с портом...

   return fgetc(file);
}

Проблема в том, что данный статический метод не может работать с не-статическими данными. Ошибка будет такой:
FileChannel.cpp:39:17: error: invalid use of member ‘FileChannel::file’ in static member function
    return fgetc(file);
                 ^~~~
In file included from FileChannel.cpp:1:0:
FileChannel.h:26:11: note: declared here
     FILE *file;
           ^~~~

Тогда я пытаюсь объявить file как static:
static FILE *file;

Но тогда не проходит линковка. Почему-то класс перестает видеть свой член file. Ошибка, например в конструкторе:
FileChannel::FileChannel()
{
    file = NULL; // Тут ошибка
}

Сами ошибки выглядят вот так:
FileChannel.o: In function `FileChannel::FileChannel()':
FileChannel.cpp:6: undefined reference to `FileChannel::file'
FileChannel.o: In function `FileChannel::open(char const*)':
FileChannel.cpp:16: undefined reference to `FileChannel::file'
...
FileChannel.o:FileChannel.cpp:31: more undefined references to `FileChannel::file' follow
collect2: error: ld returned 1 exit status
make: *** [Indicator] Error 1
14:09:37: Процесс «/usr/bin/make» завершился с кодом 2.

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

★★★★★

А как выглядит та часть кода, в которую ты свой коллбэк хочешь отдать? Обычно там есть что-то типа void *data, в который можно засунуть this.

Deleted
()

А в cpp не забыл свою статическую переменную описать? И вообще, если у тебя плюсы, то статик тебе не нужен - std::bind можешь юзать, лямбды там всякие.

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

А как выглядит та часть кода, в которую ты свой коллбэк хочешь отдать? Обычно там есть что-то типа void *data, в который можно засунуть this.

Ну задание функции обратного вызова выглядит так:

Device device;
device.GetChanApi().onCHAN_Getc = channel.getChar;

Где getChar - это метод, про который и написано в теме.

Далее, само описание элемента, который хранит указатель на функцию обратного вызова, выглядит так:
typedef int (*TCHAN_Getc)(num_t Port);
...
struct TChanApi {
   TCHAN_Getc onCHAN_Getc;
   ...
}

Xintrea ★★★★★
() автор топика

почему-то не работает доступ к статическим данным класса внутри самого класса

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

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

А в cpp не забыл свою статическую переменную описать? И вообще, если у тебя плюсы, то статик тебе не нужен - std::bind можешь юзать, лямбды там всякие.

Вот поубирал я все эти static, пытаюсь скомпилять. В самом классе FileChannel ошибок не выявляется, ошибка появляется на строке запоминания указателя на функцию:

device.GetChanApi().onCHAN_Getc = std::bind(channel.getChar);

Текст ошибки:
DataFromLogFile.cpp:46:63: error: invalid use of non-static member function ‘int FileChannel::getChar(num_t)’
    Compas.GetChanApi().onCHAN_Getc = std::bind(channel.getChar);
                                                               ^
In file included from DataFromLogFile.h:14:0,
                 from DataFromLogFile.cpp:4:
FileChannel.h:19:9: note: declared here
     int getChar(num_t Port);
         ^~~~~~~

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

Тебе же сказали - опиши статическое поле в цпп-файле, ну.

Не пойму о чем речь вообще идет. Поля классов описываются в определении классов. Что значит «опиши статическое поле в цпп-файле» ?

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

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

У меня нет никаких глобальных переменных.

Хотя , может быть где-то в глубинах легаси может быть глобальная переменная file, ты это имеешь в виду?

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

Читай матчасть.
std::bind(&Some::method, some)

Ну вот прописал так:

device.GetChanApi().onCHAN_Getc = std::bind(&FileChannel::getChar, channel);

Ошибки теперь другие:
DataFromLogFile.cpp:1:0:
/usr/include/c++/6/functional: In instantiation of ‘struct std::_Bind_check_arity<int (FileChannel::*)(int), FileChannel&>’:
/usr/include/c++/6/functional:1299:12:   required from ‘struct std::_Bind_helper<false, int (FileChannel::*)(int), FileChannel&>’
/usr/include/c++/6/functional:1322:5:   required by substitution of ‘template<class _Func, class ... _BoundArgs> typename std::_Bind_helper<std::__is_socketlike<_Func>::value, _Func, _BoundArgs ...>::type std::bind(_Func&&, _BoundArgs&& ...) [with _Func = int (FileChannel::*)(int); _BoundArgs = {FileChannel&}]’
DataFromLogFile.cpp:46:78:   required from here
/usr/include/c++/6/functional:1286:7: error: static assertion failed: Wrong number of arguments for pointer-to-member
       static_assert(_Varargs::value
       ^~~~~~~~~~~~~
DataFromLogFile.cpp: In member function ‘int DataFromLogFile::compasInit(TCompas&)’:
DataFromLogFile.cpp:46:78: error: cannot convert ‘std::_Bind_helper<false, int (FileChannel::*)(int), FileChannel&>::type {aka std::_Bind<std::_Mem_fn<int (FileChannel::*)(int)>(FileChannel)>}’ to ‘TCHAN_Getc {aka int (*)(int)}’ in assignment
    device.GetChanApi().onCHAN_Getc = std::bind(&FileChannel::getChar, channel);
                                                                              ^

Я вот тут читаю:

http://en.cppreference.com/w/cpp/utility/functional/bind

и не могу понять что там в качестве второго параметра указывается.

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

Ты же вроде давно на плюсах пишешь, а вопросы как у первокурсника.

У первокусника есть с кем посоветоваться и у кого спросить если что неясно. У меня есть только интернет и лор, профессиональной среды нет.

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

auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
Второй параметр указатель на объект.

Понял. Вот я делаю точно так же:

device.GetChanApi().onCHAN_Getc = std::bind(&FileChannel::getChar, &channel);

Ошибки:
DataFromLogFile.cpp:1:0:
/usr/include/c++/6/functional: In instantiation of ‘struct std::_Bind_check_arity<int (FileChannel::*)(int), FileChannel*>’:
/usr/include/c++/6/functional:1299:12:   required from ‘struct std::_Bind_helper<false, int (FileChannel::*)(int), FileChannel*>’
/usr/include/c++/6/functional:1322:5:   required by substitution of ‘template<class _Func, class ... _BoundArgs> typename std::_Bind_helper<std::__is_socketlike<_Func>::value, _Func, _BoundArgs ...>::type std::bind(_Func&&, _BoundArgs&& ...) [with _Func = int (FileChannel::*)(int); _BoundArgs = {FileChannel*}]’
DataFromLogFile.cpp:46:79:   required from here
/usr/include/c++/6/functional:1286:7: error: static assertion failed: Wrong number of arguments for pointer-to-member
       static_assert(_Varargs::value
       ^~~~~~~~~~~~~
DataFromLogFile.cpp: In member function ‘int DataFromLogFile::compasInit(TCompas&)’:
DataFromLogFile.cpp:46:79: error: cannot convert ‘std::_Bind_helper<false, int (FileChannel::*)(int), FileChannel*>::type {aka std::_Bind<std::_Mem_fn<int (FileChannel::*)(int)>(FileChannel*)>}’ to ‘TCHAN_Getc {aka int (*)(int)}’ in assignment
    device.GetChanApi().onCHAN_Getc = std::bind(&FileChannel::getChar, &channel);
                                                                               ^


Может, аргумента не хватает? Попробовал так:
device.GetChanApi().onCHAN_Getc = std::bind(&FileChannel::getChar, &channel, std::placeholders::_1);

Ошибки:
DataFromLogFile.cpp:46:102: error: cannot convert ‘std::_Bind_helper<false, int (FileChannel::*)(int), FileChannel*, const std::_Placeholder<1>&>::type {aka std::_Bind<std::_Mem_fn<int (FileChannel::*)(int)>(FileChannel*, std::_Placeholder<1>)>}’ to ‘TCHAN_Getc {aka int (*)(int)}’ in assignment
    device.GetChanApi().onCHAN_Getc = std::bind(&FileChannel::getChar, &channel, std::placeholders::_1);

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

У меня нет никаких глобальных переменных.

Вот: static FILE *file;

non-static member function ‘int FileChannel::getChar(num_t)’

Такое впечатление, что здесь ты постишь один код, а компилируешь другой. Если getChar - не-статическая функция-член, то в bind нужно передавать указатель на объект (или _N). Сделай маленький пример и заставь его работать.

tailgunner ★★★★★
()
Ответ на: комментарий от Xintrea
void foo(std::function<int(int)> f) {
    f(10);
}

int main (int argc, char *argv[]) 
{
    FileChannel channel;
    
    auto callBackMethod=std::bind(&FileChannel::getChar, &channel, std::placeholders::_1);
    foo(callBackMethod);
    
    return 0;
}

И так компилится.

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

Угу, и так компилится:

auto callBackMethod=std::bind(&FileChannel::getChar, &channel, std::placeholders::_1);

Значит, в реальном коде проблема в приведении типа с того, что возвращает std::bind() к тому типу, который имеется у
device.GetChanApi().onCHAN_Getc

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

Вот тут я показал:

Не могу сделать метод обратного вызова. Что-то странное со статическими данными (комментарий)

std::function невозможно преобразовать в указатель на функцию. Купи себе учебник по Си++.

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

Можешь std::placeholders::_1 убрать, все равно будет ошибка.

Я еще поигрался и вот что вижу. Если взять обычную функцию, то она нормально присваивается и все компилится:

int easyGetChar(int Port)
{
   (void)Port;
   return 25;
}

int main (int argc, char *argv[]) 
{
    FileChannel channel;
    TChanApi chanApi;

    chanApi.onCHAN_Getc=easyGetChar;
}


А с std::bind и методом класса такое не прокатывает. А нужно чтоб прокатило.

Вот третий пример, с обычной функцией, компилябельный:

http://rgho.st/7vTzBXlVx

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

std::function невозможно преобразовать в указатель на функцию.

Вот и я не понимаю, с чего мне тут весь топик полоскают мозги, что надо делать через std::bind. В том то и проблема, что легаси нормально работает с указателем на функцию, а мне надо вместо функции задать метод класса. Вот в этом и проблема.

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

Стоп, это разве не определение члена класса?

class FileChannel
{
    ...
protected:

    static FILE *file;
};

Я уже уточнял, анонимус отмалчивается: Не могу сделать метод обратного вызова. Что-то странное со статическими данными (комментарий)

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

Вот и я не понимаю, с чего мне тут весь топик полоскают мозги, что надо делать через std::bind

Потому что ты не сказал, что тебе обязательно нужен указатель на функцию.

мне надо вместо функции задать метод класса. Вот в этом и проблема.

Вообще-то нет, это не проблема.

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

Да впринципе нет. Третий пример демонстрирует, что с обычной функцией все компиляется именно со структурой в которой такой onCHAN_Getc.

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

У fluorite ссылка, первый же семпл по ссылке:

class X { static int n; }; // declaration (uses 'static')
int X::n = 1;              // definition (does not use 'static')

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

Все, разобрался. В FileChannel.cpp не хватало строчки:

FILE* FileChannel::file=NULL;

С ней все закомпилилось как задумано.

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

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

P.S. витрины должны умереть

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

Ты почитай, что такое declaration и что такое difinition переменных

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