Extreme type erasure via std::function 通过模板(而不是继承)实现接口
来源:互联网 发布:嵌入式系统编程 pdf 编辑:程序博客网 时间:2024/06/10 00:05
转自: https://a4z.bitbucket.io/blog/2017/01/11/exterm_typererasure-using-std::function.html
本文:
1.介绍了一种通过模板(而不是继承)实现接口的方法,并且可以将不同类型(具有接口指定方法)的对象放入同一个容器。
2.介绍了通过std::function对可调用对象进行类型擦除。
If you have a good title add some content… so I did and you can read the result here ;-)
Some time ago, I think it was 2013, Sean Parent gave a talk where he demonstrated how to use type erasure to avoid implementing interfaces in classes.
The talk was called “Inheritance is the base class of evil” and you can find it online. If you have not already seen it you should watch it.
Summarize type erasure
This summary is maybe to short and handles not all aspects, but that’s OK. I do not what to handle all aspects but rather keep it simple.
A classical way
You have different type of classes, like TextWidget, BoxWedget, ….
You want to have instances of those types in a list
You want to call a method, like for example
draw
, on each of the objects in the list
The common way to implement this is having a IDrawable interface with a draw method and each class needs to implement this interface. Than you have a bunch of 'drawable' objects and have a list of them, for example in a std::vector<IDrawable*>
.
A different way
Sean Parent showed that there is a different way to do this.
After seeing this way my reaction was something of:
What ??? hmmm…. ??? … ahhh …, of course!
Why did I not think about that myself, why did nobody tell me already years ago….
You might know such moments.
- A incomplete and to short summary
It is possible to create a drawable_type that can hold different types of classes.
This type has a draw method and calls the draw method of the class it hold.
The drawable_type creates therefor an interface and hides the concrete type behind that.
This is done with the help of templates.
This sounds complicated, but becomes pretty obvious if you see it in code.
So here a short sample implementation that you can copy and use to play with.
#include <iostream>#include <vector>#include <memory>// 2 classes, class1 and class2,// both have a draw method, but thats all they have in common.// we want to have class1 and class2 in a list,// they do not share a type,struct class1 { void draw() const { std::cout << "class1" << std::endl; }};struct class2 { void draw() const { std::cout << "class2" << std::endl; }};// so we create a drawable typeclass drawable_t { // drawable knows the concept of drawing objects, // using a simple abstract class as you know it struct concept_t { virtual ~concept_t() = default ; virtual void draw() const = 0; }; // A model realize the concept by inheriting it // What it actually calls is intern and private // This works as long as: // 1) the given object is movable // 2) the given object has a draw method template< typename T > struct model_t : concept_t { // constructing the model model_t(T x) : erased_type(std::move(x) ) {} // call draw on whatever it holds void draw() const { erased_type.draw(); } // Whatever it holds member variable // Some user spcified type what is erased now. // We only know it has a draw method, // if not, a a compiler error happens. T erased_type; }; // The drawable type has to hold the model. // No problem, we can do that via the interface. std::unique_ptr<const concept_t> self_;public: // The constructor is a template, // we don't know what type will be used. // It has to work with // every movable type that has a draw method. template< typename T > drawable_t(T x) : self_(new model_t<T> (std::move(x))) {} // the draw call void draw() const { self_->draw(); }};// drawable_t hold model_t<SomeType>// the requirement of some type is to have a draw method.// has a pointer to the abstract class concept_t,// What concept_t pointer points to is a concrete model_t<SomeType>,// some type is hidden// we do not know about it when we write drawable_t.// It is a bit like duck typing.// If we would use quack instead of draw as the required method// and call our drawable_t duck.// We could hide everything that has a quack method inside a duck.// usage of the code above works like thatint main(int argc, char* argv[]){ std::vector<drawable_t> objects; // add whatever movable object that has a draw method objects.emplace_back(class1()); objects.emplace_back(class2()); // draw all the different typs for (const auto& object : objects) object.draw(); return 0;}
You can compile the above code, works also with older compilers like gcc 4.8.x
g++ -std=c++11 sample.cpp
Running the code will, less surprising, look like that
./a.outclass1class2
So far so good, you did possible already know about that, if not, I hope this summary helped you to understand what this type erasure thing is about.
Extreme type erasure with std::function
Now, to the topic, extreme type erasure with std::function.
I call it like that because I had not other one-liner and it sounds like a good headline.
I recently had something like 'sendable' objects but they did not have anything in common.
No same named function implemented, and some of the objects where even functions them self!
But I needed dynamic assembled lists of all these things that send something and call them in order as they are in the list.
To make it as short as possible, the solution I have chosen looks something like that:
std::vector<std::function<void()>> senderlist ;
And then filling senderlist
it with lambdas that capture what ever they do and need.
And that’s it?
Yes, of course!
This is super short, super flexible and super simple, isn’t it?
Via std::function I can put anything in the call list, no matter if it makes quack, muhh or määä.
Of course the signature of the function object in the vector might vary, also what type of callable objects are in there and what they capture. And in reality it does.
But no need to keep this post not as simple as possible.
I think we all got the point that this is just an other of those "of course you can do it like that" things.
But it took me a moment to realize that this is also type erasure, just more extreme, and therefore the title of this post.
The example
And just to see that what I am talking about also compiles, here a short sample:
#include <iostream>#include <vector>#include <functional>void sendto(int dest, int data) { std::cout << "sendto" << dest << ": " << data << std::endl ;}struct Foo{ void send_1(int data) { std::cout << "send foo 1: " << data << std::endl; } void send_2(int data){ std::cout << "send foo 2: " << data << std::endl; }};struct Bar{ int data ; void send() { std::cout << "send bar " << data <<std::endl; }};int main(int argc, char* argv[]){ std::vector<std::function<void()>> sender; int data = 12 ; sender.emplace_back([data]{sendto(0,data);}) ; Foo f; data = 34; sender.emplace_back([&f, data]() { f.send_1(data) ; }) ; data = 56; sender.emplace_back([&f, data]() { f.send_2(data) ; }) ; Bar b{78}; sender.emplace_back([&b]() { b.send() ; }) ; for (auto& send : sender) send(); return 0;}// compile with g++ std=c++11 ..// running will print// sendto0: 12// send foo 1: 34// send foo 2: 56// send bar 78
That’s it, extreme type erasure via std::function. :-)
- Extreme type erasure via std::function 通过模板(而不是继承)实现接口
- Type Erasure
- 1)钟爱组合而不是继承,2)钟爱接口而不是实现
- std::tr1::function模板类 std::tr1::bind()模板函数
- std::tr1::function模板类 std::tr1::bind()模板函数
- Lightweight type erasure matching
- Effective C# 原则19:选择定义和实现接口,而不是继承
- java多线程选择实现Runnable接口而不是直接继承Thread类的原因
- 通过继承Thread实现多继承和通过实现Runnable接口实现多线程的比较
- 通过继承Thread实现多继承和通过实现Runnable接口实现多线程的比较
- 桥接模式(把接口和实现分为两个继承树,而不是将实现来继承接口,造成实现和接口耦合
- 关于Java的Type Erasure
- Pattern matching around type erasure
- 使用std::function和std::bind实现局部函数做回调
- C++ 使用std::function 和std::bin实现委托
- STD::FUNCTION
- std::function
- std::function
- Eclipse下使用Android Design Support Library中的控件(比如TabLayout)
- 【转载】相似度测度
- python+robotframework --第一个UI自动化脚本
- 连接管理工具httpclient的简单使用
- JS变量基本类型和引用类型的区别
- Extreme type erasure via std::function 通过模板(而不是继承)实现接口
- iOS手把手教会自定义刷新控件
- java环境搭建及配置
- 几点建议,让Redis在你的系统中发挥更大作用
- 《Android高级进阶》小密圈开通啦
- Kth largest numbers ii
- 【cocos2d-x】CCEAGLView背景为黑色,遮挡ios中的UIView的解决办法
- Scala匿名类的函数是private还是public?
- GLFW