从 main 函数开始
首先注册 command
, option
以及 subcommand
,每个 command
都是一个 App
对象,并为每个 App
对象设置回调set_callback
。
获取程序启动时的指定参数,解析参数,从 App
对象的树形结构中,找到指定的command
的,然后执行对应的回调函数。
设置命令及参数
- 声名第一个
App
类的对象,相当于cleos
,再看指针,通过App
的成员函数add_subcommand()
实现,成功地实现了链表 -
add_subcommand
函数 将option
添加到vector subcommands_
里,返回一个App
对象, 返回的App
对象可以继续add_subcommand
,形成一个树型结构。 -
require_subcommand
函数指定该 command 不是一个单独有效的命令,需要一个subcommand
. -
add_option
函数将 创建Option
对象,并将指针保存在vector options_
中,每个对象可以无限扩展配置选项。 -
option
调用required()
方法,表示这个option
需要一个参数 -
add_flag
添加flag (不带参数),内部会调用add_option
方法。 -
set_callback
函数给每个App
对象设置一个回调.
option 对象
属性
-
expected_
:该option
需要几个参数 -
required_
: 是否需要参数 -
pname_
: 参数名,且不含前缀-
或--
-
snames_
: 参数名,以-
为前缀 -
lnames_
: 参数名,以--
为前缀
解析参数
解析参数调用
app.parse(argc, argv);
parse
将命令行参数,存放在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
类型参数被指定,回抛出异常。
_parse
- 函数
void _parse(std::vector<std::string> &args)
中, 首先循环调用_parse_single
方法,处理参数所有参数。 - 处理当前
app
对象的所有option
对象,当该option
被解析过后,调用该option
的回调函数,将 解析后的results_
作为参数,如下
/// 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
函数将参数分成POSITIONAL_MARK、SUBCOMMAND、LONG、SHORT、NONE
五个种类。
-
SUBCOMMAND
代表解析的该参数在subcommands
列表中 -
LONG
代表参数--XXX
-
SHORT
代表参数-XXX
-
NONE
代表参数XXX
-
POSITIONAL_MARK
攒不知什么作用
_parse_subcommand
如果找到对应名字的 subcommand (com)
对象,弹出最后一个参数,执行 com->_parse(args)
_parse_long
取出当前参数,--name=value
格式,找出对应的option
,设置给result
属性
_parse_short
取出当前参数,-name
格式, 查找对应 option
对象,根据 option
对象的 expected
字段,接受剩余的 参数,并设置给 option
的result
属性
_parse_positional
如果当前已接受的option
参数个数还未到达expected
的数量,添加到vector
parse_order_
中。
callback
每个 command
或者 option
都可以设置一个回调, 解析命令行的参数后,会依次调用这些 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);
}
打包交易数据后,然后会走到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
会发现是个字符串
const string chain_func_base = "/v1/chain";
const string push_txn_func = chain_func_base + "/push_transaction";
call
函数最终调用:
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
函数就将打包好的参数,path
信息以http
请求的形式发送出去了,返回值给cleos
在命令行输出