開源伺服器設計總計(plain framework2020年總計)

戀月發表於2021-04-11

2020年註定會被歷史銘記,世界遭受著一場前所未有的災難,這種災難到現在還在持續。還記得19年末的時候,那時候聽到一點點訊息,哪裡想得到年關難過,災難來的讓人猝不及防。由於疫情防控,2020年感覺轉瞬即逝,彷彿晃眼的功夫。本來做些自身職業上的改變,去年因為自身的原因因此擱淺。想想這幾年自己維護的這個框架並未有太大的起色,甚者一度遺忘了它,所規劃的很多內容並未得到實現。自己的懶惰擱淺,未免是對生命的嚴重浪費。今年的雖然暫時沒有太多的計劃,但是改變已經走在路上,希望這對自己的人生有所幫助。plain framework原本是面向遊戲伺服器設計的,不過在設計上儘量相容所有的網路應用,從14年開始到現在已經六七年了,經過多次的修改,也擴充了不少的介面,在使用上更加的便捷。在這篇總結中,我主要講述的是最近加入的控制檯(console)模組,其實這個模組在許多框架中是很常見的。未來的技術日新月異,如果大家對程式設計上面有所興趣,不妨可以做一定的參考,如有不足的地方也請指正,我將認真的思考其中遺留的問題。在這裡我也祝願大家在2021年,在風雨之後迎來希望的彩虹!

總結

  在以前的文章中,我已經總結過plain framework的成長過程,從C++98到C++11經歷了不小的變化。如今的目錄結構和當初很不同,從參考到自己的一些設計,讓整個框架越來越好用越來越高效是最終的目的。但是這種改變是漫長的,而且總覺得力不從心,許多的設計雖然有過臨時的想法始終沒能得到執行。當前這個框架現階段如果直接用於專案中問題不大,卻不能保證有所隱藏的一些BUG。今年我要做出一些改變,不單單做遊戲方面的設計,我對於未來的技術也有過興趣,如在之前的專案中使用過VUE框架來做後臺的相應設計。不過相對於未來,演算法始終是大方向以及技術上需要突破的,比如AI人工智慧,其中的演算法需要許多的數學知識,但這些相應的知識我自己要麼遺忘要麼還沒認真接觸,作為自己的興趣後續會向這些方向研究。

  人生總要有些改變,支援遲來和早來!因此我需要有段時間來放空腦袋,想一想未來的方向了。隨著年齡的增長就產生了一種莫名的危機感,在職業上也有些乏味缺少新鮮,但是生活總是要繼續的。一個人如果有了計劃而不執行,到了沒有精力和時間去做得時候,那時候再後悔未免可笑。因此我覺得一個人就得堅定地向著自己的目標走去,就算目標看來那樣遙不可及,但我們可以不斷改變策略,畢竟沒有人隨隨便便就成功,但如果不去行動那麼連成功的機會都沒有。

  希望大家都能慢慢接近自己的理想,也希望這場人類的災難早點過去!

控制檯(console)

  控制檯是在應用中提供除錯的工具,一般情況下可以來分析應用執行過程中一些資料。在經典的作業系統中,控制檯實在是太常見了,如windows中的命令列控制器,可以使用相應的命令對系統或者應用進行執行、除錯和分析。

  

   plain framework加入控制檯的目的,也是為了除錯以及在應用執行中做一些除錯和處理,提供了比較豐富的命令介面使得外部註冊命令比較容易。在伺服器的設計中,很多時候想要看看應用的執行緒記憶體情況,還有網路的連結和資料的收發情況。增加控制檯,有助於我們在測試的時候,對不同情況下特別是壓力測試時分析出重要的資料,這有助於幫助我們對程式進行優化。目前PF 中的命令不多,主要幾個常見的命令,後續會繼續完善。

  下圖為PF中控制檯的除錯(包括在LINUX下的編譯部分),目前僅支援網路除錯(直接輸入的方式很快整合,由於感覺用處不大暫時沒實現):

 

