Search code examples
c++functionclassarduinoarduino-c++

How to use a function as an initializer for a class in C++


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)


Solution

  • 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.