Как использовать функцию в качестве инициализатора класса в C++

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

class TestClass:
    def __init__(self, function_parameter):
        self.fp = function_parameter
    def executeFP(self):
        self.fp()

def testFunction():
    print("test")

q = TestClass(testFunction)

q.executeFP()

Как я могу сделать это на С++? (Я использую Arduino, если это имеет значение)


person Merlin04    schedule 19.08.2019    source источник
comment
Я бы хотел, чтобы люди объяснили, почему они проголосовали за пост после того, как они это сделали, просто проголосовав против, я ничего не сказал.   -  person Merlin04    schedule 19.08.2019
comment
Я думаю, кто-то был недоволен отсутствием попытки, может быть?   -  person Tagger5926    schedule 19.08.2019
comment
@WeaktoEnumaElish Это не должна быть анонимная функция, она должна быть уже существующей.   -  person Merlin04    schedule 19.08.2019
comment
И std::function, и шаблонный подход работают с существующими функциями.   -  person Weak to Enuma Elish    schedule 19.08.2019
comment
Будет ли std::function работать с Arduino C++? Кроме того, он должен быть инициализатором метода, а не параметром другой функции.   -  person Merlin04    schedule 19.08.2019
comment
Вероятно, вам не следует пытаться изучать C++ из фрагментов кода Arduino или SO-ответов. В любой хорошей книге по C++ должна объясняться передача функций в качестве аргументов, возможно, несколькими способами. Это действительно фундаментальная часть C++.   -  person n. 1.8e9-where's-my-share m.    schedule 19.08.2019


Ответы (2)


У Arduino нет std::function, потому что AVR GCC не поставляется со стандартной библиотекой, поэтому предложения в комментариях не будут работать для этой конкретной платформы.

Если вам нужно подобное поведение для Arduino или других встроенных платформ, вы можете использовать ETL etl::function или etl::delegate, или создайте свою собственную реализацию. std::function использует выделение кучи для стирания типов, что обычно не является хорошим выбором для встраиваемых систем.

Простейшая реализация будет использовать указатели функций в стиле C:

// Generic definition of the function type
template <typename F>
class function;

// R: return type
// Args: Any arguments a function can take
template <typename R, typename... Args>
class function<R(Args...)> {
 public:
  // Type definition of the equivalent C function pointer
  using function_type = R (*)(Args...);

  // Default constructor: empty function. 
  // Never call the function while not initialized if using it this way.
  function() = default;

  // Constructor: store the function pointer
  function(function_type f) : function_ptr(f){};

  // Call operator: calls the function object like a normal function
  // PS: This version does not do perfect forwarding.
  R operator()(Args... args) { return function_ptr(args...); }

 private:
  function_type function_ptr;
};

// A helper function can be used to infer types!
template <typename R, typename... Args>
function<R(Args...)> make_function(R (*f)(Args...)) {
  return {f};
}

Живой пример с некоторыми вариантами использования.

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

person Joel Filho    schedule 19.08.2019
comment
Я пытался реализовать это в своем классе, но он выдает ошибку, говорящую об отсутствии подходящей функции для вызова «function‹void()›::function()» и кучу других ошибок. Похоже, некоторые из этих ошибок связаны с тем, что я не могу создать пустую функцию (в файле .h), но без этого я не уверен, как сделать функцию доступной для всех методов моего класса. Я разместил свой код и все ошибки компилятора по адресу pastebin.com/K4Np4YHm. - person Merlin04; 19.08.2019
comment
@ Merlin04 Плохо. Определив конструктор функции, я отключил его конструктор по умолчанию и не объявлял его обратно. Вот ваш код компилируется на компиляторе Arduino без ошибок после его повторного добавления. Кстати, вам не нужно использовать make_function для этого конкретного случая (или эту оболочку, если вам просто нужны функции в стиле C). - person Joel Filho; 19.08.2019
comment
Хорошо, если мне не нужно использовать эту обертку, как бы я это сделал? - person Merlin04; 19.08.2019
comment
@Merlin04 Merlin04 Эта оболочка будет основой, если вы захотите реализовать поверх нее дополнительные функции, например классы ETL, о которых я упоминал. Если вы просто хотите использовать пустые функции C без параметров, вы можете просто определить тип, например using function_type = void(*)(), и использовать его в своем классе. - person Joel Filho; 19.08.2019

Вы можете сделать что-то вроде следующего: (Не уверен, что Arduino может иметь подобное)

template <typename F>
class TestClass {
    public:
    TestClass( F func )
        :m_func(func)
    {

    }

    void executeFP()
    {
        m_func();
    }

    private:
    F *m_func;

};

void doStuff()
{

    std::cout << "test" << std::endl;
}

bool doAnotherStuff( )
{
    std::cout <<"test " << 40 +2 << std::endl;

    return true;

}

int main()
{
    TestClass<decltype(doStuff)> someObj ( doStuff );

    TestClass<decltype(doAnotherStuff)> someObj2 ( doAnotherStuff );

    someObj.executeFP();

    someObj2.executeFP();

}

См. здесь

person P0W    schedule 19.08.2019
comment
m_func(func) — они разных типов (F* и F)... - person Aconcagua; 19.08.2019
comment
@Aconcagua foo объявлен как имеющий тип F, на самом деле он имеет тип указателя F*. Хотя decltype(doStuff) оценивается как void () , а не void (*) , но функция неявно преобразуется в указатель на себя в someObj ( doStuff ), поэтому ошибки нет. - person P0W; 19.08.2019
comment
Хм, ладно, но тогда нельзя использовать с функторами. Пропуск указателя в сочетании с decltype(&doStuff) позволит избежать этого (или мы можем просто позволить вывести аргументы шаблона, если C++17 доступен...). - person Aconcagua; 19.08.2019