部分程式碼

  由於控制檯需要使用網路命令列,因此在框架中增加了standard的網路協議(protocol),這個網路協議是遇到換行便將內容讀出並呼叫註冊的執行介面:

#include "pf/basic/string.h"                                                       
#include "pf/net/stream/input.h"                                                   
#include "pf/net/stream/output.h"                                                  
#include "pf/net/connection/basic.h"                                               
#include "pf/net/connection/manager/listener.h"                                    
#include "pf/net/protocol/standard.h"                                              
                                                                                   
using namespace pf_basic::string;                                                  
using namespace pf_net::protocol;                                                  
                                                                                   
bool Standard::command(connection::Basic *connection, uint16_t count) {            
  if (connection->is_disconnect()) return false; //Leave this when not connected.
  stream::Input *istream = &connection->istream();                                 
  auto line = istream->readline();                                                 
  if (!line.empty()) {                                                             
    if (!connection->check_safe_encrypt()) return false;                           
    auto listener = connection->get_listener();                                    
    if (!is_null(listener)) {                                                      
      rtrim(line); // Remove '\n' '\r' or other words on last.                     
      auto callback = listener->get_standard_callback();                           
      if (callback) callback(line, connection);                                    
    }                                                                                                                                                  
  }                                                                                
  return true;                                                                     
}                                                                                  
                                                                                   
bool Standard::send(connection::Basic *connection, packet::Interface *packet) { 
  return true;                                                                     
}                                                                                  

  在處理命令的時候註冊的回撥函式如下:

void console_net_handle(                                                                                       
    const std::string &cmd, pf_net::connection::Basic *connection) {            
  using namespace pf_console;                                                                                  
  using namespace pf_basic::string;                                                                            
  if (is_null(ENGINE_POINTER)) return;                                                                         
  auto console = ENGINE_POINTER->get_console();                                                                
  if (is_null(console)) return;                                                                                
  if ("quit" == cmd) {                                                                                         
    connection->exit();                                                                                        
    return;                                                                                                    
  }                                                                                                            
  StringInput input(cmd);                                                                                      
  NetOutput output(connection);                                                                                
  console->run(&input, &output);                                                                               
}                 

  整個控制檯的實現目錄結構如下:

  控制檯應用程式碼:

#include "pf/basic/string.h"
#include "pf/console/argv_input.h"
#include "pf/console/array_input.h"
#include "pf/console/commands/app.h"
#include "pf/console/commands/help.h"
#include "pf/console/commands/list.h"
#include "pf/basic/logger.h"
#include "pf/console/application.h"

using namespace pf_console;
using namespace pf_interfaces::console;
using namespace pf_basic::string;

uint8_t Application::run(Input *input, Output *output) {
  std::unique_ptr<Input> input_temp;
  std::unique_ptr<Output> output_temp;
  if (is_null(input)) {
    unique_move(Input, new ArgvInput(), input_temp);
    input = input_temp.get();
  }
  if (is_null(output)) {
    unique_move(Output, new Output(), output_temp);
    output = output_temp.get();
  }
  configure_IO(input, output);
  uint8_t exit_code{0};
  try {
    exit_code = do_run(input, output);
  } catch (std::exception &e) {
    std::cout << "Application::run get error!!!: " << e.what() << std::endl;
  }
  return exit_code;
}

uint8_t Application::do_run(Input *input, Output *output) {
  if (input->has_parameter_option({"--version", "-V"})) {
    output->write_ln(get_long_version());
    return 0;
  }
  try {
    input->bind(get_definition());
  } catch(...) {

  }
  auto name = get_command_name(input);
  std::unique_ptr<Input> input_temp;
  if (input->has_parameter_option({"--help", "-h"}, true)) {
    if (name == "") {
      name = "help";
      unique_move(Input,
          new ArrayInput({{"command_name", default_command_name_}}),
          input_temp);
      input = input_temp.get();
    } else {
      want_helps_ = false;
    }
  }
  if (name == "") {
    name = default_command_name_;
    auto definition = get_definition();
    definition->set_argument(InputArgument("command",
        InputArgument::kModeOptional,
        definition->get_argument("command").get_description(), name));
  }
  Command *command{nullptr};
  try {
    running_command_ = nullptr;
    command = find(name);
  } catch (...) {

  }
  if (is_null(command)) {
    FAST_ERRORLOG(CONSOLE_MODULENAME, 
                  "[console] (Application::run)"
                  " can't find the command: %s", 
                  name.c_str());
    return 1;
  }
  running_command_ = command;
  auto exit_code = do_runcommand(command, input, output);
  return exit_code;
}

