4

Say I have listeners built in C++98, they are abstract and must for example implement ActionPerformed. In C++0x is there a way to do similar to Java:

button.addActionListener(new ActionListener() {
public void actionPerfored(ActionEvent e)
{
// do something.
}
});

Thanks

jmasterx
  • 52,639
  • 96
  • 311
  • 557

4 Answers4

6

Not exactly, but you can do something close with Lambdas.

i.e.:

class ActionListener
{
public:
   typedef std::function<void(ActionEvent&)> ActionCallback;

public:
   ActionListener( ActionCallback cb )
      :_callback(cb)
   {}

   void fire(ActionEvent& e )
   {
      _callback(e);
   }

private:
   ActionCallback _callback;
};


..
button.addActionListener( new ActionListener(
   []( ActionEvent& e )
   {
       ...
   }
));
Gerald
  • 23,011
  • 10
  • 73
  • 102
  • 6
    A proper C++ interface would of course have any such "addActionListener" take a *proper* functor, one that overloads `operator()`, rather than overriding a virtual function and relying on inheritance. – Nicol Bolas Dec 23 '11 at 18:15
5

No you can't do that.

If you give up on "similar to Java", though, and just use a functor, you'll find C++11 lambdas very helpful.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • The problem is I have lots of code that uses the Java way of doing it and Lambdas cant help here. – jmasterx Dec 23 '11 at 05:16
  • You can't do it the Java way, so you'll have to find another way, and Lambdas can certainly help you there. – Gerald Dec 23 '11 at 06:51
  • 7
    You have lots of *C++ code* that uses the Java approach? Trying to pretend C++ is Java is suboptimal, and definitely going to make your life miserable. – Ben Voigt Dec 23 '11 at 07:27
5

This is C++, not Java, so writing C++ like Java won't work well.

Anyway, you could create an adaptor function. Suppose

typedef int ActionEvent; // <-- just for testing

class ActionListener
{
public:
    virtual void actionPerformed(const ActionEvent& event) = 0;
};

Then we could write a templated subclass of ActionListener that wraps a function object:

#include <memory>

template <typename F>
class ActionListenerFunctor final : public ActionListener
{
public:
    template <typename T>
    ActionListenerFunctor(T&& function)
        : _function(std::forward<T>(function)) {}

    virtual void actionPerformed(const ActionEvent& event)
    {
        _function(event);
    }
private:
    F _function;
};

template <typename F>
std::unique_ptr<ActionListenerFunctor<F>> make_action_listener(F&& function)
{
    auto ptr = new ActionListenerFunctor<F>(std::forward<F>(function));
    return std::unique_ptr<ActionListenerFunctor<F>>(ptr);
}

and then use make_action_listener to wrap a lambda, e.g ( http://ideone.com/SQaLz ).

#include <iostream>

void addActionListener(std::shared_ptr<ActionListener> listener)
{
    ActionEvent e = 12;
    listener->actionPerformed(e);
}

int main()
{
    addActionListener(make_action_listener([](const ActionEvent& event)
    {
        std::cout << event << std::endl;
    }));
}

Note that this is far from idiomatic C++, where in addActionListener() you should simply take a const std::function<void(const ActionEvent&)>&, or even a template parameter for maximum efficiency, and supply the lambda directly.

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
1

I think we can do this in C++ using lambdas

button.addActionListener([]()->ActionListener*{ struct A: ActionListener {
void actionPerfored(ActionEvent e)
{
// do something.
}
}; return new A;}());

It should be easy to wrap this up in a macro.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 1
    Too bad we can't just do `[]{ return new struct:ActionListener{ ... }; }`. That would actually look a lot like the Java version... – Xeo Jan 23 '12 at 01:08