开发者

How to pass Virtually Anything To A Function In C++ (or C)?

I need to pass something like a pointer that takes anything as a function parameter. You know, something without any predefined type or a type that can take anything like this:

 void MyFunc( *pointer ); 

And then use it like:

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

in开发者_JAVA百科t y = 10;
MyFunc(&y);

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

And I don't want to use templates because I am mostly using C in my project. Is there anything that can be used here except a function macro?


In C++, Boost.Any will let you do this in a type-safe way:

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

In C, you would use a void pointer:

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

You seem adverse to it but I'll recommend it anyway: if you're using C++, embrace it. Don't be afraid of templates. Things like Boost.Any and void pointers have a place in C++, but it is very small.

Update:

Well , I am making a small signals - slots - connections library to be used with my gui toolkit. So that I can get rid of the Ugly WNDPROC. I need these pointers for the connections.

If you need multi-target signals, Boost.Signals already provides a full and tested signals/slots implementation. You can use Boost.Bind (or std::bind, if you've got a C++0x compiler) to connect member functions:

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

If you only want a simple callback, Boost.Function (or std::function if you've got a C++0x compiler) will work well:

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


You can use a function that takes a void*, but you must be aware of the pointer types that are not compatible with void*:

  • pointers to functions:

    void MyFunc(void*);
    
    MyFunc(&MyFunc); // WRONG
    
  • pointers to members:

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

While these pointers are not compatible with void* (even with casting, on some compilers), they are themselves data. So you can pass a pointer to the pointer:

void MyFunc(void*);

void (*pfn)(void*) = &MyFunc;
MyFunc(&pfn); // ok

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


You can define the method as taking one void * argument. Of course, at that point, it's up to you to figure out what to do with the data (as far as accessing it or casting it.)

void MyFunc(void * ptr);


You could use:

void MyFunc( void* p){}

int g = 10;
MyFunc( (void*)&g );


void * is the way to do it. You can assign any pointer type to and from a void *. But to use the pointer in the called function, you'll have to know the type so you can create an appropriate local pointer or cast appropriately. You can encode a limited set of types as enum symbols, and perhaps use a switch to select type-specific behavior. But without a specific purpose or use-case, you might end up chasing your tail in a quest for generality for which C was never intended.

Another way would be to make a union to contain all the various types you know are needed.

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

Then if the value can carry around its own type-identifier, it becomes a tag-union or variant-record.

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

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