Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I create an anonymous on-the-fly class (implementation of an interface) in C++

In C++, can I create an implementation of an interface on the fly (which ideally binds local-scope variables.) Not sure how to explain it better, so I will put down what I would like the code to look like (roughly):

// Given the following:

class Visitor
{
    virtual void visit(const Data& data) = 0;
}

class DataStore
{
    void visitData(Visitor& visitor) { /** Invokes visitor with each item of data. */ }
}

// Imagine one would write something like:

void inSomeFunction(DataStore& store)
{
    std::string myName = "bob";

    class MyVisitor: public Visitor
    {
    public:
        MyVisitor(const std::string& p): person(p);
        virtual void visit(const Data& data) override
        {
            std::cout << person << " is visiting " << data << std::endl;
        }
    private:
        std::string person;
    }

    MyVisitor myVisitor(myName);
    store.visitData(myVisitor);
}

// Instead of the above, I want to write instead something like:

void inSomeFunction(DataStore& store)
{
    std::string myName = "bob";

    store.visitData(
        class: public MyVisitor
        {
            virtual void visit(const Data& data) override
            {
                std::cout << myName << " is visiting " << data << std::endl;
            }
        }
    );
}

I realise that I'm asking 2 questions here - can you create an anonymouse class like this, and can you bind variables from the local scope (like I've referred to myName). But I need both for it to be useful.

If the above can't be done, what's the closest one can get to using say C++11 lambdas or similar, in terms of conciseness / lack of boilerplate.

like image 334
nappyfalcon Avatar asked May 24 '16 16:05

nappyfalcon


2 Answers

If you have to have an interface, then you should do what Kerrek suggested.

But even better would be to change your interface from:

class DataStore
{
    void visitData(Visitor& visitor) { 
        // bunch of calls to visitor.visit(...) ... 
    }
}

to:

    template <class Visitor>
    void visitData(Visitor visitor) { 
        // bunch of calls to visitor(...) ... 
    }

This would allow you to simply write:

std::string myName = "bob";
store.visit([myName](const Data& data) {
    std::cout << myName << " is visiting " << data << std::endl;
});

which is far more natural in my opinion. If the interface of DataStore is outside of your control, then this answer is totally moot.

like image 161
Barry Avatar answered Oct 21 '22 23:10

Barry


You can do it with one more level of indirection:

#include <functional>
#include <utility>

class AdHocVisitor : public Visitor
{
public:
    explicit AdHocVisitor(std::function<void(const Data&)> f) : f_(std::move(f)) {}
    void visit(const Data& data) override { f_(data); }
private:
    std::function<void(const Data&)> f_;
};

Usage:

AdHocVisitor v([a, &b, this](const Data&) {});
store.visitData(v);
like image 45
Kerrek SB Avatar answered Oct 22 '22 01:10

Kerrek SB