Home > Coding > C++ Closures

C++ Closures

The upcoming C++0x standard has already ratified the Lambda Expression concept, which is also being touted as closures. Kinda.

I’m not keen on the syntax, at all. It’s ugly. Even Herb Sutter fumbles with it. The whole thing feels hacky and grumble-grumble-give-them-something that lets you do it.

The problem with a closure is that you want to have your cake and eat it. You want to be able to pass around a pointer to a piece of code to execute, but then you probably want it to be stateful and packaged with a bunch of data.

You can use a struct/class with an operator() functor to implement this, and that’s OK as long as what you are passing the object pointer to knows that’s what you are doing.

But that is not very often the case, especially if you have to go through an API. Infact, function pointers are generally a PITA of C++ programmers.

For a closure, you want the ability to pass a pointer to a member function, and C++ can’t do that because from the outside, a member function is a part of an instance of an object, but on the inside it’s actually a pointer to a static piece of code that is called with a hidden argument, “Object* this” which points to the instance.

myObject->hurtMonkeys(12);

is actually

MyObject::hurtMonkeys(myObject, 12);

And that’s the problem (not the monkeys, or that we’re going to hurt 12 of them). You can’t pass two addresses in one pointer.

What you’d need is some kind of extension to each object instance that stores both the function pointer and the this pointer.

Anyway, I use closures often enough that I’m putting together a C++ language extension proposal that would introduce a “closure” declaration:

closure FriendsList
{
  connect_t connection;
  playerId_t playerId;

  // This is the routine that will be invoked from operator()
  // with the right value for "this" passed to us.
  void operator(FriendsList)(database_results_t* results)
  {
    Player* player = Player::Lookup(playerId);
    // player went away?
    if ( player == NULL || player->Connection() != connection )
      return;

    if ( results == NULL || results->size() != 1 )
      player->Message(NO_FRIENDS_LIST);
    else
      player->Message(results->Row(0));
  }
};

void DotGetFriendsList(const Player& player)
{
  FriendsList closure(player.Connection(), player.PlayerID());

  DB::Query query("SELECT GROUP_CONCAT(friend) FROM friends WHERE player = %u", player.PlayerID());
  query.queue(closure);
}

