Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to save a function pointer with arguments for later use?

Yesterday, I was trying to program a basic renderer where the renderer controlled when data was loaded into a shader without the renderable object knowing anything about the shader being used. Being a stubborn person (and not running on enough sleep), I spent several hours trying to have function pointers sent to the renderer, saved, then run at the appropriate time. It wasn't until later that I realized what I was trying to build was a message system. It got me wondering, though, is it possible to save function pointers with arguments directly to be run at a later time in c++.

My original idea looked something like this:

// set up libraries and variables
Renderer renderer();
renderable obj();
mat4 viewMatrix();
// renderer returns and object id
int objID = renderer.loadObj(obj)

int main()
{
  //do stuff
  while(running)
  {
    //do stuff
    renderer.pushInstruction(//some instruction);
    renderer.render();
  }
}

// functionPtr.h
#include <functional>

class storableFunction
{
  public:
  virtual ~storableFunction = 0;
  virtual void call() = 0;
};

template<class type>
class functionPtr : public storableFunction
{
  std::function<type> func;
public:
  functionPtr(std::function<type> func)
    : func(func) {}
  void call() { func(); }
};

//renderer.h
struct  modelObj
{
  // model data and attached shader obj
  std::queue<storableFunction> instruction;
}

class renderer
{
  std::map<int, modelObj> models;
public:
    // renderer functions
    void pushInputDataInstruction(int id, //function, arg1, arg2);
    // this was overloaded because I did not know what type the second argument  would be
    // pushInputDataInstruction implementation in .cpp
    {
      models[id].instruction.push(functionPtr(std::bind(//method with args)))
    }
  void render();
};

//implantation in .cpp
{
  for(// all models)
  //bind all data
  applyInstructions(id);
  // this would call all the instructrions using functionptr.call() in the queue and clear the queue
  draw();
  // unbind all data
}

I realize that boost probably supports some kind of similar functionality, but I wanted to avoid using boost.

Is something like this possible, what would the general design look like, and what would it even be used for seeing as a message bus is a much more proven design pattern for something like this?

like image 475
Sara W Avatar asked Oct 15 '25 08:10

Sara W


1 Answers

std::bind is one approach, but if you have access to C++ 11 and later, you may want to consider using lambdas instead. Scott Meyer recommends their use over std::bind (in most cases) in Effective Modern C++.

A lambda has three parts:

  • the [] part, which identifies the values or references to capture,
  • the () part, which identifies the arguments that will be provided later, when the lambda is invoked.
  • the {} part, which identifies what to do with the captured values and parameters

Simple example:

#include <iostream>

void printValue(int x) {
    std::cout << x << std::endl;
}

int main(int argc, char * argv[]) {
    int x = 23;

    // [x] means 'capture x's value, keep it for later'
    // (int y) means 'I'll provide y when I invoke the lambda'
    auto storedFunction = [x](int y){return printValue(x + y);};

    x = 15;

    // Now we invoke the lamda, with y = 2
    // Result: 25 (23 + 2), even if x was changed after the lambda was created
    storedFunction(2); 
    return 0;
}

If you want to capture a reference to x, use [&x]. In the example above, the result would then be 17 (i.e. 15 + 2). If you do use a reference, be careful not to let x fall out of scope before the storedFunction, as it would then become a dangling reference to garbage data.

Most compilers support C++ 11 now, but you may need to add the support explicitly in the project settings:

  • Visual Studio: Project Properties/C++/Language/C++ Language Standard
  • gcc: --std=c++11 (or 14, or 17...)
  • CMake also allows you to set the standard: set (CMAKE_CXX_STANDARD 11)
like image 111
BareMetalCoder Avatar answered Oct 16 '25 22:10

BareMetalCoder



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!