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:
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?
You can override virtual functions defined in a base class from the Visual Studio Properties window.
By default, methods are non-virtual, and they cannot be overridden. Virtual modifiers cannot be used with static, abstract, private, and override modifiers.
New! Save questions or answers and organize your favorite content. Learn more.
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.
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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With