Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to override virtual functions with external friend lambda functions?

Tags:

c++

gcc

clang

c++14

I am writing some game code. I have an abstract game object class, and I want to make many various instances of the class. I want to write them quickly, without writing subclasses for each of them, so I tried to override the virtual functions with external friend lambda functions. Here is some simplified code:

#include <iostream>
#include <memory>

class B {
public:
    virtual ~B() = default;
    virtual void g() = 0;
protected:
    int x_ = 42;
};

template<typename F>
class C : public B {
public:
    C(F f) : f_(f) {}
    void g() override { f_(*this); }
private:
    F f_;
    friend decltype(f_);
};

template<typename F>
std::unique_ptr<B> make_c(F f) {
    return std::make_unique<C<F>>(f);
}

int main() {
    std::unique_ptr<B> c = make_c([](auto& self) {
        std::cout << self.x_ << "\n";
    });
    c->g();

    return 0;
}

I tried this code with Clang and GCC. Clang compiles it, but GCC dies with an internal compiler error:

  • Clang: https://wandbox.org/permlink/Kw82BdsKYEg0i2ln
  • GCC: https://wandbox.org/permlink/F344XvvzO2JJH8Fa

I tried changing the argument type of the lambda from auto& to B&. Then, neither GCC and Clang will compile it, saying "int B::x_ is protected within this context".

Is my code valid? If not, do some alternative methods exist?

Why does Clang compile it while GCC fails?

like image 534
vabuff Avatar asked Mar 08 '18 14:03

vabuff


People also ask

Can you override a virtual function?

You can override virtual functions defined in a base class from the Visual Studio Properties window.

Can you override non virtual functions?

By default, methods are non-virtual, and they cannot be overridden. Virtual modifiers cannot be used with static, abstract, private, and override modifiers.

Can you override without virtual C++?

New! Save questions or answers and organize your favorite content. Learn more.

What is the difference between virtual function and function overriding?

The virtual keyword is used to modify a method, property, indexer, or event declared in the base class and allow it to be overridden in the derived class. The override keyword is used to extend or modify a virtual/abstract method, property, indexer, or event of base class into a derived class.


1 Answers

I do not really see how to get around the limitation of friend just with C++ means. However consider the following:

int main(int argc, char* argv[])
{
    class : public B
    {
    public:
        virtual void g() override { std::cout << x_ << "\n"; }
    } c;
    c.g();

    return 0;
}

Anonymous class with single instance. Sure, not yet what you are after, but it can be the base for some macro magic...

#define MAKE_GENERIC_B(INSTANCE_NAME, IMPLEMENTATION) \
class : public B                                      \
{                                                     \
public:                                               \
    virtual void g() IMPLEMENTATION                   \
} INSTANCE_NAME

int main(int argc, char* argv[])
{
    MAKE_GENERIC_B(c,                             \
            {                                     \
                    char a[12] = { };             \
                    char d[10] = { };             \
                    memcpy(a, d, sizeof(d));      \
                    std::cout << x_ << std::endl; \
            }                                     \
    );
    c.g();
    return 0;
}

Suppose that comes quite close to what you are after. Multi-line implementations as illustrated come with quite ugly line endings, though, but if you have many short implementations that fit into a single line, it might be interesting...

Edit: Using std::unique_ptr:

#define CONCAT_(X, Y) X ## Y
#define CONCAT(X, Y) CONCAT_(X, Y)
#define MAKE_C() CONCAT(C, __LINE__)

#define MAKE_GENERIC_B(INSTANCE_NAME, IMPLEMENTATION) \
class MAKE_C() : public B                             \
{                                                     \
public:                                               \
    virtual void g() IMPLEMENTATION                   \
}; auto INSTANCE_NAME = std::make_unique<MAKE_C()>()

int main(int argc, char* argv[])
{
    MAKE_GENERIC_B(c,                             \
            {                                     \
                    char a[12] = { };             \
                    char d[10] = { };             \
                    memcpy(a, d, sizeof(d));      \
                    std::cout << x_ << std::endl; \
            }                                     \
    );
    c->g();

    MAKE_GENERIC_B(d, { std::cout << x_ * 2 << std::endl; });
    d->g();

    return 0;
}

As we cannot define classes in template arguments, we need to define it in advance. To get individual names, I use the __LINE__ macro, an alternative could be providing a class name as macro parameter.

Additionally, with the now named classes, you could provide a constructor, too, and even extend the macro to feed in parameters to it, if need be...

like image 117
Aconcagua Avatar answered Sep 20 '22 03:09

Aconcagua