C++如何泛化类函数参数来处理多种类型的函数指针?

发布于 2024-11-16 01:41:02 字数 1718 浏览 0 评论 0原文

我不知道我的要求是否可行、愚蠢或简单。 我最近才开始研究模板函数和类,我想知道以下情况是否可能: 一个包含要调用的函数指针的类。函数指针不能是具体的,而是抽象的,因此每当调用类的构造函数时,它都可以接受不同类型的函数指针。当调用类的执行函数时,它会执行在构造时分配的函数指针,并带有一个参数(或多个参数)。 基本上,在整个设计过程中都保留了抽象,并让用户决定要传递哪些函数指针和参数。以下代码尚未经过测试,只是为了演示我想要做什么:

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

我不喜欢这样的事实:我必须在可能的可重载函数指针的类中保留一个列表。我怎样才能改进上面的内容以尽可能地概括它,以便它可以与抛出的任何类型的函数指针一起使用? 请记住,我希望保留这些实例化对象的容器并按顺序执行每个函数指针。 谢谢 ! 编辑:也许类本身应该是一个模板,以便于与许多不同的函数指针一起使用? Edit2:我找到了一种解决问题的方法,仅供将来参考,不知道它是否正确,但它有效:

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

但是,在导入函数时,它们的函数指针需要进行 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;
}

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

禾厶谷欠 2024-11-23 01:41:02

如果您可以使用 C++0x 和可变参数模板,则可以通过使用 std::functionstd::bind 和完美转发的组合来实现此目的:

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

带有输出的示例: http://ideone.com/9HYWo

在 C++ 98/03 中,不幸的是您需要重载构造函数对于高达如果您需要支持可变数量的参数,请自行设置 N 个参数。您还可以使用 boost::functionboost::bind 而不是 std:: 等效项。还有转发问题,因此要进行完美的转发,您需要根据需要支持的参数数量进行指数级的重载。 Boost 有一个您可以使用的预处理器库生成所需的重载,而无需手动编写所有重载;但这是相当复杂的。

下面是如何使用 C++98/03 执行此操作的示例,假设您传递给指令的函数不需要通过可修改的引用获取参数,为此,您还需要为 P1& 提供重载p1 而不仅仅是 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;
}

示例:http://ideone.com/iyXp1

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

携余温的黄昏 2024-11-23 01:41:02

我还创建了一个带有一些示例用法的 C++0x 版本。您可能可以更好地使用 reko_t 提供的那个,但我仍然发布了这个。这个使用递归来解压一个带有值的元组,从而用一个元组来存储要传递给函数的参数。请注意,这个不使用完美转发。如果您使用它,您可能想添加它。

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

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;
}
2024-11-23 01:41:02

嗯,你正在做的事情是正确的。但是,由于所有指针在 C++ 中具有相同的大小,因此您可以存储一个指针(void 类型):

void *funcptr;

并在需要时将其转换为必要的类型:

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

但是请仅在无法使用更好的技术时使用此方法,但我不能告诉如果你不告诉我们大局的话


您可能想查看 boost::function

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文