C++: Instances of Functions

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

C++: Instances of Functions

Postby sourmìlk » Sat Mar 26, 2011 8:09 pm UTC

So, in javascript, you can do things like this:

Code: Select all

someObject.oneOfItsFunctions = function()
{
    //code goes here
}


How do I do the equivalent in C++? I'm making an OpenGL GUI library and this seems like a really basic gap in my knowledge. I'm essentially trying to make it so that each instance of a button class has its own onMouseOver function. I don't know the term for this, so its surprisingly difficult to find on Google.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.

User avatar
TheChewanater
Posts: 1279
Joined: Sat Aug 08, 2009 5:24 am UTC
Location: lol why am I still wearing a Santa suit?

Re: C++: Instances of Functions

Postby TheChewanater » Sat Mar 26, 2011 9:09 pm UTC

It's called an unnamed function, or lambda. What you probably want is either a callback or functor, though.
ImageImage
http://internetometer.com/give/4279
No one can agree how to count how many types of people there are. You could ask two people and get 10 different answers.

User avatar
Sc4Freak
Posts: 673
Joined: Thu Jul 12, 2007 4:50 am UTC
Location: Redmond, Washington

Re: C++: Instances of Functions

Postby Sc4Freak » Sat Mar 26, 2011 9:34 pm UTC

Boost.Signals2

Code: Select all

#include <iostream>
#include <boost/signals2.hpp>

using namespace std;
using namespace boost::signals2;

void FooHandler(int a, int b)
{
    cout << "FooHandler: " << a << " " << b << endl;
}

int main()
{
    signal<void (int, int)> fooEvent;
    fooEvent.connect(FooHandler);

    fooEvent(1, 5); // calls FooHandler(1, 5)
}

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

Re: C++: Instances of Functions

Postby sourmìlk » Sat Mar 26, 2011 9:53 pm UTC

TheChewanater wrote:It's called an unnamed function, or lambda. What you probably want is either a callback or functor, though.


A callback is exactly what I want, thank you.
Sc4Freak wrote:Boost.Signals2

Code: Select all

#include <iostream>
#include <boost/signals2.hpp>

using namespace std;
using namespace boost::signals2;

void FooHandler(int a, int b)
{
    cout << "FooHandler: " << a << " " << b << endl;
}

int main()
{
    signal<void (int, int)> fooEvent;
    fooEvent.connect(FooHandler);

    fooEvent(1, 5); // calls FooHandler(1, 5)
}


I appreciate this, but I try to minimize the amount of external libraries I use. It's sort of an OCD thing: I like being familiar with the code I'm using. All I'm using are OpenGL and GLUT for this.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11129
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: C++: Instances of Functions

Postby Yakk » Sat Mar 26, 2011 10:00 pm UTC

The easy method is to use C function pointers. Function pointers can be NULL (and crash if called when NULL), and do not have an automatic "this" pointer.

A step up from that would be std::tr1::function objects (or std::function, depending on your C++ compiler -- in <functional> or <tr1/functional> I think).

A std::function< void(int) > is something that acts like a function that returns nothing (or void), and accepts one argument (an int).

Now, in C++0x they added some really good support for these -- lambda syntax.

Here is some C++0x:

Code: Select all

#include <functional>

class myClass {
  public:
    int y;
    myClass():y(-2) {}
    std::function< void(int) > myMethod;
    void setMethod( function<void(myClass*,int)> func )
    {
      myMethod = [this](int x)->void{ func(this,x); };
    }
};

void test() {
  myClass myInstance;
  myInstance.myMethod = [](int x)->void{std::cout << x << "\n";};
  myInstance.myMethod( 3 ); // prints 3
  myInstance.setMethod( []( myClass* self, int x )->void { std::cout << x << "," << self->y << "\n"; } );
  myInstance.myMethod( 7 ); // prints 7,-2
  myInstance.y = 9;
  myInstance.myMethod( 7 ); // prints 7,9
}

Now, some C++ compilers already have support for the above syntax.

You can do all of the above manually, however, even if the compiler doesn't support the new C++0x features.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

kmatzen
Posts: 214
Joined: Thu Nov 15, 2007 2:55 pm UTC
Location: Ithaca, NY

Re: C++: Instances of Functions

Postby kmatzen » Mon Mar 28, 2011 4:29 am UTC

I'm curious what this GUI library will accomplish better than the existing ones. Is there some domain specific goal?


