-1

I'm trying to create a class which accepts a function as a parameter when it is being initialized. Ideally, the function should already exist. I know that in Python, I can do this:

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()

How can I do this in C++? (I'm using an Arduino if it matters)

Merlin04
  • 1,837
  • 4
  • 23
  • 27
  • I'd like it if people explained why they downvoted the post after they did that, just a downvote doesn't tell me anything – Merlin04 Aug 19 '19 at 03:40
  • I guess someone was unsatisfied with the lack of attempt maybe? – Tagger5926 Aug 19 '19 at 03:42
  • @WeaktoEnumaElish It shouldn't be an anonymous function, it should be an already existing one. – Merlin04 Aug 19 '19 at 03:49
  • Both std::function and the template approach work with existing functions. – Weak to Enuma Elish Aug 19 '19 at 03:50
  • Would std::function work with the Arduino C++? Also, it should be an initializer for a method, not a parameter for another function. – Merlin04 Aug 19 '19 at 03:52
  • 2
    You probably should not try to learn C++ from Arduino code snippets *or* SO answers. Any good C++ book should explain passing functions as arguments, probably in more than one way. It is really a fundamental part of C++. – n. m. could be an AI Aug 19 '19 at 03:52

2 Answers2

1

Arduino doesn't have std::function, because AVR GCC doesn't ship with the Standard Library, so those suggestions in the comments won't work for that specific platform.

If you need similar behavior for Arduino or other embedded platforms, you can use ETL's etl::function or etl::delegate, or create your own implementation. std::function uses heap allocations for type erasure, which is usually not a good choice for embedded.

The simplest implementation would use C-style function pointers:

// 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};
}

Live example, with some use cases.

Of course, you can also just use the C pointer for this case, but this class can be extended for other types. If you need more complex behavior, like functors, member functions and capturing lambdas, see ETL's implementations I cited above.

Joel Filho
  • 1,300
  • 1
  • 5
  • 7
  • I've tried to implement this in my class, but it's giving an error saying "no matching function for call to 'function::function()'" and a bunch of other errors. It looks like some of those errors are because I can't make an empty function (in the .h file), but without doing that, I'm not sure how I could make the function accessible to all the methods of my class. I've put my code and all the compiler errors at https://pastebin.com/K4Np4YHm. – Merlin04 Aug 19 '19 at 17:06
  • @Merlin04 My bad. By defining the function constructor, I disabled its default constructor and didn't declare it back. [Here](https://godbolt.org/z/iPgYF0)'s your code being compiled on the Arduino compiler without errors after re-adding it. BTW, you don't need to use make_function for this specific case (nor this wrapper, if you just need C-style functions). – Joel Filho Aug 19 '19 at 17:46
  • Ok, if I don't need to use this wrapper how would I do it? – Merlin04 Aug 19 '19 at 18:36
  • @Merlin04 This wrapper would be a base if you wanted to implement extra functionality over it, like the ETL classes I mentioned. If you just want to use void C functions without parameters, you can just define a type like `using function_type = void(*)()` and use it in your class. – Joel Filho Aug 19 '19 at 18:40
0

You could do something like the following: ( Not sure if Arduino can have like following )

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();

}

See here

P0W
  • 46,614
  • 9
  • 72
  • 119
  • `m_func(func)` – they are of different types (`F*` and `F`)... – Aconcagua Aug 19 '19 at 04:12
  • 2
    @Aconcagua `foo` is declared to have a `F` type it actually has `F*` pointer type. Though `decltype(doStuff)` evaluates to `void ()` not `void (*)` , but, the function is implicitly convertible to a pointer to itself, at `someObj ( doStuff )`, hence no error. – P0W Aug 19 '19 at 06:13
  • Hm, OK, but disqualifies for use with functors then. Skipping the pointer in combination with `decltype(&doStuff)` would avoid that (or we could just let the template arguments be deduced, if C++17 is available...). – Aconcagua Aug 19 '19 at 11:21