我有一些带有空格分隔数据的文件。我想要一个仅接受文件名的函数parse
和一个作用于一行上所有元素的函数。因此,例如,如果我有一个文件,其数据格式为
int float string
int float string
int float string
...
然后我想要一个函数parse
,该函数接受包含该数据的文件名,以及一个将处理每一行的lambda。如果我知道每一行有多少个元素(在本例中为3),那么我可以这样做:
#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
using namespace std;
template <typename Func, typename A, typename B, typename C>
void parse(const string & filename, Func func){
string line;
// TODO: Get line from file, not cin
// std::ifstream file(filename);
while (std::getline(cin, line)) {
stringstream ss(line);
A a;
B b;
C c;
ss >> a >> b >> c;
func( a, b, c );
}
}
int main()
{
auto forEach = [](int a, float b, string c){ cout << a << "," << b << "," << c << endl; };
parse<decltype(forEach),int,float,string>( "test.txt", forEach );
return 0;
}
上面的代码与参数的类型无关,但是每行上恰好需要3个值。我希望将其扩展到每行参数个数为
的版本Func
类型推断出例如,我将使案例1看起来像
#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
using namespace std;
template <typename Func>
void parse(const string & filename, Func func){
string line;
// TODO: Get line from file, not cin
// std::ifstream file(filename);
while (std::getline(cin, line)) {
stringstream ss(line);
// TODO Infer from the type Func that it requires
// int, float, string
// Then use a stringstream to parse those values from `line`
// and pass the results to func
func( ... );
}
}
int main()
{
auto forEach = [](int a, float b, string c){ cout << a << "," << b << "," << c << endl; };
parse<decltype(forEach)>( "test.txt", forEach );
return 0;
}
如果这不可能,那么我将接受使用参数包的解决方案。我只是不知道该怎么做。我认为解决方案如下所示:
#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
using namespace std;
template <typename Func, typename ...Args>
void parse(const string & filename, Func func){
string line;
// TODO: Get line from file, not cin
// std::ifstream file(filename);
while (std::getline(cin, line)) {
stringstream ss(line);
// TODO: Use Args to extract parse the appropriate number of
// parameters from `line` and pass the result to `func`
func( ... );
}
}
int main()
{
auto forEach = [](int a, float b, string c){ cout << a << "," << b << "," << c << endl; };
parse<decltype(forEach),int,float,string>( "test.txt", forEach );
return 0;
}
答案 0 :(得分:4)
可以使用operator()
(无重载)或函数指针找到函数特征
template<typename C> struct function_trait : function_trait<decltype(&C::operator())> {};
template <typename C, typename Ret, typename...Args>
struct function_trait<Ret (C::*)(Args...) const> : function_trait<Ret(Args...)> {};
// Handle volatile, reference on this, C-ellipsis combination...
template <typename Ret, typename...Args>
struct function_trait<Ret (*)(Args...)> : function_trait<Ret(Args...)> {};
template <typename Ret, typename...Args>
struct function_trait<Ret (Args...)>
{
using args = std::tuple<Args...>;
};
然后
template <typename Func, typename Tuple>
void parse(std::istream& is, Func func, Tuple t)
{
std::string line;
while (std::getline(is, line)) {
std::stringstream ss(line);
std::apply([&ss](auto&... args){ ((ss >> args), ...);}, t);
std::apply(func, t);
}
}
template <typename Func>
void parse(std::istream& is, Func func)
{
parse(is, func, typename function_trait<Func>::args{});
}
使用方式:
auto forEach = [](int a, float b, string c){ cout << a << "," << b << "," << c << endl; };
parse(std::cin, forEach );
Demo C++17
Demo C++14
对于C ++ 11,您必须实现index_sequence
实用程序。
需要一些转换来处理带有const引用的函子,例如auto forEach = [](int, float, const string&) {/*..*/}
:
template <typename T> struct tuple_decay;
template <typename... Ts> struct tuple_decay<std::tuple<Ts...>>
{
using type = std::tuple<std::decay_t<Ts>...>;
};
,然后替换:
parse(is, func, typename function_trait<Func>::args{});
作者
parse(is, func, typename tuple_decay<typename function_trait<Func>::args>::type{});