关于c++17:记录一次VS-CODE调试过程中include头文件报错的解决办法

一、背景在调试C++程序的过程中,#include <bits/stdc++.h>报错说无奈援用。这让我十分好奇,为什么间接引入<iostream>能够,然而<bits/stdc++.h>不行呢? 二、调试过程1、看看为什么能间接引入iostream右键头文件中的<iostream>,点击Go to Definition关上iostream能够看到iostream在我的电脑里被放在了/Library/Developer/CommandLineTools/usr/include/c++/v1/iostream下。也能够右键iostream,抉择在文件夹中显示: 咱们能够看到在这个门路下,除了iostream,还有好多其余头文件的定义,vs code头文件引入就是在这个门路下查找的。 2、新建bits/stdc++.h那么我猜测,是不是在这个门路下新建一个bits/stdc++.h,程序就不会报错了呢? 在/Library/Developer/CommandLineTools/usr/include/c++/v1/目录下新建一个bits文件夹,而后在此文件夹下新建一个stdc++.h从github上找到bits/stdc++.h的内容:https://gist.github.com/Einst...,复制粘贴到外面。 而后从新回到要调试的程序中,debug c/c++,发现程序能够运行,撒花!!! 三、总结遇到头文件不存在时,先查看编译器/IDE是从哪个目录援用的头文件,而后在此目录下补充缺失的头文件即可。

October 16, 2022 · 1 min · jiezi

关于c++17:Win10用VS-Code编写C程序

一、装置C/C++编译器macOS和支流Linux零碎都自带C/C++编译器,Windows零碎须要通过装置mingw来取得C/C++编译器(1)mingw下载地址:https://sourceforge.net/proje...(2)装置完mingw后,将装置目录增加到用户变量和零碎变量中的Path后。bin目录下的文件列表如图: 二、验证C/C++开发环境关上命令行,输出gcc --verion和g++ --version。如果呈现以下输入证实开发环境装置实现: 三、在VS Code中装置C++相干插件关上VS Code,装置以下插件:C/C++插件,由微软官网开发保护,提供了丰盛的C和C++的开发反对Code Runner插件:一键运行代码,反对40多种编程语言CodeLLDB插件:一款用于调试C++的原生插件因为网络问题,CodeLLDB可能无奈在线装置,能够下载CodeLLDB对应的vsix文件进行离线装置 四、进入调试界面增加配置环境(1)写代码的文件夹下创立了一个.vscode文件夹,.vscode有三个文件,内容如下:(2)首次调试时,需增加配置环境,抉择C++(GDB/LLDB),会主动生成launch.json配置文件(3)编辑launch.json { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "g++.exe 生成和调试流动文件", "type": "cppdbg", "request": "launch", "program": "${fileDirname}\\${fileBasenameNoExtension}.exe", "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": true, "MIMode": "gdb", "miDebuggerPath": "D:\\myMinGW\\minGW\\bin\\gdb.exe", // minGW的装置地址 "setupCommands": [ { "description": "为 gdb 启用参差打印", "text": "-enable-pretty-printing", "ignoreFailures": true } ], "preLaunchTask": "task g++" //批改项 } ]}(4)返回cpp文件,按F5进行调试,会弹出:找不到工作“task g++”;这是抉择配置工作,会主动生成tasks.json。编辑tasks.json文件 ...

December 27, 2021 · 1 min · jiezi

关于c++17:浅谈如何实现自定义的-iterator-之二

