- 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 аргументы. Которые также шаблонизируются. И если метод не существуют у объекта, то проект не скомпилируется.
Спасибо за отзыв. Очень приятно, что кто-то прочитал и ответил.
Не совсем.
Под наследованием интерфейса понимается вот что