從 main 函數(shù)開始
首先注冊 command
, option
以及 subcommand
,每個 command
都是一個 App
對象狞山,并為每個 App
對象設置回調set_callback
从媚。
獲取程序啟動時的指定參數(shù)筒繁,解析參數(shù)胖烛,從 App
對象的樹形結構中产雹,找到指定的command
的谬莹,然后執(zhí)行對應的回調函數(shù)檩奠。
設置命令及參數(shù)
- 聲名第一個
App
類的對象,相當于cleos
,再看指針附帽,通過App
的成員函數(shù)add_subcommand()
實現(xiàn)埠戳,成功地實現(xiàn)了鏈表 -
add_subcommand
函數(shù) 將option
添加到vector subcommands_
里,返回一個App
對象, 返回的App
對象可以繼續(xù)add_subcommand
,形成一個樹型結構蕉扮。 -
require_subcommand
函數(shù)指定該 command 不是一個單獨有效的命令整胃,需要一個subcommand
. -
add_option
函數(shù)將 創(chuàng)建Option
對象,并將指針保存在vector options_
中喳钟,每個對象可以無限擴展配置選項屁使。 -
option
調用required()
方法,表示這個option
需要一個參數(shù) -
add_flag
添加flag (不帶參數(shù))奔则,內(nèi)部會調用add_option
方法蛮寂。 -
set_callback
函數(shù)給每個App
對象設置一個回調.
option 對象
屬性
-
expected_
:該option
需要幾個參數(shù) -
required_
: 是否需要參數(shù) -
pname_
: 參數(shù)名,且不含前綴-
或--
-
snames_
: 參數(shù)名易茬,以-
為前綴 -
lnames_
: 參數(shù)名酬蹋,以--
為前綴
解析參數(shù)
解析參數(shù)調用
app.parse(argc, argv);
parse
將命令行參數(shù),存放在vector args
中,
std::vector<std::string> parse(int argc, char **argv) {
name_ = argv[0];
std::vector<std::string> args;
for(int i = argc - 1; i > 0; i--)
args.emplace_back(argv[i]);
return parse(args);
}
調用另一個parse方法
/// The real work is done here. Expects a reversed vector.
/// Changes the vector to the remaining options.
std::vector<std::string> &parse(std::vector<std::string> &args) {
_validate();
_parse(args);
run_callback();
return args;
}
_validate
首先_validate
這個方法里檢查option
選項有沒有沖突的
/// Check the options to make sure there are no conficts.
///
/// Currenly checks to see if mutiple positionals exist with -1 args
void _validate() const {
auto count = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
return opt->get_expected() == -1 && opt->get_positional();
});
if(count > 1)
throw InvalidError(name_ + ": Too many positional arguments with unlimited expected args");
for(const App_p &app : subcommands_)
app->_validate();
}
expected
解釋如下
/// The number of expected values, 0 for flag, -1 for unlimited vector
int expected_{1};
即疾呻, 0 代表的是添加的 flag
選項除嘹, -1 代表無限制的vector
選項
如果有相同名字的 vector
類型參數(shù)被指定,回拋出異常岸蜗。
_parse
- 函數(shù)
void _parse(std::vector<std::string> &args)
中尉咕, 首先循環(huán)調用_parse_single
方法,處理參數(shù)所有參數(shù)璃岳。 - 處理當前
app
對象的所有option
對象年缎,當該option
被解析過后,調用該option
的回調函數(shù)铃慷,將 解析后的results_
作為參數(shù)单芜,如下
/// Process the callback
void run_callback() const {
if(!callback_(results_))
throw ConversionError(get_name() + "=" + detail::join(results_));
if(!validators_.empty()) {
for(const std::string &result : results_)
for(const std::function<bool(std::string)> &vali : validators_)
if(!vali(result))
throw ValidationError(get_name() + "=" + result);
}
}
_parse_single
_parse_single
函數(shù)將參數(shù)分成POSITIONAL_MARK、SUBCOMMAND犁柜、LONG洲鸠、SHORT、NONE
五個種類。
-
SUBCOMMAND
代表解析的該參數(shù)在subcommands
列表中 -
LONG
代表參數(shù)--XXX
-
SHORT
代表參數(shù)-XXX
-
NONE
代表參數(shù)XXX
-
POSITIONAL_MARK
攢不知什么作用
_parse_subcommand
如果找到對應名字的 subcommand (com)
對象,彈出最后一個參數(shù)扒腕,執(zhí)行 com->_parse(args)
_parse_long
取出當前參數(shù)绢淀,--name=value
格式,找出對應的option
瘾腰,設置給result
屬性
_parse_short
取出當前參數(shù)皆的,-name
格式, 查找對應 option
對象,根據(jù) option
對象的 expected
字段蹋盆,接受剩余的 參數(shù)费薄,并設置給 option
的result
屬性
_parse_positional
如果當前已接受的option
參數(shù)個數(shù)還未到達expected
的數(shù)量,添加到vector
parse_order_
中栖雾。
callback
每個 command
或者 option
都可以設置一個回調楞抡, 解析命令行的參數(shù)后,會依次調用這些 callback
以新建一個賬戶為例:
createAccount->set_callback([this] {
if( !active_key_str.size() )
active_key_str = owner_key_str;
public_key_type owner_key, active_key;
try {
owner_key = public_key_type(owner_key_str);
} EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid owner public key: ${public_key}", ("public_key", owner_key_str));
try {
active_key = public_key_type(active_key_str);
} EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid active public key: ${public_key}", ("public_key", active_key_str));
auto create = create_newaccount(creator, account_name, owner_key, active_key);
if (!simple) {
if ( buy_ram_eos.empty() && buy_ram_bytes_in_kbytes == 0) {
.......
send_actions( { create, buyram, delegate } );
} else {
send_actions( { create, buyram } );
}
} else {
send_actions( { create } );
}
});
這段回調最終走到 send_actions
, 再到 push_actions
void send_actions(std::vector<chain::action>&& actions, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) {
auto result = push_actions( move(actions), extra_kcpu, compression);
if( tx_print_json ) {
cout << fc::json::to_pretty_string( result ) << endl;
} else {
print_result( result );
}
}
fc::variant push_actions(std::vector<chain::action>&& actions, int32_t extra_kcpu, packed_transaction::compression_type compression = packed_transaction::none ) {
signed_transaction trx;
trx.actions = std::forward<decltype(actions)>(actions);
return push_transaction(trx, extra_kcpu, compression);
}
打包交易數(shù)據(jù)后岩灭,然后會走到push_transaction
fc::variant push_transaction( signed_transaction& trx, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) {
......
if (!tx_dont_broadcast) {
return call(push_txn_func, packed_transaction(trx, compression));
} else {
return fc::variant(trx);
}
}
最終會走到 call(push_txn_func, packed_transaction(trx, compression));
跟蹤push_txn_func
會發(fā)現(xiàn)是個字符串
const string chain_func_base = "/v1/chain";
const string push_txn_func = chain_func_base + "/push_transaction";
call
函數(shù)最終調用:
template<typename T>
fc::variant call( const std::string& url,
const std::string& path,
const T& v ) {
try {
eosio::client::http::connection_param *cp = new eosio::client::http::connection_param(context, parse_url(url) + path,
no_verify ? false : true, headers);
return eosio::client::http::do_http_call( *cp, fc::variant(v), print_request, print_response );
}
catch(boost::system::system_error& e) {
......
}
}
do_http_call
函數(shù)就將打包好的參數(shù)拌倍,path
信息以http
請求的形式發(fā)送出去了,返回值給cleos
在命令行輸出