zoukankan      html  css  js  c++  java
  • 介绍CppShell

    写在前面

    bajdcc/CppShell

    最近心血来潮又造了个轮子,其实启发我的是bajdcc/jMiniLang中的管道思想,java运行着太慢,因而用C艹实现一把。

    如题图所示,使用非常非常简单。

    1. range生成有限/无穷数列,`range 0`生成自然数无穷数列,`range 1 10`生成1到10
    2. take N,表示从有限/无穷数列中摘取前N行
    3. last N,表示从有限数列中取倒数N行,当然了如果数列是无穷的,那么GG
    4. load FILENAME,加载文件
    5. save FILENAME,保存至文件

    设计思路

    首先当然是解析命令行输入啦,然后则是处理与输出。

    一、处理命令

    假如命令是诸如`load 1.txt | uppercase | save 2.txt`,以“|”作分隔,分隔后得到单一程序命令行,再以空格作分隔。

    用式子来表示是:

    1. 用户输入command_string
    2. applications = command_string.split('|')

    对applications中每一app,app_args = app.split(' '),然后app_name = app_args[0],删除app_args[0],得到后面的参数arguments下面的任务是,根据app_name和arguments来创建应用程序。

    二、创建应用程序

    我们以`range 1 100 | save 2.txt`为例,意义为“生成1到100的数列,然后保存至文件”。

    所以必须生成两个程序range和save,那么两者是什么关系呢?

    思考一下:生成数列,将数列保存至文件。即:以数列作输入,文件为输出。得到:range => save。即save的输入是range的输出,是从前向后逐渐依赖的关系,换句话说,后者调用前者。

    这里用到了装饰者模式,既然后者调用前者,那么后者将前者包裹起来即可。应用程序的创建涉及工厂模式,没什么大过花哨的C++技巧。

    三、应用程序接口设计

    其实也就是“流”接口的设计。参考众多流设计,这里我只实现最简单的:

    1. bool available() const,返回流是否可用/到末尾
    2. char next(),读取当前字符,并准备下个字符

    应用程序只要重载这两个接口即可。

    代码实现

    1. Shell

    void CShell::exec(const std::string& cmd)
    {
        auto s = std::split(cmd, '|');
        std::vector<app_t> cmder;
        std::vector<std::string> names;
        std::vector<std::vector<std::string>> arg;
        for (auto& str : s)
        {
            str = std::trim(str);
            auto part = std::split(str, ' ');
            if (part.empty())
                return error("empty argument");
            names.push_back(part[0]);
            auto apt = CApp::get_type_by_name(part[0]);
            if (apt == app_none)
                return error("invalid application: " + str);
            part.erase(part.begin());
            cmder.push_back(apt);
            arg.push_back(part); // 应用程序参数
        }
        auto inner = CApp::create(app_null); // 最里层程序
        std::shared_ptr<CApp> app;
        for (uint32_t i = 0; i < cmder.size(); i++)
        {
            app = CApp::create(cmder[i]); // 工厂模式创建应用程序
            if (app->set_arg(arg[i]) != 0)
                return error(names[i] + ": " + app->get_err());
            app->set_inner_app(inner); // 装饰模式进行包装
            inner = app;
        }
        while (app->available()) // 正式工作!
        {
            auto c = app->next();
            if (c != '')
                std::cout << c;
        }
    }
    
    void CShell::error(const std::string& str)
    {
        std::cerr << str << std::endl;
    }
    

    2. App

    enum app_t
    {
        app__begin,
        app_none,
        app_null,
        app_pipe,
        app_range,
        app_take,
        app_last,
        app_load,
        app_save,
        app__end
    };
    
    class CApp
    {
    public:
        CApp();
        virtual ~CApp();
    
        static std::shared_ptr<CApp> create(app_t type);
        static app_t get_type_by_name(const std::string &name);
    
        int set_arg(std::vector<std::string> arg);
        virtual int init() = 0;
    
        void set_inner_app(std::shared_ptr<CApp> app);
    
        std::string get_err() const;
    
        virtual bool available() const = 0;
        virtual char next() = 0;
    
    protected:
        std::vector<std::string> args;
        std::string error;
        std::shared_ptr<CApp> inner;
    };
    
    // 创建
    std::shared_ptr<CApp> CApp::create(app_t type)
    {
        switch (type)
        {
        case app_none:
            break;
        case app_null:
            return std::make_shared<CAppNull>();
        case app_pipe:
            return std::make_shared<CAppPipe>();
        case app_range:
            return std::make_shared<CAppRange>();
        case app_take:
            return std::make_shared<CAppTake>();
        case app_last:
            return std::make_shared<CAppLast>();
        case app_load:
            return std::make_shared<CAppLoad>();
        case app_save:
            return std::make_shared<CAppSave>();
        default:
            break;
        }
        assert(!"invalid type");
        return nullptr;
    }
    

    3. AppLoad

    就举这一个例子吧

    int CAppTake::init() // 初始化
    {
        if (args.size() == 1) // 有一个参数
        {
            start = 1; // 计数开始
            end = atoi(args[0].c_str()); // 计数结束
        }
        else
        {
            error = "invalid argument size";
            return -1;
        }
        return 0;
    }
    
    bool CAppTake::available() const
    {
        return start <= end || !data.empty();
    }
    
    char CAppTake::next()
    {
        if (data.empty())
        {
            if (!available()) // 上一流已经中止
                return '';
            while (inner->available()) // 上一流有数据
            {
                auto c = inner->next();
                data.push(c);
                if (c == '
    ') // 读取一行到data中
                    break;
            }
            start++; // 计数加一
            if (data.empty()) // 没有数据了
                return '';
        }
        auto ch = data.front(); // 输出读取的一行数据
        data.pop();
        return ch;
    }
    

    阶段性总结

    总之,做这个轮子还是挺愉悦的~因为并未脱离舒适区。。就当复习吧。

    好吧,其实写这玩意是因为bash中的awk、sed、grep等查找替换太复杂了,还不如自己做个。

    https://zhuanlan.zhihu.com/p/26591115备份。

  • 相关阅读:
    pyhon3 mysql的下载和配置
    python3 进程池和线程池
    python3 udp socketserver
    python3 多进程实现socket通讯
    python3 在一个进程内开启多个线程实现socket通讯
    python3 单线程(gevent)实现并发的socket通讯
    python3 协程gevent模块(遇到IO自动切换)
    python3 greenlet模块
    python3 协程函数yeild
    python3 线程队列queue
  • 原文地址:https://www.cnblogs.com/bajdcc/p/8972973.html
Copyright © 2011-2022 走看看