InputDefinition *Application::get_definition() {
  if (is_null(definition_)) {
    std::unique_ptr<InputDefinition> temp(new InputDefinition());
    *temp = get_default_input_definition();
    definition_ = std::move(temp);
    if (single_command_) {
      if (is_null(definition_temp_))    {
        unique_move(InputDefinition, new InputDefinition(), definition_temp_);
        *definition_temp_ = *definition_;
      }
      definition_temp_->set_arguments({});
      return definition_temp_.get();
    }
  }
  return definition_.get();
}

std::string Application::get_long_version() const {
  std::string r{"Console Tool"};
  if (name_ != "") {
    if (version_ != "") {
      r = name_ + " " + version_;
    }
    r = name_;
  }
  return r;
}

Command *Application::add(Command *command) {
  // std::cout << "Add command: " << command->name() << std::endl;
  if (!command->is_enabled()) {
    return nullptr;
  }
  init();
  command->set_application(this);
  // Will throw if the command is not correctly initialized.
  command->get_definition();
  command->configure();
  auto name = command->name();
  if (name == "") {
    throw std::logic_error("command cannot have an empty name.");
  }
  if (commands_.find(name) != commands_.end()) {
    return commands_[name].get();
  }
  std::unique_ptr<Command> temp;
  unique_move(Command, command, temp);
  commands_[name] = std::move(temp);
  for (auto const &alias : command->get_aliases()) {
    command_aliases_[alias] = name;
  }
  return command;
}

Command *Application::get(const std::string &_name) {
  auto name = get_command_real_name(_name);
  if ("" == name) {
    std::string e = "The command \"" + _name + "\" does not exist.";
    throw std::invalid_argument(e);
  }
  if (commands_.find(name) == commands_.end()) return nullptr;
  auto command = commands_[name].get();
  if (want_helps_) {
    want_helps_ = false;
    auto help_command = get("help");
    help_command->set_command(command);
    return help_command;
  }
  return command;
}

std::vector<std::string> Application::get_namespaces() {
  std::vector<std::string> r;
  auto commands = all();
  for (auto it = commands.begin(); it != commands.end(); ++it) {
    if (it->second->is_hidden()) continue;
    auto temp = extract_all_namespace(it->first);
    for (const auto &one : temp) r.emplace_back(one);
    for (const auto &alias : it->second->get_aliases()) {
      auto temp1 = extract_all_namespace(alias);
      for (const auto &one : temp1) r.emplace_back(one);
    }
  }
  // * The result maybe need use array_unique to remove the same values.
  return r;
}

std::string Application::find_namespace(const std::string &_namespace) {
  return "";
}

Command *Application::find(const std::string &name) {
  init();
  for (auto it = commands_.begin(); it != commands_.end(); ++it) {
    if (!is_null(it->second)) {
      for (const auto &alias : it->second->get_aliases()) {
        if ("" == command_aliases_[alias]) 
          command_aliases_[alias] = it->second->name();
      }
    } else {
      std::cout << "find no command: " << it->first << std::endl;
    }
  }
  return get(name);
}

std::map<std::string, Command *>
Application::all(const std::string &_namespace) {
  std::map<std::string, Command *> r;
  init();
  if ("" == _namespace) {
    for (auto it = commands_.begin(); it != commands_.end(); ++it) {
      r[it->first] = it->second.get();
    }
    return r;
  }
  for (auto it = commands_.begin(); it != commands_.end(); ++it) {
    if (_namespace == extract_namespace(it->first)) {
      r[it->first] = it->second.get();
    }
  }
  return r;
}

