@DenKov
Android и Arduino разработчик

Как правильно передавать функцию как параметр C++?

Есть класс App

В файле App.cpp:
void App::setup() {
  bt_rest.function("startManufacturing", startManufacturing);
}

int App::startManufacturing(String command) {
  ...
  return 1;
}


bt_rest.function принимает (char * function_name, int (*f)(String))

Но компилятор выдает invalid use of non-static member function на bt_rest.function("startManufacturing", startManufacturing);

Уже все поперепробовал...
  • Вопрос задан
  • 1262 просмотра
Решения вопроса 1
myjcom
@myjcom Куратор тега C++
если bt_rest это https://github.com/marcoschwartz/aREST/blob/master...
то там по другому все устроено.

copy is as in original
// Functions array
  uint8_t functions_index;
  int (*functions[NUMBER_FUNCTIONS])(String);
  char * functions_names[NUMBER_FUNCTIONS];

void function(char * function_name, int (*f)(String)){

  functions_names[functions_index] = function_name;
  functions[functions_index] = f;
  functions_index++;
}
Может так

при условии что App будет в единственном экземпляре.
#include<iostream>
#include<string>
/*
* Будем считать, что std::string это String
*/
#define NUMBER_FUNCTIONS 10
struct aRest
{
  uint8_t functions_index;
  int (*functions[NUMBER_FUNCTIONS])(std::string);
  char* functions_names[NUMBER_FUNCTIONS];
  //...
  aRest() : functions_index(0){};
  void function(char * function_name, int (*f)(std::string))
  {
    functions_names[functions_index] = function_name;
    functions[functions_index] = f;
    functions_index++;
  }
};

class App
{
  aRest bt_rest;
public:
  explicit App() : bt_rest(){};
  //...
  int startManufacturing(std::string command);
  void setup();
  void call(std::string s);// Для теста. Там то же по другому...
  //...
};

int App::startManufacturing(std::string command)
{
  std::cout << "startManufacturing\n"
            << "with command " << command
            << " " << this << std::endl;
  return 1;
}

void App::setup()
{
  bt_rest.function("startManufacturing", (int(*)(std::string))&startManufacturing);
  //reinterpret_cast<int(*)(std::string)>(&startManufacturing)
}

void App::call(std::string s)
{
  bt_rest.functions[0](s);
}

int main()
{
  App ap;
  ap.setup();
  ap.call("COMMAND");
}
или форкнуть aRest
#include<iostream>
#include<string>
#include<functional>

#define NUMBER_FUNCTIONS 10
template<typename T>
struct aRest
{
  uint8_t functions_index;

  int (T::*functions[NUMBER_FUNCTIONS])(std::string);
  char* functions_names[NUMBER_FUNCTIONS];
  //...
  aRest() : functions_index(0){};

  void function(char * function_name, int(T::*f)(std::string))
  {
    functions_names[functions_index] = function_name;
    functions[functions_index] = f;
    functions_index++;
  }
};

class App
{
  aRest<App> bt_rest;
public:
  App() : bt_rest(){};
  //...
  int startManufacturing(std::string command);
  void setup();
  void call(std::string fn);
  //...
};

int App::startManufacturing(std::string command)
{
  std::cout << "startManufacturing\n"
            << "with command " << command
            << " " << this << " " << std::endl;
  return 1;
}

void App::setup()
{
  bt_rest.function("startManufacturing", &startManufacturing);
}

void App::call(std::string s)
{
  std::mem_fn(bt_rest.functions[0])(this, s);
}

int main()
{
  App ap;

  ap.setup();

  ap.call("COMMAND");
}

Ответ написан
Пригласить эксперта
Ответы на вопрос 2
@Mercury13
Программист на «си с крестами» и не только
Не забывайте, что у startManufacturing есть скрытый параметр this.

Четыре варианта.

1. Использовать указатель на метод App:
Rest::function(char * function_name, int (App::* f)(String), App& object)
      // вместо App можно какой-то интерфейс, который App реализует
…
object.*f("string");
...
bt_rest.function("", &App::startManufacturing, *this);


2. Сделать startManufacturing static:
class App {
  static int startManufacturing(String command)
};


3. Сделать обёртку с замыканием:
Rest::function(char * function_name, int (*f)(String, void*), void*);

void doStartManufacturing(String command, void* closure) {
  reinterpret_cast<App*>(closure)->startManufacturing(command);
}
...
bt_rest.function("startManufacturing", doStartManufacturing, this);


4. «Избегай незнакомых женщин и глобальных переменных». Костыль, в общем.
App app;
int doStartManufacturing(String command) { return app.startManufacturing(command); }
...
bt_rest.function("startManufacturing", doStartManufacturing);


Ах да. Вы передаёте String’и по значению. Приспособлены они к такой передаче или всё же лучше по ссылке?
Ответ написан
GavriKos
@GavriKos
Нельзя просто так взять, и передать нестатический метод. Потому что неясно, у какого экземпляра его вызывать.
Либо делайте статический метод, либо переделывайте bt_rest.function чтобы она еще и экземпляр принимала.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы