【问题标题】:How to pass Virtually Anything To A Function In C++ (or C)?如何将几乎任何东西传递给 C++(或 C)中的函数?
【发布时间】:2011-07-30 03:34:32
【问题描述】:

我需要传递一个指针之类的东西,它将任何东西都作为函数参数。你知道,没有任何预定义类型的东西,或者可以采用这样的类型:

 void MyFunc( *pointer ); 

然后像这样使用它:

char * x = "YAY!";
MyFunc(x);

int y = 10;
MyFunc(&y);

MyObj *b = new MyObj();
MyFunc(b);

而且我不想使用模板,因为我在项目中主要使用 C。 除了函数宏,这里有任何东西可以使用吗?

【问题讨论】:

  • “任何东西”是否包含指向函数的指针?指向成员的指针?
  • 我不确定,但在开发库的后期阶段我可能也需要这些。尤其是指向函数的指针。
  • “任何东西”听起来都像 YAGNI(至少现在不是)。
  • 我建议您提出真正的问题,您的设计目标是什么,人们可以提出比函数采取任何措施更好的决策。

标签: c++ c function pointers


【解决方案1】:

在 C++ 中,Boost.Any 将允许您以类型安全的方式执行此操作:

void func(boost::any const &x)
{
    // any_cast a reference and it
    // will throw if x is not an int.
    int i = any_cast<int>(x);

    // any_cast a pointer and it will
    // return a null pointer if x is not an int.
    int const *p = any_cast<int>(&x);
}

// pass in whatever you want.
func(123);
func("123");

在 C 中,您将使用 void 指针:

void func(void const *x)
{
    // it's up to you to ensure x points to an int.  if
    // it's not, it might crash or it might silently appear
    // to work. nothing is checked for you!
    int i = *(int const*)x;
}

// pass in whatever you want.

int i = 123;
func(&i);

func("123");

您似乎反对它,但无论如何我都会推荐它:如果您使用的是 C++,请接受它。不要害怕模板。 Boost.Any 和 void 指针之类的东西在 C++ 中占有一席之地,但它非常小。

更新

好吧,我正在制作一个小信号 - 插槽 - 连接库 与我的 gui 工具包一起使用。这样我就可以摆脱丑陋的 WNDPROC。一世 需要这些指针进行连接。

如果您需要多目标信号,Boost.Signals 已经提供了完整且经过测试的信号/插槽实现。您可以使用Boost.Bind(或std::bind,如果您有C++0x 编译器)连接成员函数:

struct button
{
    boost::signal<void(button&)> on_click;
}

struct my_window
{
    button b;

    my_window()
    {
        b.on_click.connect(std::bind(&my_window::handle_click,
                                     this, std::placeholders::_1));
    }

    void handle_click(button &b)
    {
    }

    void simulate_click()
    {
        b.on_click(b);
    }
};

如果您只想要一个简单的回调,Boost.Function(或std::function,如果您有 C++0x 编译器)会很好用:

struct button
{
    std::function<void(button&)> on_click;
}

struct my_window
{
    button b;

    my_window()
    {
        b.on_click = std::bind(&my_window::handle_click,
                               this, std::placeholders::_1);
    }

    void handle_click(button &b)
    {
    }

    void simulate_click()
    {
        b.on_click(b);
    }
};

【讨论】:

    【解决方案2】:

    您可以使用带有void* 的函数,但您必须注意与void* 不兼容的指针类型:

    • 函数指针:

      void MyFunc(void*);
      
      MyFunc(&MyFunc); // WRONG
      
    • 指向成员的指针:

      void MyFunc(void*);
      
      struct A { int x; };
      
      MyFunc(&A::x); // WRONG
      

    虽然这些指针与void* 不兼容(即使在某些编译器上使用强制转换),但它们本身就是数据。所以你可以将指针传递给指针:

    void MyFunc(void*);
    
    void (*pfn)(void*) = &MyFunc;
    MyFunc(&pfn); // ok
    
    struct A { int x; };
    int A::*px = &A::x;
    MyFunc(&px); // ok
    

    【讨论】:

      【解决方案3】:

      您可以将方法定义为采用一个void * 参数。当然,到那时,您可以自己决定如何处理数据(就访问或强制转换而言)。

      void MyFunc(void * ptr);
      

      【讨论】:

      • 然后我需要转换每个指针吗?喜欢MyFunc((void*) myptr);
      • 取决于您的编译器和选项,不。大多数编译器会让指针转换为void * 滑动。它可能会产生警告或错误,但同样,您的里程可能会有所不同。
      • @Adam :标准保证任何指针类型到void* 的隐式转换。如果任何编译器产生警告或错误,这些编译器就会损坏。
      • @ildjarn:差不多。所有类型的对象指针都隐式转换为void*。但指向函数的指针和指向成员的指针不兼容,实际上与void* 不兼容。
      • @BenVoigt :是的,所以对于每个可以合法调用此函数的指针类型,指针类型保证可以隐式转换为void*;对于调用此函数不合法的每种指针类型,显式转换都无济于事。
      【解决方案4】:

      你可以使用:

      void MyFunc( void* p){}
      
      int g = 10;
      MyFunc( (void*)&g );
      

      【讨论】:

      • 然后我需要在函数内部将其重铸为 int 吗?
      • @Ben Voigt:比抱歉更安全
      • 我相信重铸:int x = *(int *)p
      • @Dave:安全总比抱歉好,是的。铸造是不安全的。这样做总有一天会让你后悔的。
      • @Ben Voigt:有点过时,但想知道为什么在这种情况下强制转换不安全,您可以指出我的任何参考资料吗?
      【解决方案5】:

      void * 是这样做的方法。您可以将任何指针类型分配给void * 和从void * 分配。但是要在被调用函数中使用指针,您必须知道类型,以便您可以创建适当的本地指针或适当地强制转换。您可以将一组有限的类型编码为枚举符号,也可以使用开关来选择特定于类型的行为。但是,如果没有特定的目的或用例,您最终可能会在追求通用性的过程中追逐自己的尾巴,而 C 从未打算这样做。

      另一种方法是创建一个联合以包含您知道需要的所有各种类型

      typedef union {
          int i;
          char c;
          float f;
      } vartype;
      

      如果值可以携带自己的类型标识符,它就变成了标签联合或变体记录。

      typedef struct {
          enum type { INT, CHAR, FLOAT } type;
          vartype var;
      } varrec;
      

      【讨论】:

        猜你喜欢
        • 2017-12-13
        • 1970-01-01
        • 1970-01-01
        • 2019-03-12
        • 2016-04-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-06
        相关资源
        最近更新 更多