Domanda

In Java, when I need a callback function, I have to implement an anonymous class. Inside the anonymous class, I can access the outside variables if they're final.

Now I'm doing the same thing in C++. I understand that C++ lambda works better but sometimes I need to pass in many functions where with anonymous classes, I only need to pass in one instance.

I tried the following example. It works with GCC 4.3.4.

class IA {
public:
  virtual int f(int x) = 0;  
};

int main() {
    class : public IA {
        int f(int x) { return x + 1; }
    } a;
    doFancyWork(&a);
    return 0;
}

Is it possible to capture the outside variables like this?

int main() {
    int y = 100; // mark y as final if possible
    class : public IA {
        int f(int x) { return x + y; }
    } a;
    return 0;
}

UPDATE:

The second example won't compile. The errors are here,

prog.cpp: In member function ‘virtual int main()::<anonymous class>::f(int)’:
prog.cpp:9: error: use of ‘auto’ variable from containing function
prog.cpp:7: error:   ‘int y’ declared here
prog.cpp: In function ‘int main()’:
prog.cpp:7: warning: unused variable ‘y’

UPDATE:

I just realized a few more problems in doing this:

  • I cannot write a constructor because the class doesn't have a name
  • initializer list doesn't allow inheritance.
  • any change to make it compile makes the code unreadable.

I think I have to move away from anonymous classes.

È stato utile?

Soluzione

There is no way to automatically capture those variables, but you can use an alternative approach. This is if you want to capture by reference:

int main() {
    int y = 100; // mark y as final if possible
    class IB : public IA {
    public:
      IB(int& y) : _y(y) {}
      int f(int x) { return x + _y; }
    private:
      int& _y;
    } a (y);
    return 0;
}

If you want to capture by value, just change int& into int.

Anyway, you may consider using a tuple of lambdas as a "multi-callback" object if that is what bothers you about individual lambdas. You would still have everything packed in one object and capturing would be done for free.

Just as an example:

auto callbacks = make_tuple(
    [] (int x) { cout << x << endl; },
    [&] () { cout << y << endl; }, // y is captured by reference
    [=] (int x) { cout << x + y << endl; }, // y is captured by value
    // other lambdas here, if you want...
    );

Altri suggerimenti

You can capture the variable manually (which is similar to what a lambda capture does behind the scenes):

int main() {
    int y = 100;
    struct { 
        int& y;
        int operator()(int x) { return x + y; }
    } anon = { y };
}

You can then use it like this:

#include <iostream>
...
std::cout << anon(10) << std::endl;

Prints 110 as expected. Unfortunately you can't have the anonymous type inherit from another with this method as initializer-list constructable types can't inherit from another type. If inheritance is crucial then you should use the constructor method outlined by Andy Prowl.

A C++ lambda can capture "outside" variables. [Edit: when I first read the question, I somehow missed where he mentioned that he's aware of lambdas. For better or worse, C++ doesn't have anything else that really resembles an anonymous class].

For example:

#include <iostream>

int main(){ 

    int y = 100;
    auto lambda = [=](int x) { return x + y; };

    std::cout << lambda(2);
}

...prints 102 as its output.

Note that although it looks somewhat like a function, a C++ lambda really results in creating a class. I suppose I should add: that class isn't technically anonymous, but it has some unspecified name that's never directly visible.

Edit: I'm still a bit puzzled about the justification for not using lambdas though. Is the intent to use one class that contains many member functions? If so, it's not clear how you plan to specify which member function to invoke at which time/for which purpose. My immediate reaction is that this sounds suspiciously as if you're trying to twist the language to support a problematic design.

It is not the anonymity of the class that restricts access to the outside variables. In the question, y is not accessible because the class was defined locally within a function.

There are a couple of restrictions for classes defined locally. First, they can only access local variables that are static, but can access any other variable that is available to the scope of the function. Also, local classes can not have static data members.

As for anonymous classes, you can not have constructors nor destructors. All member functions must be declared inside the class definition. It can not have static static members, this includes const static integral members that normally can be instantiated inside of a class definition. Also inheritance is not allowed.

Anonymous classes are an obscure corner of C++ and have little practical value. Lambda functions and other techniques are a lot more flexible. But who knows, perhaps in some situations it could help with code readability.

If your IA class really has just one virtual method that you need to override (and the real complexity is other non-virtual methods) but you don't want to capture the local variables that this method needs, how about this:

int main() {
  int y = 100;
  auto f = [=](int x){return x+y;};
  typedef decltype(f) F;
  struct IB : IA {
    F _f;
    IB(F _f): _f(_f) {}
    int f(int x) { return _f(x); }
  } a(f);
  doFancyWork(&a);
  return 0;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top