Application &Application::set_default_command(
    const std::string &name, bool is_single_command) {
  default_command_name_ = name;
  if (is_single_command) {
    // Ensure the command exist
    find(name);
    single_command_ = true;
  }
  return *this;
}

uint8_t Application::do_runcommand(
    Command *command, Input *input, Output *output) {
  // std::cout << "do_runcommand: " << command->name() << std::endl;
  return command->run(input, output);
}

std::string Application::get_command_name(Input *input) const {
  return single_command_ ? default_command_name_ : input->get_first_argument();
}

InputDefinition Application::get_default_input_definition() const {
  std::vector<InputParameter *> p;
  std::unique_ptr<InputParameter> p1(new InputArgument(
        "command", InputParameter::kModeRequired,
        "The command to execute", ""
        ));
  p.emplace_back(p1.get());
  std::unique_ptr<InputParameter> p2(new InputOption(
        "--help", "-h", InputParameter::kModeNone,
        "Display help for the given command. When no command"
        " is given display help for the" + default_command_name_ + "command", ""
        ));
  p.emplace_back(p2.get());
  std::unique_ptr<InputParameter> p3(new InputOption(
        "--quiet", "-q", InputParameter::kModeNone,
        "Do not output any message", ""
        ));
  p.emplace_back(p3.get());
  std::unique_ptr<InputParameter> p4(new InputOption(
        "--verbose", "-v|vv|vvv", InputParameter::kModeNone,
        "Increase the verbosity of messages: 1 for normal output, "
        "2 for more verbose output and 3 for debug", ""
        ));
  p.emplace_back(p4.get());
  std::unique_ptr<InputParameter> p5(new InputOption(
        "--version", "-V", InputParameter::kModeNone,
        "Display this application version", ""
        ));
  p.emplace_back(p5.get());
  std::unique_ptr<InputParameter> p6(new InputOption(
        "--ansi", "", InputParameter::kModeNone,
        "Force ANSI output", ""
        ));
  p.emplace_back(p6.get());
  std::unique_ptr<InputParameter> p7(new InputOption(
        "--no-ansi", "", InputParameter::kModeNone,
        "Disable ANSI output", ""
        ));
  p.emplace_back(p7.get());
  std::unique_ptr<InputParameter> p8(new InputOption(
        "--no-interaction", "-n", InputParameter::kModeNone,
        "Do not ask any interactive question", ""
        ));
  p.emplace_back(p8.get());

  return InputDefinition(p);
}

std::vector<Command *> Application::get_default_commands() const {
  std::vector<Command *> r;
  return r;
}

std::string Application::get_abbreviation_suggestions(
    const std::vector<std::string> &abbrevs) const {
  return "";
}

std::vector<std::string> Application::find_alternatives(
    const std::string &name, const std::vector<std::string> &collection) const {
  return {};
}

void Application::configure_IO(Input *input, Output *output) {
  if (input->has_parameter_option({"--ansi"}, true)) {
    output->set_decorated(true);
  } else if (input->has_parameter_option({"'--no-ansi'"}, true)) {
    output->set_decorated(false);
  }
}

std::vector<std::string> Application::extract_all_namespace(
    const std::string &name) {
  std::vector<std::string> r;
  std::vector<std::string> parts;
  explode(name.c_str(), parts, ":", true, true);
  for (const auto &part : parts) {
    if (r.size() > 0) {
      std::string temp = r[r.size() - 1] + ":" + part;
      r.emplace_back(temp);
    } else {
      r.emplace_back(part);
    }
  }
  return r;
}

std::string Application::extract_namespace(
    const std::string &name, int32_t limit) const {
  return "";
}

