C++ equivalence to Java's anonymous class

Viewed 374

I am working on translating some Java code to C++.

In Java, we can create object from anonymous class, using existing constructor, and overriding some methods. E.g.,

class X {
    public X(int value) {...}
    public void work() {....}
} 

void main(String[] args) {
    X entity = new X(5) {
         public void work() { /* Something else */ }
    };
}

In C++, I know I can create anonymous class as following:

class X {
public:
    virtual void work() {...}
}

class : public X {
public:
    void work() {....}
} obj;

But C++ does not allow constructor in anonymous class, and it does not allow extending from object (e.g., the new X(5) { public void work() {} } like what Java allows.

How can I write similar code in C++?

Update 03/07/2020 05:27 CDT

More context about the problem I am working on. I am implementing aggregation function of a in-memory SQL database, and use the following class to represent an aggregation field:

class AggField {
public:
    AggField(int colIndex);
    virtual void reduce(DataRow&) = 0;
    virtual double output() = 0;
}

For each type of aggregation, e.g., avg, min/max and sum, I have a subclass. For example

class Avg : public AggField {
private:
    int counter_;
    double value_;
public:
    Avg(int colIndex) : AggField(colIndex), counter_(0), value_(0) {};
    void reduce(DataRow&) override {
        value_ += row[colIndex].doubleval();
        counter_ += 1;
    }
    double output() override {
        return value_ / counter_;
    }
}

class Sum : public AggField {
    .....
}

When processing a table, I will write the following

Table table = ...
auto agg_opr = Agg({
    new Sum(0),
    new Avg(1)
});
agg_opr.agg(table);

which does a sum on column 0, and average on column 1.

Sometimes(rare) I need to process more than one input columns. For example, doing a sum of col1 * (1 + col2). Instead of creating a new subclass of AggField, I would like to write something similar to:

Table table = ...
auto agg_opr = Agg({
    new Sum(0) {
        void reduce(DataRow& row) {
             value_ += row[0].doubleval() * (1 + row[1].doubleval());
        }
    }, 
    new Avg(1),
    new Max(1)
});
agg_opr.agg(table);
2 Answers

I can't say that I know how to write idiomatic Java but I'm guessing that this pattern in Java is an alternative to lambdas in C++. I remember using an anonymous class long ago when I was working with Swing. I think I did something like this:

button.addMouseListener(new MouseAdapter() {
  public void mouseClicked(MouseEvent e) {
    // ...
  }
});

This is sugar for inheriting from a class and overriding a method. Doing precisely that is not really how I would like to attach an event listener in C++. I'd prefer to do this:

button.addMouseClickListener([](const MouseEvent &e) {
  // ...
});

In the case of an event listener, the closure would need to be stored in a std::function or something similar. This has roughly the same performance as a virtual call.

I don't really know much about where you're using this class but if you need to store it aside (like an event listener or something) then declaring the class the long way or using std::function are probably the cleanest options. If you don't need to store it aside (like a policy for an algorithm) then you could use a functor. Of course, you can store aside a functor but it takes a bit of template machinery and probably isn't worth it (although it does have more flexibility).

struct MyPolicy {
  int doSomething(int i) {
    return i * 3;
  }

  double getSomething() const {
    return d;
  }

  double d;
};

template <typename Policy>
void algorithm(Policy policy) {
  // use policy.doSomething and policy.getSomething...
}

Using a functor or lambda with a template has much better performance than using virtual functions. In the above example, the compiler can and probably will inline the calls to doSomething and getSomething. This isn't possible with virtual functions.

If I knew more about the real problem that you're trying to solve then I might be able to write a more specific and helpful answer.


After seeing the updated question I have another suggestion. That would be to create a subclass for custom aggregate functions. Of course, this has plenty of limitations.

template <typename Func>
class CustomAgg : public AggField {
public:
    CustomAgg(int colIndex, Func func)
      : AggField{colIndex}, func{func} {}

    void reduce(DataRow &row) override {
        func(value, row);
    }

    double output() override {
        return value;
    }

private:
    Func func;
    double value = 0.0;
    // could add a `count` member if you want
};

auto agg_opr = Agg({
    new CustomAgg{0, [](double &value, DataRow &row) {
        value += row[0].doubleval() * (1 + row[1].doubleval());
    }},
    new Avg(1),
    new Max(1)
});

Honestly, I think the best solution for you is to not try to implement a Java feature in C++. I mean, if you need to handle multiple columns in some specific operation then create a class just for that. Don't take any shortcuts. Give it a name even though you might only use it in one place.

C++ has anonymous namespaces, which effectively lets you create classes that are completely isolated to the translation units they're declared in:

namespace {

   class X {

   public:
          X(int) { /* ... */ }     // Ok to have a constructor

          void work();
    };

}

int main(int argc, char **argv)
{
    X entity{5};

    // ...
}

Now, you have to declare them in global scope, you can't declare them in inner scope. You'll also need to give these classes normal names that you can reference them by in the same translation unit; but for all practical purposes they're completely anonymous and inaccessible from other translation units. Another translation unit can declare its own anonymous class "X", and there won't be any conflicts.

You can use anonymous classes in all other normal ways, subclass them, etc... You can create an anonymous class that's a subclass of a regular, non-anonymous class, which gets you pretty close to what Java does, here.

Some compilers also offer extensions where you can declare classes in inner scopes, and they'll also work very similar to anonymous namespaces, but that's going to be a compiler-specific extension.

Related