Major change: in expressInterest, remove the Closure class and use function objects for onData and onTimeout.
diff --git a/Makefile.am b/Makefile.am
index aecd075..53eb83f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -40,7 +40,6 @@
 
 libndn_cpp_la_SOURCES = \
   config.h \
-  ndn-cpp/closure.hpp \
   ndn-cpp/common.hpp ndn-cpp/common.cpp \
   ndn-cpp/data.cpp ndn-cpp/c/data.h ndn-cpp/data.hpp \
   ndn-cpp/interest.cpp ndn-cpp/c/interest.h ndn-cpp/interest.hpp \
diff --git a/Makefile.in b/Makefile.in
index 1d02b90..1944982 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -481,7 +481,6 @@
 
 libndn_cpp_la_SOURCES = \
   config.h \
-  ndn-cpp/closure.hpp \
   ndn-cpp/common.hpp ndn-cpp/common.cpp \
   ndn-cpp/data.cpp ndn-cpp/c/data.h ndn-cpp/data.hpp \
   ndn-cpp/interest.cpp ndn-cpp/c/interest.h ndn-cpp/interest.hpp \
diff --git a/ndn-cpp/closure.hpp b/ndn-cpp/closure.hpp
deleted file mode 100644
index 210ad17..0000000
--- a/ndn-cpp/closure.hpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * @author: Jeff Thompson
- * This is a port of py from PyCCN, written by: 
- * Derek Kulinski <takeda@takeda.tk>
- * Jeff Burke <jburke@ucla.edu>
- * 
- * See COPYING for copyright and distribution information.
- */
-
-#ifndef NDN_CLOSURE_HPP
-#define NDN_CLOSURE_HPP
-
-#include "common.hpp"
-
-namespace ndn {
-
-enum UpcallResult {
-  CLOSURE_RESULT_ERR               = -1, // upcall detected an error
-  CLOSURE_RESULT_OK                =  0, // normal upcall return
-  CLOSURE_RESULT_REEXPRESS         =  1, // reexpress the same interest again
-  CLOSURE_RESULT_INTEREST_CONSUMED =  2, // upcall claims to consume interest
-  CLOSURE_RESULT_VERIFY            =  3, // force an unverified result to be verified
-  CLOSURE_RESULT_FETCHKEY          =  4  // get the key in the key locator and re-call the interest
-};
-
-enum UpcallKind {
-  UPCALL_FINAL              = 0, // handler is about to be deregistered
-  UPCALL_INTEREST           = 1, // incoming interest
-  UPCALL_CONSUMED_INTEREST  = 2, // incoming interest, someone has answered
-  UPCALL_DATA               = 3, // incoming verified data packet
-  UPCALL_INTEREST_TIMED_OUT = 4, // interest timed out
-  UPCALL_DATA_UNVERIFIED    = 5, // data packet that has not been verified
-  UPCALL_DATA_BAD           = 6  // verification failed  
-};
-
-class Node;
-class Interest;
-class Data;
-
-class UpcallInfo {
-public:
-  UpcallInfo(Node *node, const ptr_lib::shared_ptr<const Interest> &interest, int matchedComps, const ptr_lib::shared_ptr<Data> &data) 
-  : node_(node), interest_(interest), data_(data)
-  {
-  }
-  
-  Node *getNode() { return node_; }
-  
-  const ptr_lib::shared_ptr<const Interest> &getInterest() const { return interest_; }
-  
-  const ptr_lib::shared_ptr<Data> &getData() const { return data_; }
-  
-private:
-  Node *node_;
-  ptr_lib::shared_ptr<const Interest> interest_;
-  ptr_lib::shared_ptr<Data> data_;
-};
-
-class Closure {
-public:
-  virtual UpcallResult upcall(UpcallKind kind, const UpcallInfo &upcallInfo) = 0;
-};
-
-}
-
-#endif
diff --git a/ndn-cpp/face.hpp b/ndn-cpp/face.hpp
index b317fa6..8db72a8 100644
--- a/ndn-cpp/face.hpp
+++ b/ndn-cpp/face.hpp
@@ -46,21 +46,56 @@
 
   /**
    * Encode name as an Interest. If interestTemplate is not 0, use its interest selectors.
-   * Send the interest through the transport, read the entire response and call
-   * closure->upcall(UPCALL_DATA (or UPCALL_DATA_UNVERIFIED),
-   *                 UpcallInfo(this, interest, 0, data)).
-   * @param name reference to a Name for the interest.  This does not keep a pointer to the Name object.
-   * @param closure a pointer for the Closure.  The caller must manage the memory for the Closure.  This will not try to delete it.
+   * Send the interest through the transport, read the entire response and call onData(interest, data).
+   * @param name A reference to a Name for the interest.  This does not keep a pointer to the Name object.
    * @param interestTemplate if not 0, copy interest selectors from the template.   This does not keep a pointer to the Interest object.
+   * @param onData A function object to call when a matching data packet is received.  This copies the function object, so you may need to
+   * use func_lib::ref() as appropriate.
+   * @param onTimeout A function object to call if the interest times out.  If onTimeout is an empty OnTimeout(), this does not use it.
+   * This copies the function object, so you may need to use func_lib::ref() as appropriate.
    */