void Application::init() {
  if (initialized_) return;
  initialized_ = true;
  // std::cout << "Application::init" << std::endl;
  add(new commands::Help());
  add(new commands::List());
  add(new commands::App());
}

  命令實現程式碼:

#include "pf/basic/string.h"
#include "pf/console/argv_input.h"
#include "pf/console/array_input.h"
#include "pf/console/commands/app.h"
#include "pf/console/commands/help.h"
#include "pf/console/commands/list.h"
#include "pf/basic/logger.h"
#include "pf/console/application.h"

using namespace pf_console;
using namespace pf_interfaces::console;
using namespace pf_basic::string;

uint8_t Application::run(Input *input, Output *output) {
  std::unique_ptr<Input> input_temp;
  std::unique_ptr<Output> output_temp;
  if (is_null(input)) {
    unique_move(Input, new ArgvInput(), input_temp);
    input = input_temp.get();
  }
  if (is_null(output)) {
    unique_move(Output, new Output(), output_temp);
    output = output_temp.get();
  }
  configure_IO(input, output);
  uint8_t exit_code{0};
  try {
    exit_code = do_run(input, output);
  } catch (std::exception &e) {
    std::cout << "Application::run get error!!!: " << e.what() << std::endl;
  }
  return exit_code;
}

uint8_t Application::do_run(Input *input, Output *output) {
  if (input->has_parameter_option({"--version", "-V"})) {
    output->write_ln(get_long_version());
    return 0;
  }
  try {
    input->bind(get_definition());
  } catch(...) {

  }
  auto name = get_command_name(input);
  std::unique_ptr<Input> input_temp;
  if (input->has_parameter_option({"--help", "-h"}, true)) {
    if (name == "") {
      name = "help";
      unique_move(Input,
          new ArrayInput({{"command_name", default_command_name_}}),
          input_temp);
      input = input_temp.get();
    } else {
      want_helps_ = false;
    }
  }
  if (name == "") {
    name = default_command_name_;
    auto definition = get_definition();
    definition->set_argument(InputArgument("command",
        InputArgument::kModeOptional,
        definition->get_argument("command").get_description(), name));
  }
  Command *command{nullptr};
  try {
    running_command_ = nullptr;
    command = find(name);
  } catch (...) {

  }
  if (is_null(command)) {
    FAST_ERRORLOG(CONSOLE_MODULENAME, 
                  "[console] (Application::run)"
                  " can't find the command: %s", 
                  name.c_str());
    return 1;
  }
  running_command_ = command;
  auto exit_code = do_runcommand(command, input, output);
  return exit_code;
}

InputDefinition *Application::get_definition() {
  if (is_null(definition_)) {
    std::unique_ptr<InputDefinition> temp(new InputDefinition());
    *temp = get_default_input_definition();
    definition_ = std::move(temp);
    if (single_command_) {
      if (is_null(definition_temp_))    {
        unique_move(InputDefinition, new InputDefinition(), definition_temp_);
        *definition_temp_ = *definition_;
      }
      definition_temp_->set_arguments({});
      return definition_temp_.get();
    }
  }
  return definition_.get();
}

std::string Application::get_long_version() const {
  std::string r{"Console Tool"};
  if (name_ != "") {
    if (version_ != "") {
      r = name_ + " " + version_;
    }
    r = name_;
  }
  return r;
}

Command *Application::add(Command *command) {
  // std::cout << "Add command: " << command->name() << std::endl;
  if (!command->is_enabled()) {
    return nullptr;
  }
  init();
  command->set_application(this);
  // Will throw if the command is not correctly initialized.
  command->get_definition();
  command->configure();
  auto name = command->name();
  if (name == "") {
    throw std::logic_error("command cannot have an empty name.");
  }
  if (commands_.find(name) != commands_.end()) {
    return commands_[name].get();
  }
  std::unique_ptr<Command> temp;
  unique_move(Command, command, temp);
  commands_[name] = std::move(temp);
  for (auto const &alias : command->get_aliases()) {
    command_aliases_[alias] = name;
  }
  return command;
}

Command *Application::get(const std::string &_name) {
  auto name = get_command_real_name(_name);
  if ("" == name) {
    std::string e = "The command \"" + _name + "\" does not exist.";
    throw std::invalid_argument(e);
  }
  if (commands_.find(name) == commands_.end()) return nullptr;
  auto command = commands_[name].get();
  if (want_helps_) {
    want_helps_ = false;
    auto help_command = get("help");
    help_command->set_command(command);
    return help_command;
  }
  return command;
}

std::vector<std::string> Application::get_namespaces() {
  std::vector<std::string> r;
  auto commands = all();
  for (auto it = commands.begin(); it != commands.end(); ++it) {
    if (it->second->is_hidden()) continue;
    auto temp = extract_all_namespace(it->first);
    for (const auto &one : temp) r.emplace_back(one);
    for (const auto &alias : it->second->get_aliases()) {
      auto temp1 = extract_all_namespace(alias);
      for (const auto &one : temp1) r.emplace_back(one);
    }
  }
  // * The result maybe need use array_unique to remove the same values.
  return r;
}

std::string Application::find_namespace(const std::string &_namespace) {
  return "";
}

Command *Application::find(const std::string &name) {
  init();
  for (auto it = commands_.begin(); it != commands_.end(); ++it) {
    if (!is_null(it->second)) {
      for (const auto &alias : it->second->get_aliases()) {
        if ("" == command_aliases_[alias]) 
          command_aliases_[alias] = it->second->name();
      }
    } else {
      std::cout << "find no command: " << it->first << std::endl;
    }
  }
  return get(name);
}

std::map<std::string, Command *>
Application::all(const std::string &_namespace) {
  std::map<std::string, Command *> r;
  init();
  if ("" == _namespace) {
    for (auto it = commands_.begin(); it != commands_.end(); ++it) {
      r[it->first] = it->second.get();
    }
    return r;
  }
  for (auto it = commands_.begin(); it != commands_.end(); ++it) {
    if (_namespace == extract_namespace(it->first)) {
      r[it->first] = it->second.get();
    }
  }
  return r;
}

Application &Application::set_default_command(
    const std::string &name, bool is_single_command) {
  default_command_name_ = name;
  if (is_single_command) {
    // Ensure the command exist
    find(name);
    single_command_ = true;
  }
  return *this;
}

uint8_t Application::do_runcommand(
    Command *command, Input *input, Output *output) {
  // std::cout << "do_runcommand: " << command->name() << std::endl;
  return command->run(input, output);
}

std::string Application::get_command_name(Input *input) const {
  return single_command_ ? default_command_name_ : input->get_first_argument();
}

InputDefinition Application::get_default_input_definition() const {
  std::vector<InputParameter *> p;
  std::unique_ptr<InputParameter> p1(new InputArgument(
        "command", InputParameter::kModeRequired,
        "The command to execute", ""
        ));
  p.emplace_back(p1.get());
  std::unique_ptr<InputParameter> p2(new InputOption(
        "--help", "-h", InputParameter::kModeNone,
        "Display help for the given command. When no command"
        " is given display help for the" + default_command_name_ + "command", ""
        ));
  p.emplace_back(p2.get());
  std::unique_ptr<InputParameter> p3(new InputOption(
        "--quiet", "-q", InputParameter::kModeNone,
        "Do not output any message", ""
        ));
  p.emplace_back(p3.get());
  std::unique_ptr<InputParameter> p4(new InputOption(
        "--verbose", "-v|vv|vvv", InputParameter::kModeNone,
        "Increase the verbosity of messages: 1 for normal output, "
        "2 for more verbose output and 3 for debug", ""
        ));
  p.emplace_back(p4.get());
  std::unique_ptr<InputParameter> p5(new InputOption(
        "--version", "-V", InputParameter::kModeNone,
        "Display this application version", ""
        ));
  p.emplace_back(p5.get());
  std::unique_ptr<InputParameter> p6(new InputOption(
        "--ansi", "", InputParameter::kModeNone,
        "Force ANSI output", ""
        ));
  p.emplace_back(p6.get());
  std::unique_ptr<InputParameter> p7(new InputOption(
        "--no-ansi", "", InputParameter::kModeNone,
        "Disable ANSI output", ""
        ));
  p.emplace_back(p7.get());
  std::unique_ptr<InputParameter> p8(new InputOption(
        "--no-interaction", "-n", InputParameter::kModeNone,
        "Do not ask any interactive question", ""
        ));
  p.emplace_back(p8.get());

  return InputDefinition(p);
}