Categories: Coding Tags: ,
  1. Drave
    February 22, 2010 at 12:00 pm

    What is the difference between your closure and a functionobject?

    struct FriendsList
    {
      connect_t connection;
      playerId_t playerId;
      
      FriendsList(connect_t conn, playerId_t id)
        : connection(conn)
        , playerId(id)
      {
      }
    
      void operator()(database_results_t* results)
      {
        Player* player = Player::Lookup(playerId);
        // player went away?
        if ( player == NULL || player->Connection() != connection )
          return;
    
        if ( results == NULL || results->size() != 1 )
          player->Message(NO_FRIENDS_LIST);
        else
          player->Message(results->Row(0));
      }
    };
    
    void DotGetFriendsList(const Player& player)
    {
      FriendsList funcObj(player.Connection(), player.PlayerID());
    
      DB::Query query("SELECT GROUP_CONCAT(friend) FROM friends WHERE player = %u", player.PlayerID());
      query.queue(funcObj);
    }
    
    // Query::queue then needs one of the following signatures:
    template<typename FuncT>
    void Query::queue(FuncT func);
    // or
    void Query::queue(std::tr1::function<void(database_results_t*)> func);
    
  2. February 22, 2010 at 6:04 pm

    A function object is a pointer to the object, the callee must explicitly call funcObj->operator()(arguments).

    Which means you can’t pass a function object pointer to something that is expecting a pointer to a function.

  3. Drave
    February 23, 2010 at 8:44 am

    “A function object is a pointer to the object, the callee must explicitly call funcObj->operator()(arguments).”
    Not if you do it the way I showed. The only question is, how is the signature of Query::queue. If you do it like I have written, then there is nothing in the way.

    As an example:

    #include <iostream>
    
    template<typename FuncT>
    void useIt(FuncT funcObj)
    {
    	funcObj("Hello!");
    }
    
    
    struct Output
    {
    	char const* user;
    	
    	void operator ()(char const* message)
    	{
    		std::cout << user << ": " << message << std::endl;
    	}
    };
    
    
    int main()
    {	
    	Output output = { "KFS1_GOD" };
    	useIt(output);
    
    	return 0;
    }
    

    If you use TR1, then you can use std::tr1::function or if you use Boost, then boost::function:

    http://www.boost.org/doc/libs/1_42_0/doc/html/function.html

    And even if you want to go over a this pointer, there are still helper functions for that case, like std::tr1::bind and boost::bind:

    http://www.boost.org/doc/libs/1_42_0/libs/bind/bind.html

    ;)

  4. February 24, 2010 at 12:23 am

    You had to change the interface in order to do that, by templating useIt.

    I’m talking about interop and external APIs, things like registering callbacks with “C” apps.

    #include <pthread.h>
    #include <string>
    #include <iostream>
    
    typedef void(*CallbackPtr)(const char*);
    
    extern "C" void
    dbQuery(const char* query, CallbackPtr handler)
    {
      if ( query != NULL )
        handler("OK") ;
    }
    
    void
    dbHandler(const char* result)
    {
     std::cout << "dbHandle(" << result << ")" << std::endl ;
    }
    
    struct FuncObj
    {
      unsigned int playerId ;
      unsigned int connectionId ;
    
      FuncObj(unsigned int pid, unsigned int cid)
       : playerId(pid), connectionId(cid)
       {}
    
      void operator()(const char* result)
      {
        std::cout << "operator("<<result<<") "
    				<< playerId << "/" << connectionId << std::endl ;
      }
    } ;
    
    int
    main(int argc, char* argv[])
    {
      FuncObj obj(1234, 1) ;
      dbQuery("kfs1", dbHandler) ;
    #if !defined(DISABLE_FUNCOBJ)
      dbQuery("kfs1", obj) ;
    #endif
    }
    

    Produces:

    osmith@ubuntu:~$ g++ -Wall -o ptest ptest.cpp
    ptest.cpp: In function ‘int main(int, char**)’:
    ptest.cpp:42: error: cannot convert ‘FuncObj’ to ‘void (*)(const char*)’ for argument ‘2’ to ‘void dbQuery(const char*, void (*)(const char*))’
    osmith@ubuntu:~$ g++ -Wall -DDISABLE_FUNCOBJ -o ptest ptest.cpp
    osmith@ubuntu:~$ ./ptest
    dbHandle(OK)

    Note that I’m including “dbQuery”s definition solely for completeness; in practice it would just be an extern definition to a library function.

  5. Drave
    February 24, 2010 at 11:51 am

    “I’m talking about interop and external APIs, things like registering callbacks with “C” apps.”
    Seems like I missed that.

    Well but it is still no problem … well perhaps not a big one. I mean you’re coding in C++, use the magic! (I’ve used pastebin for the following code, because it perhaps would be too big for this page ;))
    Old style magic:

    http://pastebin.com/K51F3CZ6

    With the next standard there are even more and easier possibilities. It will probably look something like this:

    http://pastebin.com/MPyKpFiV

    Perhaps it will work in an even easier way, I don’t have much of experience in the coming standard and its features.

    Note: I’m using TLS (Thread Local Storage) on both versions. So don’t use the callbacks accross threads, it won’t work.

  6. February 24, 2010 at 6:35 pm

    Oh, there are ways you can futz it, but none of them as as readable as replacing the whole thing with the closure concept. If you go back to the first paragraph of my post – you can use a C++0x lambda function to solve the problem. It’s just … fugly.

    And the thing with the lambda solution is that it gets really ugly if you have a large number of arguments you want to martial. Solution? Create a struct.

    Well, wait a second: If I’m going to have to create a struct to marshal the data, why can’t we have a struct replacement that does this job for me?

    Hence closure. Its primary purose: to serve as a function pointer (not a function-object pointer).

  7. Drave
    February 24, 2010 at 7:51 pm

    You can’t do this with lambda expressions, because lambda expressions create an object and not a function. So you can’t pass your lambda expression to the C API function.

    Well sure, it doesn’t look very nice in the background, but the user of that construct don’t need to know about this. In the coming standard there will be ways do automaticly find out the arguments a function wants, so you probably won’t even have to set any template parameters, it will deduce them. Like I already said, I don’t have enough experience how you can do this, but I already saw such things … somewhere :)

    So you have the possibility to create some type of closure yourself, you even get full control to modify it for your needs. And like I know the standard committee, they don’t like redundant things.

    But instead of closures I would go down to the root of the problem. C++ member functions can’t be used as normal functions. That is the main problem with member functions. If the standard would solve this problem, you could perhaps even make functions that belongs to the object and not the class. So at the end you can simply pass a member function to the C API. That would be cool!

  8. February 25, 2010 at 11:19 am

    You can’t do this with lambda expressions, because lambda expressions create an object and not a function. So you can’t pass your lambda expression to the C API function.

    Gah, true – I mean you can use it to solve the problem if you have control over the API, and can pass an object to something.

    But there isn’t a trivial way to create stateful function pointers in C++, or anything close to trivial :) Unless the callee (the thing you are passing the function pointer to) is object aware, you need two pointer values: object and function.

    So, for example, you can get away with pthread_create … you can pass it the object as the void* argument the thread functions has to take and use a static class member to receive that object and invoke the relevant member function.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 216 other followers

%d bloggers like this: