使用从字符串中提取的参数调用函数
我正在研究以下问题:
我得到的字符串格式如下:
I get strings that are formatted like this:
functionname_parameter1_parameter2_parameter3
otherfunctionname_parameter1_parameter2
.
.
.
我想用给定的参数调用函数.假设我有一个功能测试:
and i would like to call the function with the given parameters. So let's say i have a function test:
void test(int x, float y, std::string z) {}
然后我收到一条消息:
test_5_2.0_abc
然后我希望像这样自动调用函数测试:
then i would like the function test to be automatically invoked like this:
test(5, 2.0, "abc");
关于如何在 C++ 中实现这一点,您有什么提示吗?
Do you have any hints on how to accomplish this in C++?
推荐答案
更新: 更新了 stream_function
以修复评论中提到的参数评估顺序问题@Nawaz,并且还删除了 std::function
以提高效率.请注意,评估顺序修复仅适用于 Clang,因为 GCC 不遵循此处的标准. 可以找到带有手动顺序执行的 GCC 示例 这里.
Update: Updated stream_function
to fix the argument-evaluation-order problem @Nawaz mentioned in the comments, and also removed the std::function
for improved efficiency. Note that the evaluation-order fix only works for Clang, as GCC doesn't follow the standard here. An example for GCC, with manual order-enforcement, can be found here.
这通常不是那么容易实现.我曾经围绕 std::function
编写了一个小包装类,用于从 std::istream
中提取参数.下面是一个使用 C++11 的例子:
This is generally not that easy to accomplish. I wrote a little wrapper class around std::function
once that extracts the arguments from a std::istream
. Here's an example using C++11:
#include <map>
#include <string>
#include <iostream>
#include <sstream>
#include <functional>
#include <stdexcept>
#include <type_traits>
// for proper evaluation of the stream extraction to the arguments
template<class R>
struct invoker{
R result;
template<class F, class... Args>
invoker(F&& f, Args&&... args)
: result(f(std::forward<Args>(args)...)) {}
};
template<>
struct invoker<void>{
template<class F, class... Args>
invoker(F&& f, Args&&... args)
{ f(std::forward<Args>(args)...); }
};
template<class F, class Sig>
struct stream_function_;
template<class F, class R, class... Args>
struct stream_function_<F, R(Args...)>{
stream_function_(F f)
: _f(f) {}
void operator()(std::istream& args, std::string* out_opt) const{
call(args, out_opt, std::is_void<R>());
}
private:
template<class T>
static T get(std::istream& args){
T t; // must be default constructible
if(!(args >> t)){
args.clear();
throw std::invalid_argument("invalid argument to stream_function");
}
return t;
}
// void return
void call(std::istream& args, std::string*, std::true_type) const{
invoker<void>{_f, get<Args>(args)...};
}
// non-void return
void call(std::istream& args, std::string* out_opt, std::false_type) const{
if(!out_opt) // no return wanted, redirect
return call(args, nullptr, std::true_type());
std::stringstream conv;
if(!(conv << invoker<R>{_f, get<Args>(args)...}.result))
throw std::runtime_error("bad return in stream_function");
*out_opt = conv.str();
}
F _f;
};
template<class Sig, class F>
stream_function_<F, Sig> stream_function(F f){ return {f}; }
typedef std::function<void(std::istream&, std::string*)> func_type;
typedef std::map<std::string, func_type> dict_type;
void print(){
std::cout << "print()
";
}
int add(int a, int b){
return a + b;
}
int sub(int a, int b){
return a - b;
}
int main(){
dict_type func_dict;
func_dict["print"] = stream_function<void()>(print);
func_dict["add"] = stream_function<int(int,int)>(add);
func_dict["sub"] = stream_function<int(int,int)>(sub);
for(;;){
std::cout << "Which function should be called?
";
std::string tmp;
std::cin >> tmp;
auto it = func_dict.find(tmp);
if(it == func_dict.end()){
std::cout << "Invalid function '" << tmp << "'
";
continue;
}
tmp.clear();
try{
it->second(std::cin, &tmp);
}catch(std::exception const& e){
std::cout << "Error: '" << e.what() << "'
";
std::cin.ignore();
continue;
}
std::cout << "Result: " << (tmp.empty()? "none" : tmp) << '
';
}
}
在 Clang 3.3 下编译并按预期工作(小型现场示例).
Compiles under Clang 3.3 and works as expected (small live example).
Which function should be called?
a
Invalid function 'a'
Which function should be called?
add
2
d
Error: 'invalid argument to stream_function'
Which function should be called?
add
2
3
Result: 5
Which function should be called?
add 2 6
Result: 8
Which function should be called?
add 2
6
Result: 8
Which function should be called?
sub 8 2
Result: 6
再次一起破解那个课程很有趣,希望你喜欢.请注意,您需要稍微修改代码以适用于您的示例,因为 C++ IOstreams 将空格作为分隔符,因此您需要用空格替换消息中的所有下划线.不过应该很容易做到,之后只需从您的消息中构造一个 std::istringstream
:
It was fun to hack that class together again, hope you enjoy. Note that you need to modify the code a little to work for your example, since C++ IOstreams have whitespace as delimiter, so you'd need to replace all underscores in your message with spaces. Should be easy to do though, after that just construct a std::istringstream
from your message:
std::istringstream input(message_without_underscores);
// call and pass 'input'
相关文章