开发者

C++ how to generalize class function parameter to handle many types of function pointers?

开发者 https://www.devze.com 2023-03-13 23:28 出处:网络
I do not know if what I am asking is doable, stupid or simple. I\'ve only recently started dwelling in template functions and classes, and I was wondering if the following scenario is possible:

I do not know if what I am asking is doable, stupid or simple. I've only recently started dwelling in template functions and classes, and I was wondering if the following scenario is possible: A class which holds a function pointer to be called. The function pointer cannot be specific, but abstract, so that whenever the class's Constructor is called, it may accept different kinds of function pointers. When the class's execute function is called, it executes the function pointer allocated at construction, with an argument (or arguments). Basically the abstraction is kept throughout the design, and left over the user on what function pointer and arguments to pass. The following code has not been tested, just to demonstrate what I'm trying to do:

void (*foo)(double);
void (*bar)(double,double);
void (*blah)(float);

class Instruction
{
    protected:
      double var_a;
      double var_b;
      void (*ptr2func)(double);
      void (*ptr2func)(double,double);  
    public:
      template <typename F> Instruction(F arg1, F arg2, F arg3)
      {
         Instruction::ptr2func = &arg1;
         var_a = arg2;
         var_b = arg3;
      };    
      void execute()
      {
         (*ptr2func)(var_a);
      };
};

I do not like the fact I have to keep a list inside the class of possible overloadable function pointers. How could I possibly improve the above to generalize it as much as possible so that it can work with any kind of function pointer thrown at it ? Bear in mind, I will want to keep a container of those instantiated objects and execute each function pointer in sequence. Thank you ! Edit: Maybe the class should be a template it'self in order to facilitate use with many different function pointers? Edit2: I found a way around my problem just for future reference, don't know if it's the right one, but it works:

class Instruction
{
  protected:
    double arg1,arg2,arg3;
  public:
    virtual void execute() = 0;
};

template <class F> class MoveArm : public Instruction
{
  private:
    F (func);    
  public:
   template <typename T> 
   MoveArm(const T& f,double a, double b)
   {
     arg1 = a;
     arg2 = b;
     func = &f;
   };

   void execute()
   {
     (func)(arg1,arg2);
   };
};

However when importing functions, 开发者_StackOverflow社区their function pointers need to be typedef'd:

void doIt(double a, double b)
{
   std::cout << "moving arm at: " << a << " " << b << std::endl;
};

typedef void (*ptr2func)(double,double);

int main(int argc, char **argv) {

   MoveArm<ptr2func>  arm(doIt,0.5,2.3);
   arm.execute();

   return 0;
}


If you can use C++0x and variadic templates, you can achieve this by using combination of std::function, std::bind and perfect forwarding:

#include <iostream>
#include <functional>

template <typename Result = void>
class instruction
{
public:
        template <typename Func, typename... Args>
        instruction(Func func, Args&&... args)
        {
                m_func = std::bind(func, std::forward<Args>(args)...);
        }

        Result execute()
        {
                return m_func();
        }
private:
        std::function<Result ()> m_func;
};

double add(double a, double b)
{
        return (a + b);
}

int main()
{
        instruction<double> test(&add, 1.5, 2.0);
        std::cout << "result: " << test.execute() << std::endl;
}

Example with output: http://ideone.com/9HYWo

In C++ 98/03, you'd unfortunately need to overload the constructor for up-to N-paramters yourself if you need to support variable-number of arguments. You'd also use boost::function and boost::bind instead of the std:: equivalents. And then there's also the issue of forwarding problem, so to do perfect forwarding you'd need to do an exponential amount of overloads depending on the amount of arguments you need to support. Boost has a preprocessor library that you can use to generate the required overloads without having to write all the overloads manually; but that is quite complex.

Here's an example of how to do it with C++98/03, assuming the functions you pass to the instruction won't need to take the arguments by modifiable reference, to do that, you also need to have overloads for P1& p1 instead of just const P1& p1.

#include <iostream>
#include <boost/function.hpp>
#include <boost/bind.hpp>

template <typename Result = void>
class instruction
{
public:
        template <typename Func>
        instruction(Func func)
        {
                m_func = func;
        }

        template <typename Func, typename P1>
        instruction(Func func, const P1& p1)
        {
                m_func = boost::bind(func, p1);
        }

        template <typename Func, typename P1, typename P2>
        instruction(Func func, const P1& p1, const P2& p2)
        {
                m_func = boost::bind(func, p1, p2);
        }

        template <typename Func, typename P1, typename P2, typename P3>
        instruction(Func func, const P1& p1, const P2& p2, const P3& p3)
        {
                m_func = boost::bind(func, p1, p2, p3);
        }

        Result execute()
        {
                return m_func();
        }
private:
        boost::function<Result ()> m_func;
};

double add(double a, double b)
{
        return (a + b);
}

int main()
{
        instruction<double> test(&add, 1.5, 2.0);
        std::cout << "result: " << test.execute() << std::endl;
}

Example: http://ideone.com/iyXp1


I also created a C++0x version with some example usage. You can probably better use the one given by reko_t but I nevertheless post this one. This one uses recursion to unpack a tuple with values, and thus a tuple to store the arguments to pass to the function. Note that this one does not use perfect forwarding. If you use this, you probably want to add this.

#include <iostream>
#include <string>
#include <tuple>

using namespace std;

template<unsigned N>
struct FunctionCaller
{
    template<typename ... Typenames, typename ... Args>
    static void call(void (*func)(Typenames ...), tuple<Typenames ...> tuple, Args ... args)
    {
        FunctionCaller<N-1>::call(func, tuple, get<N-1>(tuple), args ...);
    }
};

template<>
struct FunctionCaller<0u>
{
    template<typename ... Typenames, typename ... Args>
    static void call(void (*func)(Typenames ...), tuple<Typenames ...> tuple, Args ... args)
    {
        func(args ...);
    }
};

template<typename ... Typenames>
class Instruction
{
    public:
    typedef void (*FuncType)(Typenames ...);

    protected:
    std::tuple<Typenames ...> d_args;
    FuncType d_function;

    public:
    Instruction(FuncType function, Typenames ... args):
        d_args(args ...),
        d_function(function)
    {
    }

    void execute()
    {
        FunctionCaller<sizeof...(Typenames)>::call(d_function, d_args);
    }
};

void test1()
{
    cout << "Hello World" << endl;
}

void test2(int a, string b, double c)
{
    cout << a << b << c << endl;
}

int main(int argc, char** argv)
{
    Instruction<> i1(test1);
    Instruction<int, string, double> i2(test2, 5, "/2 = ", 2.5);
    i1.execute();
    i2.execute();
    return 0;
}


Well, what you are doing is correct. But since all pointers have the same size in C++ you can store one pointer (of void type):

void *funcptr;

and cast it to the necessary type when needed:

static_cast<(*void)(double,double)>(funcptr)(var_a, var_b);

But please, only use this when better techniques can not be used, but I can not tell if you don't tell us the bigger picture.


You might want to look at boost::function.

0

精彩评论

暂无评论...
验证码 换一张
取 消