std::vector<Command *> Application::get_default_commands() const {
  std::vector<Command *> r;
  return r;
}

std::string Application::get_abbreviation_suggestions(
    const std::vector<std::string> &abbrevs) const {
  return "";
}

std::vector<std::string> Application::find_alternatives(
    const std::string &name, const std::vector<std::string> &collection) const {
  return {};
}

void Application::configure_IO(Input *input, Output *output) {
  if (input->has_parameter_option({"--ansi"}, true)) {
    output->set_decorated(true);
  } else if (input->has_parameter_option({"'--no-ansi'"}, true)) {
    output->set_decorated(false);
  }
}

std::vector<std::string> Application::extract_all_namespace(
    const std::string &name) {
  std::vector<std::string> r;
  std::vector<std::string> parts;
  explode(name.c_str(), parts, ":", true, true);
  for (const auto &part : parts) {
    if (r.size() > 0) {
      std::string temp = r[r.size() - 1] + ":" + part;
      r.emplace_back(temp);
    } else {
      r.emplace_back(part);
    }
  }
  return r;
}

std::string Application::extract_namespace(
    const std::string &name, int32_t limit) const {
  return "";
}

void Application::init() {
  if (initialized_) return;
  initialized_ = true;
  // std::cout << "Application::init" << std::endl;
  add(new commands::Help());
  add(new commands::List());
  add(new commands::App());
}
[viticm@izuf633l0ge76tbdctmljaz core]$ cat /home/viticm/develop/github/plain/framework/core/src/console/command.cc 
#include "pf/support/helpers.h"
#include "pf/console/application.h"
#include "pf/console/command.h"

using namespace pf_support;
using namespace pf_console;

// Static member must be initialized.
std::string Command::default_name_{"unknown"};
   
void Command::merge_application_definition(bool merge_args) {
  if (is_null(app_) or !is_null(full_definition_)) return;
  unique_move(InputDefinition, new InputDefinition(), full_definition_);
  full_definition_->set_options(array_values(definition_->get_options()));
  full_definition_->add_options(
    array_values(app_->get_definition()->get_options()));
  if (merge_args) {
    full_definition_->set_arguments(
        array_values(app_->get_definition()->get_arguments()));
    full_definition_->add_arguments(array_values(definition_->get_arguments()));
  } else {
    full_definition_->set_arguments(array_values(definition_->get_arguments()));
  }
}

uint8_t Command::run(Input *input, Output *output) {
  uint8_t r{0};
  merge_application_definition();
  // bind the input against the command specific arguments/options
  try {
    input->bind(get_definition(), is_parse_input());
  } catch (std::exception &e) {
    if (!ignore_validation_errors_)
      throw std::runtime_error(e.what());
  }
  initialize(input, output);

  // Set process title.
  if (!process_title_.empty()) {

  }
  if (input->is_interactive()) {
    interact(input, output);
  }

  // The command name argument is often omitted when a command is executed 
  // directly with its run() method.
  // It would fail the validation if we didn't make sure the command argument 
  // is present, since it's required by the application.
  if (input->has_argument("command") && input->get_argument("command") == "") {
    input->set_argument("command", name_);
  }

  input->validate();

  if (code_) {
    r = code_(input, output);
  } else {
    r = execute(input, output);
  }

  return r;
}

 

更多

  可以在github上找到完整的專案:https://github.com/viticm/plain

相關文章