-  void expressInterest(const Name &name, Closure *closure, const Interest *interestTemplate)
+  void expressInterest(const Name &name, const Interest *interestTemplate, const OnData &onData, const OnTimeout &onTimeout)
   {
-    node_.expressInterest(name, closure, interestTemplate);
+    node_.expressInterest(name, interestTemplate, onData, onTimeout);
+  }
+
+  /**
+   * Encode name as an Interest, using a default interest lifetime.
+   * Send the interest through the transport, read the entire response and call onData(interest, data).
+   * @param name A reference to a Name for the interest.  This does not keep a pointer to the Name object.
+   * @param onData A function object to call when a matching data packet is received.  This copies the function object, so you may need to
+   * use func_lib::ref() as appropriate.
+   * @param onTimeout A function object to call if the interest times out.  If onTimeout is an empty OnTimeout(), this does not use it.
+   * This copies the function object, so you may need to use func_lib::ref() as appropriate.
+   */
+  void expressInterest(const Name &name, const OnData &onData, const OnTimeout &onTimeout) 
+  {
+    node_.expressInterest(name, onData, onTimeout);
   }
   
-  void expressInterest(const Name &name, Closure *closure)
+  /**
+   * Encode name as an Interest. If interestTemplate is not 0, use its interest selectors.
+   * Send the interest through the transport, read the entire response and call onData(interest, data).
+   * @param name A reference to a Name for the interest.  This does not keep a pointer to the Name object.
+   * @param interestTemplate if not 0, copy interest selectors from the template.   This does not keep a pointer to the Interest object.
+   * @param onData A function object to call when a matching data packet is received.  This copies the function object, so you may need to
+   * use func_lib::ref() as appropriate.
+   */
+  void expressInterest(const Name &name, const Interest *interestTemplate, const OnData &onData)
   {
-    node_.expressInterest(name, closure);
+    node_.expressInterest(name, interestTemplate, onData);
+  }
+  
+  /**
+   * Encode name as an Interest, using a default interest lifetime.
+   * Send the interest through the transport, read the entire response and call onData(interest, data).
+   * @param name A reference to a Name for the interest.  This does not keep a pointer to the Name object.
+   * @param onData A function object to call when a matching data packet is received.  This copies the function object, so you may need to
+   * use func_lib::ref() as appropriate.
+   */
+  void expressInterest(const Name &name, const OnData &onData)
+  {
+    node_.expressInterest(name, onData);
   }
   
   /**
diff --git a/ndn-cpp/node.cpp b/ndn-cpp/node.cpp
index 77befa3..802b84a 100644
--- a/ndn-cpp/node.cpp
+++ b/ndn-cpp/node.cpp
@@ -22,8 +22,8 @@
   return t.tv_sec * 1000.0 + t.tv_usec / 1000.0;
 }
   
-Node::PitEntry::PitEntry(const ptr_lib::shared_ptr<const Interest> &interest, Closure *closure)
-: interest_(interest), closure_(closure)
+Node::PitEntry::PitEntry(const ptr_lib::shared_ptr<const Interest> &interest, const OnData &onData, const OnTimeout &onTimeout)
+: interest_(interest), onData_(onData), onTimeout_(onTimeout)
 {
   // Set up timeoutTime_.
   if (interest_->getInterestLifetimeMilliseconds() >= 0.0)
@@ -44,14 +44,13 @@
 bool Node::PitEntry::checkTimeout(Node *parent, double nowMilliseconds)
 {
   if (timeoutTimeMilliseconds_ >= 0.0 && nowMilliseconds >= timeoutTimeMilliseconds_) {
-    shared_ptr<Data> dummyData;
-    UpcallInfo upcallInfo(parent, interest_, 0, dummyData);
-    
-    // Ignore all exceptions.
-    try {
-      closure_->upcall(UPCALL_INTEREST_TIMED_OUT, upcallInfo);
+    if (onTimeout_) {
+      // Ignore all exceptions.
+      try {
+        onTimeout_(interest_);
+      }
+      catch (...) { }
     }
-    catch (...) { }
     
     return true;
   }
@@ -59,7 +58,7 @@
     return false;
 }
 
-void Node::expressInterest(const Name &name, Closure *closure, const Interest *interestTemplate)
+void Node::expressInterest(const Name &name, const Interest *interestTemplate, const OnData &onData, const OnTimeout &onTimeout)
 {
   shared_ptr<const Interest> interest;
   if (interestTemplate)
@@ -71,7 +70,7 @@
   else
     interest.reset(new Interest(name, 4000.0));
   
-  shared_ptr<PitEntry> pitEntry(new PitEntry(interest, closure));
+  shared_ptr<PitEntry> pitEntry(new PitEntry(interest, onData, onTimeout));
   pit_.push_back(pitEntry);
   
   shared_ptr<vector<unsigned char> > encoding = pitEntry->getInterest()->wireEncode();  
@@ -109,12 +108,11 @@
     
     int iPitEntry = getEntryIndexForExpressedInterest(data->getName());
     if (iPitEntry >= 0) {
-      UpcallInfo upcallInfo(this, pit_[iPitEntry]->getInterest(), 0, data);
-      
-      // Remove the PIT entry before the calling the callback.
-      Closure *closure = pit_[iPitEntry]->getClosure();
+      // Copy pointers to the needed objects and remove the PIT entry before the calling the callback.
+      const OnData onData = pit_[iPitEntry]->getOnData();
+      const ptr_lib::shared_ptr<const Interest> interest = pit_[iPitEntry]->getInterest();
       pit_.erase(pit_.begin() + iPitEntry);
-      closure->upcall(UPCALL_DATA, upcallInfo);
+      onData(interest, data);
     }
   }
 }
diff --git a/ndn-cpp/node.hpp b/ndn-cpp/node.hpp
index b72ba9c..911298b 100644
--- a/ndn-cpp/node.hpp
+++ b/ndn-cpp/node.hpp
@@ -6,13 +6,23 @@
 #ifndef NDN_NODE_HPP
 #define NDN_NODE_HPP
 
+#include "common.hpp"
 #include "interest.hpp"
-#include "closure.hpp"
 #include "transport/udp-transport.hpp"
 #include "encoding/binary-xml-element-reader.hpp"
 
 namespace ndn {
 
+/**
+ * An OnData function object is used to pass a callback to expressInterest.
+ */
+typedef func_lib::function<void(const ptr_lib::shared_ptr<const Interest> &, const ptr_lib::shared_ptr<Data> &)> OnData;
+
+/**
+ * An OnTimeout function object is used to pass a callback to expressInterest.
+ */
+typedef func_lib::function<void(const ptr_lib::shared_ptr<const Interest> &)> OnTimeout;
+
 class Face;
   
 class Node : public ElementListener {
@@ -48,18 +58,53 @@
 
   /**
    * Encode name as an Interest. If interestTemplate is not 0, use its interest selectors.
-   * Send the interest through the transport, read the entire response and call
-   * closure->upcall(UPCALL_DATA (or UPCALL_DATA_UNVERIFIED),
-   *                 UpcallInfo(this, interest, 0, data)).
-   * @param name reference to a Name for the interest.  This does not keep a pointer to the Name object.
-   * @param closure a pointer for the Closure.  The caller must manage the memory for the Closure.  This will not try to delete it.
+   * Send the interest through the transport, read the entire response and call onData(interest, data).
+   * @param name A reference to a Name for the interest.  This does not keep a pointer to the Name object.
    * @param interestTemplate if not 0, copy interest selectors from the template.   This does not keep a pointer to the Interest object.
+   * @param onData A function object to call when a matching data packet is received.  This copies the function object, so you may need to
+   * use func_lib::ref() as appropriate.
+   * @param onTimeout A function object to call if the interest times out.  If onTimeout is an empty OnTimeout(), this does not use it.
+   * This copies the function object, so you may need to use func_lib::ref() as appropriate.
    */
-  void expressInterest(const Name &name, Closure *closure, const Interest *interestTemplate);
-  
-  void expressInterest(const Name &name, Closure *closure)
+  void expressInterest(const Name &name, const Interest *interestTemplate, const OnData &onData, const OnTimeout &onTimeout);
+
+  /**
+   * Encode name as an Interest, using a default interest lifetime.
+   * Send the interest through the transport, read the entire response and call onData(interest, data).
+   * @param name A reference to a Name for the interest.  This does not keep a pointer to the Name object.
+   * @param onData A function object to call when a matching data packet is received.  This copies the function object, so you may need to
+   * use func_lib::ref() as appropriate.
+   * @param onTimeout A function object to call if the interest times out.  If onTimeout is an empty OnTimeout(), this does not use it.
+   * This copies the function object, so you may need to use func_lib::ref() as appropriate.
+   */
+  void expressInterest(const Name &name, const OnData &onData, const OnTimeout &onTimeout) 
   {
-    expressInterest(name, closure, 0);
+    expressInterest(name, 0, onData, onTimeout);
+  }
+  
+  /**
+   * Encode name as an Interest. If interestTemplate is not 0, use its interest selectors.
+   * Send the interest through the transport, read the entire response and call onData(interest, data).
+   * @param name A reference to a Name for the interest.  This does not keep a pointer to the Name object.
+   * @param interestTemplate if not 0, copy interest selectors from the template.   This does not keep a pointer to the Interest object.
+   * @param onData A function object to call when a matching data packet is received.  This copies the function object, so you may need to
+   * use func_lib::ref() as appropriate.
+   */
+  void expressInterest(const Name &name, const Interest *interestTemplate, const OnData &onData)
+  {
+    expressInterest(name, interestTemplate, onData, OnTimeout());
+  }
+  
+  /**
+   * Encode name as an Interest, using a default interest lifetime.
+   * Send the interest through the transport, read the entire response and call onData(interest, data).
+   * @param name A reference to a Name for the interest.  This does not keep a pointer to the Name object.
+   * @param onData A function object to call when a matching data packet is received.  This copies the function object, so you may need to
+   * use func_lib::ref() as appropriate.
+   */
+  void expressInterest(const Name &name, const OnData &onData)
+  {
+    expressInterest(name, 0, onData);
   }
 
   /**
@@ -85,14 +130,16 @@
     /**
      * Create a new PitEntry and set the timeoutTime_ based on the current time and the interest lifetime.
      * @param interest A shared_ptr for the interest.
-     * @param closure A pointer to the closure with the callbacks to call on match. 
-     * The caller must manage the memory for the Closure.  This will not try to delete it.
+     * @param onData A function object to call when a matching data packet is received.  This copies the function object, so you may need to
+     * use func_lib::ref() as appropriate.
+     * @param onTimeout A function object to call if the interest times out.  If onTimeout is an empty OnTimeout(), this does not use it.
+     * This copies the function object, so you may need to use func_lib::ref() as appropriate.
      */
-    PitEntry(const ptr_lib::shared_ptr<const Interest> &interest, Closure *closure);
+    PitEntry(const ptr_lib::shared_ptr<const Interest> &interest, const OnData &onData, const OnTimeout &onTimeout);
     
     const ptr_lib::shared_ptr<const Interest> &getInterest() { return interest_; }
     
-    Closure *getClosure() { return closure_; }
+    const OnData &getOnData() { return onData_; }
     
     /**
      * Get the struct ndn_Interest for the interest_.
@@ -108,7 +155,7 @@
     }
     
     /**
-     * If this interest is timed out, call the timeout callback and return true.
+     * If this interest is timed out, call onTimeout_ (if defined) and return true.
      * @param parent The parent Node for the UpcallInfo.
      * @param nowMilliseconds The current time in milliseconds from gettimeofday.
      * @return true if this interest timed out and the timeout callback was called, otherwise false.
@@ -121,7 +168,8 @@
     std::vector<struct ndn_ExcludeEntry> excludeEntries_;
     struct ndn_Interest interestStruct_;
   
-    Closure *closure_;
+    const OnData onData_;
+    const OnTimeout onTimeout_;
     double timeoutTimeMilliseconds_; /**< The time when the interest times out in milliseconds according to gettimeofday, or -1 for no timeout. */
   };
   