If you want to restrict the style of how you implement events callbacks in your library to how GLUT handles events and callbacks, then the function pointer solution is the correct solution.

User avatar
TheChewanater
Posts: 1279
Joined: Sat Aug 08, 2009 5:24 am UTC
Location: lol why am I still wearing a Santa suit?

Re: C++: Instances of Functions

Postby TheChewanater » Mon Mar 28, 2011 4:48 am UTC

While we're suggesting design changes here, I'm a fan of overloading as a way to define custom event callbacks. Your derived class(es) would have some set of parameters to determine specific behavior for each button. This is probably the most encapsulated solution, although it could be a pain in some cases (such as if you want a large range of buttons with completely different behavior). Also, I like how gtkmm allows you to use either this or callbacks on the same widget.

For example,

Code: Select all

#include <libsourmìlk.h>

class StringPrinterButton : public SourMilk::Button
{
  string str;

  public:
    StringPrinterButton (string __str) str (__str) {}
    virtual void on_click ()
    {
      std::cout << str << std::endl;
    }
};
 

Code: Select all

int main ()
{
  SourMilk::GUI* gui = new SourMilk::GUI (640, 400);
  SourMilk::Button* button1 = new StringPrinterButton ("Button 1 pressed");
  SourMilk::Button* button2 = new StringPrinterButton ("Button 2 pressed");
  //snip...

  gui->add (button1);
  gui->add (button2);
  gui->run ();
}
 
ImageImage
http://internetometer.com/give/4279
No one can agree how to count how many types of people there are. You could ask two people and get 10 different answers.

User avatar
Sc4Freak
Posts: 673
Joined: Thu Jul 12, 2007 4:50 am UTC
Location: Redmond, Washington

Re: C++: Instances of Functions

Postby Sc4Freak » Mon Mar 28, 2011 4:59 am UTC

This is probably the most encapsulated solution


I think you might have that the other way around. :P

Inheritance is the strongest form of coupling that's possible in C++ and in terms of breaking encapsulation, it's second only to friend classes. If you just want to print something when your button is clicked, there's no reason why it needs to be exposed to all the protected innards of the base Button type. "Events" as implemented in C# or with boost::signals in C++ allow you to attach callbacks without exposing any more than necessary.

User avatar
TheChewanater
Posts: 1279
Joined: Sat Aug 08, 2009 5:24 am UTC
Location: lol why am I still wearing a Santa suit?

Re: C++: Instances of Functions

Postby TheChewanater » Mon Mar 28, 2011 5:55 am UTC

I was thinking of encapsulation in the program itself, not the library. So, if you had several buttons that each added 7 to a different variable, you could encapsulate incrementation in a class, instead of making a new function for it each time.

Code: Select all

template<typename T, T i>
class VariableIncrementerButton : public Button
{
  T variable, increment;
  
  public
:
    virtual VariableIncrementerButton (T __variable) : variable (__variable), increment (i) {}
    
    void on_click 
()
    {
        variable += increment;
    }
};
 

Code: Select all

float some_variable = 0.0f;
int some_other_variable = 0;

Button* button1 = VariableIncrementerButton<float, 7> (&some_variable);
Button* button2 = VariableIncrementerButton<int, 7> (&some_other_variable); 


