- 1. Hello templates
- 1.
- 2. Real code:
Type erasure idiom
The article assumes that the reader has basic knowledge of C++
Straight to the point.
Suppose you need to dynamically (like me, on button click) change the text
several objects such as:
- QLabel
- QPushButton
- QLineEdit
Most of you know that these widgets inherit from QWidget, so why not declare a method that takes a pointer to this base class and then call the setText method?
QWidget * base_pointer = new QLabel(); base_pointer->setText("ERROR");
The problem is that QWidget doesn't define the setText() method, so you can't do that.
My friend advised me 2 options for solving this problem:
- Via the "type erasure" idiom
- Through signals and slots
You already know for sure about signals and slots, and therefore I will talk about 1 method. Briefly.
The type erasure idiom allows you to erase the type of any object by hiding it behind
object of a more general type.
Hello templates
How it looks in words:
- We create an abstract base class A, in it we declare the virtual function SetText
We inherit from this class the already template class B, and it only has:
field in the form of a pointer to a template type
implementation of the virtual method SetText
In the client class, we declare a pointer of type A
- Use :)
How it looks in practice:
- An abstract base class and its successor:
namespace om_animation { class AbstractWritableWidget { public: ~AbstractWritableWidget() {} virtual void SetText(const QString& text) = 0; }; template <class Widget> class WritableWidget : public AbstractWritableWidget { public: WritableWidget(Widget* writable_widget) : writable_widget_(writable_widget) {} ~WritableWidget() {} void SetText(const QString& text) override { writable_widget_->setText(text); } private: Widget* writable_widget_ = nullptr; }; }
- We use (just an example):
class Client { public: template <typename T> void Run(T * type){ // do what you want writable_widget_ = new WritableWidget<T> (type); writable_widget_->SetText("Success"); // do what you want } private: AbstractWritableWidget* writable_widget = nullptr; }
template <typename Widget> void om_animation::TextAnimator::RunAnimation(Widget* widget) { if (WritableMatcher::IsWidgetWritable(widget->metaObject()->className())) { if (writable_widget_) { delete writable_widget_; } writable_widget_ = new WritableWidget<Widget>(widget); timer_->start(animation_delay_); } else { throw std::logic_error( "incompatible type for text animation, add type to " "WritableMatcher class"); } }
In real code, I also made a class with static methods to check which class supports
setText method in Qt library. There's just a vector under the hood. Nothing complicated.
Through the metaobject, we get the name of the class, compare it with the one already in the list. Yes - yes, no - no.
Further I have made simple check of the pointer, and actually already use.
If the object was not found, the client receives an exception.
That's all. I find this idiom very cool.
Thank you for your attention.
Идиома достаточно интересная, единственное, код кажется весьма громоздким.
В стандарте С++14 также есть возможность объявлять в качестве аргумента лямбда функции auto аргументы. Которые также шаблонизируются. И если метод не существуют у объекта, то проект не скомпилируется.
Спасибо за отзыв. Очень приятно, что кто-то прочитал и ответил.
Не совсем.
Под наследованием интерфейса понимается вот что