diff --git a/tests/test-get-async.cpp b/tests/test-get-async.cpp
index 692ed36..60b02b5 100644
--- a/tests/test-get-async.cpp
+++ b/tests/test-get-async.cpp
@@ -14,51 +14,42 @@
 using namespace ndn;
 using namespace ptr_lib;
 
-class MyClosure : public Closure {
-public:
-  MyClosure()
-  : gotCallbackCount_(0)
-  {  
-  }
-  
-  virtual UpcallResult upcall(UpcallKind kind, const UpcallInfo &upcallInfo)
-  {
-    if (kind == UPCALL_DATA || kind == UPCALL_DATA_UNVERIFIED) {
-      ++gotCallbackCount_;
-      cout << "Got data packet with name " << upcallInfo.getData()->getName().to_uri() << endl;
-      for (unsigned int i = 0; i < upcallInfo.getData()->getContent().size(); ++i)
-        cout << upcallInfo.getData()->getContent()[i];
-      cout << endl;
-      
-      return CLOSURE_RESULT_OK;
-    }
-    else if (kind == UPCALL_INTEREST_TIMED_OUT) {
-      ++gotCallbackCount_;
-      cout << "Time out for interest  " << upcallInfo.getInterest()->getName().toUri() << endl;    
-      return CLOSURE_RESULT_OK;
-    }
-    else
-      return CLOSURE_RESULT_OK;
-  }
-  
-  int gotCallbackCount_;
-};
+static int CallbackCount = 0;
+
+void myOnData(const ptr_lib::shared_ptr<const Interest> &interest, const ptr_lib::shared_ptr<Data> &data)
+{
+  ++CallbackCount;
+  cout << "Got data packet with name " << data->getName().to_uri() << endl;
+  for (unsigned int i = 0; i < data->getContent().size(); ++i)
+    cout << data->getContent()[i];
+  cout << endl;  
+}
+
+void myOnTimeout(const ptr_lib::shared_ptr<const Interest> &interest)
+{
+  ++CallbackCount;
+  cout << "Time out for interest " << interest->getName().toUri() << endl;    
+}
 
 int main(int argc, char** argv)
 {
   try {
-    MyClosure closure;
     Face face("E.hub.ndn.ucla.edu");
     
     Name name1("/ndn/ucla.edu/apps/ndn-js-test/hello.txt/level2/%FD%05%0B%16%7D%95%0E");    
     cout << "Express name  " << name1.toUri() << endl;
-    face.expressInterest(name1, &closure);
+    face.expressInterest(name1, myOnData, myOnTimeout);
+    
     Name name2("/ndn/ucla.edu/apps/lwndn-test/howdy.txt/%FD%05%05%E8%0C%CE%1D");
     cout << "Express name  " << name2.toUri() << endl;
-    face.expressInterest(name2, &closure);
+    face.expressInterest(name2, myOnData, myOnTimeout);
+    
+    Name name3("/test/timeout");
+    cout << "Express name  " << name3.toUri() << endl;
+    face.expressInterest(name3, myOnData, myOnTimeout);
 
     // The main event loop.
-    while (closure.gotCallbackCount_ < 2) {
+    while (CallbackCount < 3) {
       face.processEvents();
       // We need to sleep for a few milliseconds so we don't use 100% of the CPU.
       usleep(10000);