(This code isn't tested, I may have made some mistake in it somewhere)
ImageImage
http://internetometer.com/give/4279
No one can agree how to count how many types of people there are. You could ask two people and get 10 different answers.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11129
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: C++: Instances of Functions

Postby Yakk » Mon Mar 28, 2011 12:10 pm UTC

Code: Select all

function< function< shared_ptr<Button>(void) >( function< void() >) > buttonMaker;
template<T type>
function< shared_ptr<Button>() > buttonIncrementorFactory( T& t, buttonMaker maker )
{
  return [&t, =maker]() {
    return maker( [&t]()->void {
      ++t;
    });
  };
}

buttonIncrementorFactory(foo, defaultButtonMaker) returns a function that produces buttons that increment foo.

An improvement on this would be making it fully chainable.

In a sense, this factory behaves like the class half of a in a class/instance pattern. You'd actually want a function that would augment a given Button with the behavior you want rather than one that creates a Button with the behavior you want for the full power of the class/instance hierarchy.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

Re: C++: Instances of Functions

Postby sourmìlk » Mon Apr 04, 2011 4:49 am UTC

kmatzen wrote:I'm curious what this GUI library will accomplish better than the existing ones. Is there some domain specific goal?


Aside from Qt there don't really exist libraries for this kind of thing, and I was having trouble getting Qt to work.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.

User avatar
TheChewanater
Posts: 1279
Joined: Sat Aug 08, 2009 5:24 am UTC
Location: lol why am I still wearing a Santa suit?

Re: C++: Instances of Functions

Postby TheChewanater » Mon Apr 04, 2011 6:47 pm UTC

What is "this kind of thing" supposed to mean? I'm sure whatever you're trying to solve has already been solved by some library.
ImageImage
http://internetometer.com/give/4279
No one can agree how to count how many types of people there are. You could ask two people and get 10 different answers.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11129
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: C++: Instances of Functions

Postby Yakk » Mon Apr 04, 2011 7:36 pm UTC

Chew: OpenGL based windowing toolkit.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

Re: C++: Instances of Functions

Postby sourmìlk » Tue Apr 05, 2011 6:21 am UTC

TheChewanater wrote:What is "this kind of thing" supposed to mean? I'm sure whatever you're trying to solve has already been solved by some library.


GUI elements in OpenGL. Buttons and frames and such.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.

User avatar
Sc4Freak
Posts: 673
Joined: Thu Jul 12, 2007 4:50 am UTC
Location: Redmond, Washington

Re: C++: Instances of Functions

Postby Sc4Freak » Tue Apr 05, 2011 8:51 am UTC

CEGUI

Don't try to write your own GUI library unless you have to. GUIs are notoriously hard to get right. CEGUI is popular, stable, and extensible. Not much point trying to write your own.

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

Re: C++: Instances of Functions

Postby sourmìlk » Tue Apr 05, 2011 9:00 am UTC

Sc4Freak wrote:CEGUI

Don't try to write your own GUI library unless you have to. GUIs are notoriously hard to get right. CEGUI is popular, stable, and extensible. Not much point trying to write your own.


What's the fun in using a pre-built one? When I'm coding for fun, I like to avoid external libraries as much as possible because I want to understand all of the code I'm using. I'm just using OpenGL because I don't know linear algebra (amongst other things), and I'm using GLUT because I want a cross-platform API for basic things like window creation.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

Re: C++: Instances of Functions

Postby sourmìlk » Thu Apr 07, 2011 9:27 am UTC

So, I thought I'd post this here because it's part of the same project.

GLUT has a function called glutKeyboardFunction, which takes a function in the format of void(char key, int x, int y) as arguments. So I might have something like this:

Code: Select all


void handleKeyPress
(unsigned char keyint xint y)
{
//do stuff
}

glutKeyboardFunc(handleKeyPress);
 


But what if I want to pass other arguments to the handleKeyPress function? So, something like

Code: Select all


void handleKeyPress
(char keyint xint yanotherVariable)
{
//do stuff
}

glutKeyboardFunc(handleKeyPress(/*somehow pass additional parameters*/));
 


Not sure how to describe it better. Basically, I want to send additional arguments to my handleKeyPress function, i.e. more arguments than glutKeyboardFunc gives.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.

User avatar
Sc4Freak
Posts: 673
Joined: Thu Jul 12, 2007 4:50 am UTC
Location: Redmond, Washington

Re: C++: Instances of Functions

Postby Sc4Freak » Thu Apr 07, 2011 10:55 am UTC

Short answer: you can't.

Almost every well-designed callback will have some way to pass user data to the callback (usually in the form of a void*). But since glutKeyboardFunc doesn't provide this, and since it takes a function pointer (instead of a templated function object like you see in the C++ standard library), there's no good way to do it. Things like boost::function allow you to bind arguments to function parameters, but you can't do that with a plain function pointer.

Your best bet is to use a global variable that you can access from within your handler. You'll run into all the usual problems and risks of using global variables, though.

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

Re: C++: Instances of Functions

Postby sourmìlk » Thu Apr 07, 2011 12:16 pm UTC

Sc4Freak wrote:Short answer: you can't.

Almost every well-designed callback will have some way to pass user data to the callback (usually in the form of a void*). But since glutKeyboardFunc doesn't provide this, and since it takes a function pointer (instead of a templated function object like you see in the C++ standard library), there's no good way to do it. Things like boost::function allow you to bind arguments to function parameters, but you can't do that with a plain function pointer.

Your best bet is to use a global variable that you can access from within your handler. You'll run into all the usual problems and risks of using global variables, though.


Oh, I guess I can just set some static data.....

okay, I think I got it. That's unfortunate though, that I can't do that.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11129
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: C++: Instances of Functions

Postby Yakk » Thu Apr 07, 2011 12:34 pm UTC

For maximum hack, you could build functions in assembly that store your "void" pointer as a constant, and twiddle with page execute/read/write permission flags to make it all legal as far as the OS is concerned.

Now, if you have at most a finite number of such handlers, you could use template-mojo to spawn a number of such functions that only differ by some integer constant, and use a lookup table to get at a "void*" equivalent. Ie:

Code: Select all

class Handlers_base {
public:
  typedef void(*pRawHandler)(unsigned char, int, int);
  typedef std::tr1::function< void(unsigned char, int, int ) > Handler;
protected:
  // uses myMap below:
  static Handler getNthHandler(int n);
  static void setNthHandler(int n, Handler);
  template<int n>
  static void template_handler(unsigned char key, int x, int y)
  {
    Handlers_base::getNthHandler(n)(key,x,y);
  }
private:
  typedef std::map<int, Handler> handler_map;
  static handler_map myMap;
};
template<int base, int width>
class Handlers_helper: virtual Handlers_base, Handlers_helper<base, width/2>, Handlers_helper<base+width/2, width/2> {
protected:
  static pHandler lookup(int n)
  {
    if (n < base+width/2)
    {
      return Handlers_helper<base,width/2>::lookup(n-base);
    } else {
      return Handlers_helper<base+width/2,(width+1)/2>::lookup(n-base-width/2);
    }
  }
};
template<int base>
class Handlers_helper<base, 1>: virtual Handlers_base
{
protected:
  static pHandler lookup(int n)
  {
    Assert(n== 0);
    return &Handlers_base::template_handler<base>;
  }
};
template<int count>
class Handlers: Handlers_helper<0, count>, virtual Handlers_base
{
public:
  static pHandler getHandlerN( int n )
  {
    return Handlers_helper<0, count>::lookup(n);
  }
  static void registerHandlerN( int n, Handler h )
  {
    Handlers_base::setNthHandler( n, h );
  }
};

When you declare a Handler<50>, this creates 50 static handlers for you to use. You can register function<> objects that can carry as much state as you like. When the nth static function is called, the nth function<> object is called by it.

You'd probably want to create a heap-like "memory-space management" system in front of the above. That would let you at run-time allocate who gets handler slot n (and catch if you run out of handlers).

Note that each static handler is a different function, which means it compiles to a different function, so it takes up memory (and disk space, because it is in the executable). The dynamic function<> handlers only take up space when assigned.

Edit Fixed my assert, added commentary.

In case you are wondering what I'm doing, I generate static methods for each of Handlers_base::template_handler<n> from the set of integers [0, count). Each is searched for (via a balanced binary tree) in the class hierarchy of Handlers_helper template functions. I need to instantiate pointers to these classes somewhere. In theory, I could make an array-based solution that didn't require lg(n) access when you wanted to find it, but I find functionally programming arrays to be harder than binary search trees and I'm lazy. (plus, not having compiled the above, doing the simpler case seems better).

Handlers<50>::getHandlerN(22) returns the 22nd handler in the form of a parameter-less function you can pass to the API.
Handlers<50>::setNthHandler( Handler ) takes a std::tr1::function< void(unsigned char, int, int) >, which you can build by making a class with an operator() or other methods. Ie:

Code: Select all

struct DoStuff {
  void* data;
  typedef void(*pFunc)(void* data, unsigned char, int, int );
  pFunc func;
  void operator()( unsigned char a, int b, int c ) const
  {
    func( data, a, b, c );
  }
  DoStuff( void* data_, pFunc func_ ): data(data_), func(func_) {}
};


Handlers<50>::registerHandlerN( 22, DoStuff( myFoo, myFunc ) );
pHandler handler = Handlers<50>::getHandlerN( 22 );
// calling handler( a, b c) ends up calling myFunc( myFoo, a, b, c )
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

User avatar
sourmìlk
If I can't complain, can I at least express my fear?
Posts: 6393
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong
Contact:

Re: C++: Instances of Functions

Postby sourmìlk » Thu Apr 07, 2011 6:26 pm UTC

Yeah...

in lieu of "maximum hack" I got it working with some slightly creative use of static functions.
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 8 guests