实现你本人的迭代器 II实现一个树结构容器,而后为其实现 STL 格调的迭代器实例。 本文是为了给上一篇文章 浅谈如何实现自定义的 iterator 提供补充案例。 tree_t 的实现我打算实现一个简略而又不简略的树容器,让它成为规范的文件目录结构式的容器类型。但简略就在于,我只筹备实现最最必要的几个树结构的接口,诸如遍历啦什么的。 这是一个很规范的文件目录的仿真品,致力于齐全仿照文件夹的体现。它和什么 binary tree,AVL,又或是红黑树什么的齐全是驴唇不对马嘴。 首先能够确定的是 tree_t 依赖于 generic_node_t,tree_t 本身并不真的负责树的算法,它只是持有一个 root node 指针。所有与树操作相干的内容都在 generic_node_t 中。 tree_t因而上面首先给出 tree_t 的具体实现: namespace dp::tree{ template<typename Data, typename Node = detail::generic_node_t<Data>> class tree_t : detail::generic_tree_ops<Node> { public: using Self = tree_t<Data, Node>; using BaseT = detail::generic_tree_ops<Node>; using NodeT = Node; using NodePtr = Node *; using iterator = typename Node::iterator; using const_iterator = typename Node::const_iterator; using reverse_iterator = typename Node::reverse_iterator; using const_reverse_iterator = typename Node::const_reverse_iterator; using difference_type = std::ptrdiff_t; using value_type = typename iterator::value_type; using pointer = typename iterator::pointer; using reference = typename iterator::reference; using const_pointer = typename iterator::const_pointer; using const_reference = typename iterator::const_reference; ~tree_t() { clear(); } void clear() override { if (_root) delete _root; BaseT::clear(); } void insert(Data const &data) { if (!_root) { _root = new NodeT{data}; return; } _root->insert(data); } void insert(Data &&data) { if (!_root) { _root = new NodeT{data}; return; } _root->insert(std::move(data)); } template<typename... Args> void emplace(Args &&...args) { if (!_root) { _root = new NodeT{std::forward<Args>(args)...}; return; } _root->emplace(std::forward<Args>(args)...); } Node const &root() const { return *_root; } Node &root() { return *_root; } iterator begin() { return _root->begin(); } iterator end() { return _root->end(); } const_iterator begin() const { return _root->begin(); } const_iterator end() const { return _root->end(); } reverse_iterator rbegin() { return _root->rbegin(); } reverse_iterator rend() { return _root->rend(); } const_reverse_iterator rbegin() const { return _root->rbegin(); } const_reverse_iterator rend() const { return _root->rend(); } private: NodePtr _root{nullptr}; }; // class tree_t} // namespace dp::tree其中的必要的接口基本上都转向到 _root 中了。 ...

October 31, 2021 · 5 min · jiezi

关于c++17:浅谈如何实现自定义的-iterator

实现你本人的迭代器应用 std::iterator在 C++17 之前,实现自定义的迭代器被举荐采纳从 std::iterator 派生的形式。 std::iterator 的根本定义Std::iterator 具备这样的定义: template< class Category, class T, class Distance = std::ptrdiff_t, class Pointer = T*, class Reference = T&> struct iterator;其中,T 是你的容器类类型,无需多提。而 Category 是必须首先指定的所谓的 迭代器标签,参考 这里 。Category 次要能够是: input_iterator_tag:输出迭代器output_iterator_tag:输入迭代器forward_iterator_tag:前向迭代器bidirectional_iterator_tag:双向迭代器random_access_iterator_tag:随机拜访迭代器contiguous_iterator_tag:间断迭代器这些标签看起来仿佛相当莫名其妙,好像我晓得它们的用意,但实际上却又难以明确,难以筛选。 迭代器标签上面粗略地对它们及其关联实体进行个性上的介绍,以帮忙你了解。 这些 tags 实际上绑定关联着一些同名实体类如 input_iterator 等等,通过模板特化技术别离实现专有的 distance() 和 advance() ,以达到特定的迭代优化成果。 input_iterator_taginput_iterator_tag 能够包装函数的输入——以用作它人的输出流。所以它是仅可递增的(只能 +1),你不能对它 +n,只能通过循环 n 次递增来模仿相应的成果。input_iterator 无奈递加(-1),因为输出流没有这样的个性。它的迭代器值(*it)是只读的,你不能对其置值。 但 output_iterator_tag,forward_iterator_tag 的迭代器值是可读写的。可读写的迭代器值是指: std::list<int> l{1,2,3};auto it = l.begin();++it;(*it) = 5; // <- set value back into the container pointed by iteratorinput_iterator 将容器出现为一个输出流,你能够通过 input_iterator 接管输出数据流。 ...

October 28, 2021 · 5 min · jiezi

关于c++17:谈-C17-里的-Chain-of-Responsibility-模式

责任链模式:介绍相干概念并模仿实现一个音讯散发零碎。Responsibility Chain Pattern对于本系列文章这次的 谈XX模式 系列,并不会一一全副介绍 GoF 的 23 个模式,也不限于 GoF。有的模式可能是没有模板化复用的必要性的,另外有的模式却并不蕴含在 GoF 中,所以有时候会有注释的补充版本,像上次的 谈 C++17 里的 Observer 模式 - 4 - 信号槽模式 就是如此。 因为本系列的重心在于模板化实作下面,以工程实作为指标,所以咱们并不会像个别的设计模式文章那样规规矩矩地介绍动机、场景什么的(有时候又未必),而是会以咱们的教训和对模式的了解,用本人的话来做论述,我感觉它可能会有点用,当然快消的世界这样做是很愚昧。 这对于咱们来讲,对集体来讲,也是一个扫视和再思考的过程。而对于你来说,换个角度看看别人的了解,说不定其实是有用途的。 形容责任链模式也是一种行为模式(Behavior Patterns)。它的外围概念在于音讯或者申请沿着一条观察者链条被传递,每个观察者都能够解决申请、或者略过申请,又或者通过信号终止音讯持续向后传递。 音讯散发零碎是它的典型使用场景。 除此之外,在用户身份鉴权与角色赋予环节也是利用责任链的好场景。 Responsibility Chain 和观察者模式的区别在于前者的观察者是顺次解决同一事件且有可能被中断的,观察者们具备一个轮次关系,而后者的观察者们具备普遍意义上的平等性。实作咱们会建设一个音讯散发零碎的可复用模板,借助于这个 message_chain_t 能够很容易地建设一套音讯散发机制起来。其特点在于 message_chain_t 负责散发音讯事件,接收者 receivers 会收到所有事件。 所以每个接收者应该判断消息来源以及音讯类别来决定本人是否应该解决一个音讯。如果接收者生产了某个事件,那么应该返回一个生产后果实体,这个实体由你的音讯协定来决定,能够是一个简略的 bool,或者一个状态码,也能够是一个处理结果包(struct result)。一个无效的后果实体会令 message_chain_t 完结音讯散发行为。如果返回空(std::optional<R>{}),则 mediator_t 会持续散发音讯给其它全副接收者。和信号槽、observer 模式等的不同之处在于,message_chain_t 是一个 message bumper,而不是公布订阅零碎,它是泛泛播送的。 message_chain_tmessage_chain_t 是一个能够指定音讯参数包 Messages 以及音讯处理结果 R 的模板。音讯处理结果 R 由 std::optional 打包,所以 mediator_t 依据 std::optional<R>::has_value() 来决定是否持续音讯散发循环。 namespace dp::resp_chain { template<typename R, typename... Messages> class message_chain_t { public: using Self = message_chain_t<R, Messages...>; using SenderT = sender_t<R, Messages...>; using ReceiverT = receiver_t<R, Messages...>; using ReceiverSP = std::shared_ptr<ReceiverT>; using Receivers = std::vector<ReceiverSP>; void add_receiver(ReceiverSP &&o) { _coll.emplace_back(o); } template<class T, class... Args> void add_receiver(Args &&...args) { _coll.emplace_back(std::make_shared<T>(args...)); } std::optional<R> send(SenderT *sender, Messages &&...msgs) { std::optional<R> ret; for (auto &c : _coll) { ret = c->recv(sender, std::forward<Messages>(msgs)...); if (!ret.has_value()) break; } return ret; } protected: Receivers _coll; };}如果接收者成千上万,那么音讯散发循环将会是一个性能瓶颈点。 ...

October 22, 2021 · 3 min · jiezi

关于c++17:谈-C17-里的-Command-模式

命令模式:介绍相干概念。实作参考上回的 MementoCommand Pattern对于本系列文章这次的 谈XX模式 系列,并不会一一全副介绍 GoF 的 23 个模式,也不限于 GoF。有的模式可能是没有模板化复用的必要性的,另外有的模式却并不蕴含在 GoF 中,所以有时候会有注释的补充版本,像上次的 谈 C++17 里的 Observer 模式 - 4 - 信号槽模式 就是如此。 因为本系列的重心在于模板化实作下面,以工程实作为指标,所以咱们并不会像个别的设计模式文章那样规规矩矩地介绍动机、场景什么的(有时候又未必),而是会以咱们的教训和对模式的了解,用本人的话来做论述,我感觉它可能会有点用,当然快消的世界这样做是很愚昧。 这对于咱们来讲,对集体来讲,也是一个扫视和再思考的过程。而对于你来说,换个角度看看别人的了解,说不定其实是有用途的。 对于命令模式在 谈 C++17 里的 Memento 模式 一文中我提到过备忘录模式和命令模式往往是联动协同工作的,并且在给出的传统实现以及 Undo Manager 实现(类库 undo-cxx)中居蕴含了命令模式局部。 所以本文算是凑数的意思。 动机命令模式是一种行为模式。这种设计模式把多种多样的动作形象为命令,Client 通过执行器 Caller/Invoker/Executor 执行这些命令而不用关怀调用的细节。一个具体的命令对象 ConcreteCommand 负责解释命令执行动作的全副细节,包含命令的接收者。接收者 Receiver 是命令执行的承受者,例如在字处理器中,接收者是以后编辑器的以后抉择文字,而字体款式命令会对该接收者做出款式设定。 这段形容的 UML 图是这样的: FROM: here: svg file另一张图很漂亮,摘取在这里供对照: FROM: The Command Pattern - fjp.github.io场景在一个餐厅中,顾客点餐后,点餐的动作能够被视为 Client 在告诉 Executor 该要执行命令了。命令被执行的上下文蕴含了顾客点餐的菜单(Receipt)。菜单被送到后厨,被指派给失当的厨师烹饪,这相当于命令被具体执行。 在一个音乐播放器中,Play,Pause,Stop,Forward,Rewind 是相应的命令,Invoker 执行命令时,导致接收者 Receriver 即录音机被管制。 在一个字处理器中,以后编辑器的以后抉择文字通常被视作接收者,Bold,Italic 等用户的 UI 操作会触发到相应命令的执行。 ...

October 20, 2021 · 2 min · jiezi

关于c++17:谈-C17-里的-Factory-模式之二

前言回顾上回回忆上回为了写一个 memento 模式(请看 谈 C++17 里的 Memento 模式),感觉仅仅 memento 太干瘪了,罗唆就写了个类库 undo-cxx,也真是没谁了。 这两日想来想去,愈发感觉这事干得太那啥了。今后还是不用如此的吧?话说这两天头发都不长长了,担心啊。 本文缘起在 谈 C++17 里的 Factory 模式 中我介绍了 hicc/cmdr-cxx 中的 factory 模板类,看了一下时间表,动念是 0822,竟然这么长时间了(而且都三个月了,写个 GoF 系列居然也没写进去,我不应该这么懒的)。过后提到 factory 的存在的 T data 问题,即在 factory 的 tuple 中持有每个 products 类的一个具体化实例,起因是为了稍后可能从 T data 中抽出类型供 create 应用。 这显然是一个不难受的货色。 然而过后不想纠缠了,问题就这么遗留下来了,直到起初某一天感到了不能忍,才去钻研了怎么毁灭这玩意,事实上它的确是能够被毁灭的。 factory<> 改进版所以当初改良的版本是: namespace cmdr::util::factory { /** * @brief a factory template class * @tparam product_base such as `Shape` * @tparam products such as `Rect`, `Ellipse`, ... */ template<typename product_base, typename... products> class factory final { public: CLAZZ_NON_COPYABLE(factory); using string = id_type; template<typename T> struct clz_name_t { string id = id_name<T>(); using type = T; using base_type = product_base; static void static_check() { static_assert(std::is_base_of<product_base, T>::value, "all products must inherit from product_base"); } template<typename... Args> std::unique_ptr<base_type> gen(Args &&...args) const { return std::make_unique<T>(args...); } // T data; }; using named_products = std::tuple<clz_name_t<products>...>; template<typename... Args> static auto create(string const &id, Args &&...args) { std::unique_ptr<product_base> result{}; std::apply([](auto &&...it) { ((it.static_check() /*static_check<decltype(it.data)>()*/), ...); }, named_products{}); std::apply([&](auto &&...it) { ((it.id == id ? result = it.gen(args...) : result), ...); }, named_products{}); return result; } template<typename... Args> static std::shared_ptr<product_base> make_shared(string const &id, Args &&...args) { std::shared_ptr<product_base> ptr = create(id, args...); return ptr; } template<typename... Args> static std::unique_ptr<product_base> make_unique(string const &id, Args &&...args) { return create(id, args...); } template<typename... Args> static product_base *create_nacked_ptr(string const &id, Args &&...args) { return create(id, args...).release(); } private: }; // class factory} // namespace cmdr::util::factory在这个改进版中,咱们通过在 clz_name_t 中定义一个 generator 函数的形式来结构 T 的最终实例,而不用借助于 decltype(T data) 这样的运算来取得 T 类型,所以可能顺利地打消 T data。 ...

October 19, 2021 · 4 min · jiezi

关于c++17:谈-C17-里的-Memento-模式

备忘录模式:介绍相干概念并实现一个较全面的 Undo Manager 类库。Memento Pattern动机备忘录模式也是一种行为设计模式。它在 Ctrl-Z 或者说 Undo/Redo 场合中时最为重要,这里也是它的最佳利用场合。除此之外,有时候咱们也能够称之为存档模式,你能够将其泛化到所有备份、存档、快照的场景里,例如 macOS 的 Time Machine。 Memento 之所以能成为一种 Pattern,就在于它曾经将上述场景进行了形象和覆盖。在这里探讨备忘录模式时肯定须要留神到它作为一种设计模式所提供的最弱小的能力:不是可能 Undo/Redo,而是可能覆盖细节。 当然要以文字编辑器的 Undo/Redo 场景为例来阐明这一点: Memento 模式会覆盖编辑器编辑命令的实现细节,例如编辑地位、键击事件、批改的文字内容等等,仅仅只是将它们打包为一条编辑记录总体地提供给内部。内部使用者无需理解所谓的实现细节,它只须要收回 Undo 指令,就能从编辑历史中抽出并回退一条编辑记录,从而实现 Undo 动作。 这就是现实中的 Memento 模式应该要达到的成果。 轻量的古典定义下面提到的字处理器设计是较为饱满的案例。实际上少数古典的如 GoF 的 Memento 模式的定义是比拟轻量级的,它们通常波及到三个对象: originator : 创始人通常是指领有状态快照的对象,状态快照由创始人负责进行创立以便于未来从备忘录中复原。memento : 备忘录贮存状态快照,一般来说这是个 POJO 对象。caretaker : 负责人对象负责追踪多个 memento 对象。它的关系图是这样的: FROM: Here一个略有调整的 C++ 实现是这样的: namespace dp { namespace undo { namespace basic { template<typename State> class memento_t { public: ~memento_t() = default; void push(State &&s) { _saved_states.emplace_back(s); dbg_print(" . save memento state : %s", undo_cxx::to_string(s).c_str()); } std::optional<State> pop() { std::optional<State> ret; if (_saved_states.empty()) { return ret; } ret.emplace(_saved_states.back()); _saved_states.pop_back(); dbg_print(" . restore memento state : %s", undo_cxx::to_string(*ret).c_str()); return ret; } auto size() const { return _saved_states.size(); } bool empty() const { return _saved_states.empty(); } bool can_pop() const { return !empty(); } private: std::list<State> _saved_states; }; template<typename State, typename Memento = memento_t<State>> class originator_t { public: originator_t() = default; ~originator_t() = default; void set(State &&s) { _state = std::move(s); dbg_print("originator_t: set state (%s)", undo_cxx::to_string(_state).c_str()); } void save_to_memento() { dbg_print("originator_t: save state (%s) to memento", undo_cxx::to_string(_state).c_str()); _history.push(std::move(_state)); } void restore_from_memento() { _state = *_history.pop(); dbg_print("originator_t: restore state (%s) from memento", undo_cxx::to_string(_state).c_str()); } private: State _state; Memento _history; }; template<typename State> class caretaker { public: caretaker() = default; ~caretaker() = default; void run() { originator_t<State> o; o.set("state1"); o.set("state2"); o.save_to_memento(); o.set("state3"); o.save_to_memento(); o.set("state4"); o.restore_from_memento(); } };}}} // namespace dp::undo::basicvoid test_undo_basic() { using namespace dp::undo::basic; caretaker<std::string> c; c.run();}int main() { test_undo_basic(); return 0;}这个实现代码中对于负责人局部的职责进行了简化,将相当多的工作交给其他人去实现,目标是在于让使用者的编码可能更简略。使用者只须要像 caretaker 那样去操作 originator_t<State> 就可能实现 memento 模式的使用。 ...

October 17, 2021 · 6 min · jiezi

关于c++17:谈-C17-里的-Strategy-模式

策略模式Strategy Pattern在地图上对两点进行路线布局就是一种典型的策略模式利用场景。当咱们进行终点到起点的路线布局时,咱们期待地图给出这些形式的最佳路线:步行。公交,驾车。有时候可能细分为公交(轨道交通优先),公交(换乘优先)等若干策略。 规范实作依照咱们的结构习惯,上面是门路布局的一个框架性代码 namespace hicc::dp::strategy::basic { struct guide {}; struct context {}; class router { public: virtual ~router() {} virtual guide make_guide(context &ctx) = 0; }; template<typename T> class router_t : public router { public: virtual ~router_t() {} }; class by_walk : public router_t<by_walk> { public: virtual ~by_walk() {} guide make_guide(context &ctx) override { guide g; UNUSED(ctx); return g; } }; class by_transit : public router_t<by_transit> { public: virtual ~by_transit() {} guide make_guide(context &ctx) override { guide g; UNUSED(ctx); return g; } }; class by_drive : public router_t<by_drive> { public: virtual ~by_drive() {} guide make_guide(context &ctx) override { guide g; UNUSED(ctx); return g; } }; class route_guide { public: void guide_it(router &strategy) { context ctx; guide g = strategy.make_guide(ctx); print(g); } protected: void print(guide &g) { UNUSED(g); } };} // namespace hicc::dp::strategy::basicvoid test_strategy_basic() { using namespace hicc::dp::strategy::basic; route_guide rg; by_walk s; rg.guide_it(s);}除了下面的测试代码局部那样的写法之外,咱们还能够征引工厂模式来创立所有 router 的实例,并且枚举全副 routers 来一次性地失去所有门路布局。这种遍历的形式也是工程上实在会采纳的计划,例如地图软件中总是这么治理所有的可能的路由器的。 ...

October 3, 2021 · 2 min · jiezi

关于c++17:谈-C17-里的-State-模式之二

这是第二局部,无关无限状态机(FSM)的 C++ 实作局部,也等同于状态模式实现Prologue上一篇 谈 C++17 里的 State 模式之一 对于状态模式所牵扯到的基本概念做了一个综述性的梳理。所以是时候从这些概念中抽取咱们感兴趣的局部予以实作了。 C++ 实现(元编程实现)如果不采纳 DFA 实践推动的伎俩,而是在 C++11/17 的语境里思考实现状态模式,那么咱们应该从新梳理一下实践: 状态机 FSM:状态机总是无限的(咱们不可能去解决有限的状态汇合)。开始状态 S:Start State/Initial State以后状态 C:Current State下一状态 N:Next State终止状态:Terminated State (Optional)进入状态时的动作:enter-action来到状态时的动作:exit-action输出动作/输出流:input action,也能够是输出条件、或者事件对象等转换:Transition上下文:Context负载:Payload有的时候,Input Action 也被称作 Transition Condition/Guard。它的外延始终如一,是指在进入下一状态前通过条件进行断定状态变迁是否被许可。 状态机外围定义依据以上的设定,咱们决定了 fsm machine 的根底定义如下: namespace fsm_cxx { AWESOME_MAKE_ENUM(Reason, Unknown, FailureGuard, StateNotFound) template<typename S, typename EventT = event_t, typename MutexT = void, // or std::mutex typename PayloadT = payload_t, typename StateT = state_t<S>, typename ContextT = context_t<StateT, EventT, MutexT, PayloadT>, typename ActionT = action_t<S, EventT, MutexT, PayloadT, StateT, ContextT>, typename CharT = char, typename InT = std::basic_istream<CharT>> class machine_t final { public: machine_t() {} ~machine_t() {} machine_t(machine_t const &) = default; machine_t &operator=(machine_t &) = delete; using Event = EventT; using State = StateT; using Context = ContextT; using Payload = PayloadT; using Action = ActionT; using Actions = detail::actions_t<S, EventT, MutexT, PayloadT, StateT, ContextT, ActionT>; using Transition = transition_t<S, EventT, MutexT, PayloadT, StateT, ContextT, ActionT>; using TransitionTable = std::unordered_map<StateT, Transition>; using OnAction = std::function<void(StateT const &, std::string const &, StateT const &, typename Transition::Second const &, Payload const &)>; using OnErrorAction = std::function<void(Reason reason, State const &, Context &, Event const &, Payload const &)>; using StateActions = std::unordered_map<StateT, Actions>; using lock_guard_t = util::cool::lock_guard<MutexT>; // ... };}这是重复迭代之后的成绩。 ...

October 1, 2021 · 9 min · jiezi

关于c++17:谈-C17-里的-State-模式之一

回顾状态模式,思考实作它的各种问题——特地是无关如何实现一个状态机的问题。同时,这一篇呢,可能不得不分几篇,因为写的时候脑壳在发散嘛,于是就关联失去的、能想起来的都提了一嘴,就多了。然而最初还是会给出代码的,我喜爱写代码的。 注: 文中不会做固定式实践介绍(像通常的 Design Patterns 书籍那样的固定版式),你须要相干背景常识的初步理解;有深刻理解更好,有助于互相印证。 然而你也能够全无理解,藉此入门后再进入教科书。为此我也致力束缚术语的准确应用,尽力不带来误会。 本文应该是无趣的,我只是在整顿。 Prologue状态模式,是指一个对象 A 领有多种不同的状态,当特定条件满足时,对象 A 由一种状态切换到另一种,且在不同状态时(可能)具备不同的外在体现。据此,对象 A 将总是有一个开始状态 S,当状态正在产生转换时,咱们说前一状态为以后状态 C,而将要转换到的状态为下一状态 N。而当状态转换实现时,以后状态也就切换到了下一状态。 如果有必要,状态模式可能存在着一个终止状态 T。对于一段输出序列来说,如果输出完毕,可能会触发到迁徙到终止状态;或者特定的标记(Token)会做出这样的触发,例如 Pascal 语言翻译机在遇到 END. 标记时就完结一个编译单元的编译解决。 从狭义上讲,状态模式和状态机、编译器有着亲密乃至于等价的关系。你能够把状态模式视为简版的编译器,这算是一种约定俗成的习语。而少数语境里,大家对状态模式和状态机不作辨别,但有的时候,如果咱们的探讨语境较为学术性时,那么多半会采纳状态机这一术语。 2021-09-07 23:35State Pattern实践状态模式是一种结构型的设计模式,它往往和状态机、无限状态机、自动机、编译原理、UML 状态图等近似或关联性概念一起被混用以及同时提起。 一般来说,咱们认为状态模式就是状态机的一种实现范式。因而本文中会将状态模式与如何实现一个状态机等同起来。 提到状态机,它首先示意的是一个对象领有一系列状态,并且可能在状态之间互相状态,而当产生转换时则会触发特定的动作 Action(分为进入动作 Entry Action 和退出动作 Exit Action,以及输出动作 Input Action,有时候也包含转移动作 Transition Action),并体现出不同的外在形象。 分类状态机/自动机(Automaton)能够被分为多种类别: 接受器/识别器/序列检测器,Acceptors - 例如 gcc 这样的编译器承受一个正规文法序列,正确实现编译的序列则被认为是可承受的。变换器,Transducers - 重点在于给定输出能够引发动作,生成输入。这一类状态机还能够细分为两个小类:Moore Machine 和 Mealy Machine。除了上述的次要划分之外,还有其它分类形式。 例如分类器 Classfiers 和序列(时序/音序等)测定器/生成器 Sequencers。将多个接受器依照确定性与否,则可划分为 DFA 与 NFA 等。在确定型自动机(DFA)中,每个状态对每个可能输出只有准确的一个转移。在非确定型自动机(NFA)中,给定状态对给定可能输出能够没有或有多于一个转移。 等等。 Moore MachineMoore Machine 的特点是只应用进入动作,就是说输入只依赖于以后状态,与输出信号(如果有)无关。 例如电梯门的运行到站和超时时开闭的状态图: 图示中,从状态 Opened 只能迁徙到 Closed,当进入到 Opened 时其进入动作 open door 会导致电机启动并开门,而从状态 Closed 也只能迁徙到 Opened,其进入动作 close door 导致电机反向启动并关门。 ...

September 30, 2021 · 2 min · jiezi

关于c++17:谈-C17-里的-Observer-模式-4-信号槽模式

上上上回的 谈 C++17 里的 Observer 模式 介绍了该模式的根本结构。起初在 谈 C++17 里的 Observer 模式 - 补/2 外面提供了改良版本,次要聚焦于针对多线程环境的暴力应用的场景。再起初又有一篇 谈 C++17 里的 Observer 模式 - 再补/3,谈的是间接绑定 lambda 作为观察者的计划。Observer Pattern - Part IV所以嘛,我感觉这个第四篇,无论如何也要复刻一份 Qt 的 Slot 信号槽模型的独立实现了吧。而且这次复刻做完之后,观察者模式必须告一段落了,毕竟我在这个 Pattern 上真还是费了老大的神了,该完结了。 要不要做 Rx 轻量版呢?这是个问题。原始参考说起 Qt 的信号槽模式,能够算是鼎鼎大名了。它强就强在可能忽视函数签名,想怎么绑定就怎么绑定(也不是全然随便,但也很能够了),从 sender 到 receiver 的 UI 事件推送和触发显得比拟清晰洁净,而且不用受制于确定性的函数签名。 确定性的函数签名嘛,Microsoft 的 MFC 乃至于 ATL、WTL 都爱在 UI 音讯泵局部采纳,它们还应用宏来解决绑定问题,着实是充斥了落后的气味。 要说在当年,MFC 也要是当红炸子鸡了,Qt 只能悄悄地龟缩于一隅,哪怕 Qt 有很多好设计。那又怎么样呢?咱们家 MFC 的优良设计,尤其是 ATL/WTL 的优良设计也多的是。所以这又是一个技术、市场认可的古老历史。 好的,轻易吐槽一下而已。 Qt 的问题,在于两点:一是模凌两可始终暧昧的许可制度,再一是令人无奈去爱的公有扩大,从 qmake 到 qml 到各种 c++ 上的 MOC 扩大,切实是令 Pure C++ 派很不爽。 ...

September 22, 2021 · 6 min · jiezi

关于c++17:谈-C17-里的-Observer-模式-3

上上回的 谈 C++17 里的 Observer 模式 介绍了该模式的根本结构。起初在 谈 C++17 里的 Observer 模式 - 补 外面提供了改良版本,次要聚焦于针对多线程环境的暴力应用的场景。也能够返回 博客原文Observer Pattern - Part III而后咱们提到了,对于观察者模式来说,GoF 的原生定义当然是采纳一个 observer class 的形式,但对于差不多 15 年后的 C++11 来说,观察者应用一个 class 定义的形式有点掉队了。特地是到了简直 23 年后的 C++14/17 之后,lambda 以及 std::function 的反对力度变得较为稳固,无需太多“高级”手法也能轻松地包装闭包或者函数对象,在加上折叠表达式对变参模板的加成能力。所以当初是有一种呼声认为,间接在被观察者上绑定匿名函数对象、或者函数对象,才是观察者模式的正确打开方式。 那么是不是如此呢? 咱们首先要做的是实现这样的想法,而后用一段测试用例来展现这种模态下编码的可能性。再而后才来看看它的优缺点。 根本实现这一次的外围类模板咱们将其命名为 observable_bindable,因为这在你批改本人的实现代码时无利——只须要增加后缀就能够。这个模板类依然应用一个繁多的构造 S 作为事件/信号实体: namespace hicc::util { /** * @brief an observable object, which allows a lambda or a function to be bound as the observer. * @tparam S subject or event will be emitted to all bound observers. * */ template<typename S> class observable_bindable { public: virtual ~observable_bindable() { clear(); } using subject_t = S; using FN = std::function<void(subject_t const &)>; template<typename _Callable, typename... _Args> observable_bindable &add_callback(_Callable &&f, _Args &&...args) { FN fn = std::bind(std::forward<_Callable>(f), std::forward<_Args>(args)...); _callbacks.push_back(fn); return (*this); } template<typename _Callable, typename... _Args> observable_bindable &on(_Callable &&f, _Args &&...args) { return add_callback(f, args...); } /** * @brief fire an event along the observers chain. * @param event_or_subject */ void emit(subject_t const &event_or_subject) { for (auto &fn : _callbacks) fn(event_or_subject); } private: void clear() {} private: std::vector<FN> _callbacks{}; };}首先,咱们不提供解除 observer 绑定的成员函数。咱们感觉这是不必要的,因为这一设计的指标原本就是冲着 lambda 函数去的,解除 lambda 函数的绑定,没有多大意义。当然你能够实现一份 remove_observer,这并没有什么技术性难度,甚至你都不用如许懂 c++,照猫画虎也能弄一份。 ...

September 20, 2021 · 3 min · jiezi