问题描述
我想做的是将函数内部的所有输出写入文件。也许我需要一种方法来将 test_func 中的所有输出(不仅是数组)分配给某种变量,以便我可以返回它,但无法弄清楚。
Foo
我需要从 test_func 中获取所有输出,而不是仅从数组a和b中获取,因为我有多种格式不同的函数,都需要使用相同的函数将其写入文件 write_to_file 。
解决方法
这里有一些代码可以按照您想要的方式工作。您必须用其中一个文件流替换std::cout
当前的rdbuf()
,然后再将其重置:
void write_to_file(function<void()>test_func) {
ofstream ofile;
ofile.open("abc.txt");
std::streambuf* org = cout.rdbuf(); // Remember std::cout's old state
cout.rdbuf(ofile.rdbuf()); // Bind it to the output file stream
test_func(); // Simply call the anonymous function
cout.rdbuf(org); // Reset std::cout's old state
ofile.close();
}
在这里您可以看到它按预期运行: Demo
要克服函数签名变化的问题,可以使用委托的lambda函数:
void test_func2(double a,int b) {
cout << a << " * " << b << " = " << (a * b) << endl;
}
int main() {
// Create a lambda function that calls test_func2 with the appropriate parameters
auto test_func_wrapper = []() {
test_func2(0.356,6);
};
write_to_file(test_func_wrapper); // <<<<< Pass the lambda here
// You can also forward the parameters by capturing them in the lambda definition
double a = 0.564;
int b = 4;
auto test_func_wrapper2 = [a,b]() {
test_func2(a,b);
};
write_to_file(test_func_wrapper2);
return 0;
}
您甚至可以使用一个小的帮助程序类来做到这一点,该类可以概括任何std::ostream
类型的情况:
class capture {
public:
capture(std::ostream& out_,std::ostream& captured_) : out(out_),captured(captured_),org_outbuf(captured_.rdbuf()) {
captured.rdbuf(out.rdbuf());
}
~capture() {
captured.rdbuf(org_outbuf);
}
private:
std::ostream& out;
std::ostream& captured;
std::streambuf* org_outbuf;
};
void write_to_file(function<void()>test_func)
{
ofstream ofile;
ofile.open("abc.txt");
{
capture c(ofile,cout); // Will cover the current scope block
test_func();
}
ofile.close();
}
关于您的comment:
可以,但是我将需要一些东西来存储这些cout,或者也许还有另一种完全不同的方法,而不是使用test_func()进行处理?
我们现在拥有一切准备就绪
#include <iostream>
#include <fstream>
#include <functional>
#include <string>
#include <sstream>
using namespace std;
void test_func1(const std::string& saySomething) {
cout << saySomething << endl;
}
void test_func2(double a,int b) {
cout << "a * b = " << (a * b) << endl;
}
class capture {
public:
capture(std::ostream& out_,org_outbuf(captured_.rdbuf()) {
captured.rdbuf(out.rdbuf());
}
~capture() {
captured.rdbuf(org_outbuf);
}
private:
std::ostream& out;
std::ostream& captured;
std::streambuf* org_outbuf;
};
int main() {
std::string hello = "Hello World";
auto test_func1_wrapper = [hello]() {
test_func1(hello);
};
double a = 0.356;
int b = 6;
auto test_func2_wrapper = [a,6);
};
std::stringstream test_func1_out;
std::stringstream test_func2_out;
std::string captured_func_out;
{ capture c(test_func1_out,cout);
test_func1_wrapper();
}
{ capture c(test_func2_out,cout);
test_func2_wrapper();
}
captured_func_out = test_func1_out.str();
cout << "test_func1 wrote to cout:" << endl;
cout << captured_func_out << endl;
captured_func_out = test_func2_out.str();
cout << "test_func2 wrote to cout:" << endl;
cout << captured_func_out << endl;
}
当然还有 Demo 。
,行ofile << test_func();
表示被调用的test_func();
的返回值被定向到该流。它对调用的函数中完成的操作没有任何作用。不过,您可以将流传递给该函数。
void test_func(ostream& outs)
{
outs << "Below is the result: "<< endl;
}
并使用cout
或ofile
-任何ostream
作为参数调用它。
void write_to_file(function<void(ostream&)>test_func)
{
ofstream ofile;
ofile.open("abc.txt");
test_func(ofile); // This is not allowed
ofile.close();
}
但是,如果您想要功能作为流操纵器的行为,则必须设计一个合适的运算符。
ostream& operator<< (ostream& o,void(*func)(ostream&) )
{
func(o);
return o;
}
然后您可以编写类似的内容
cout << test_func << " That's all,folks\n";
请注意,此处未调用test_func
,其ID用作表达式会导致将函数的地址传递给operator<<
。
实时流操纵器(例如https://en.cppreference.com/w/cpp/io/manip/setw)不是作为函数实现的,而是作为功能对象的模板实现的,其中setw的参数位于行内:
is >> std::setw(6) >> arr;
实际上是构造函数的参数
,我想做的是将函数内部的所有输出写入文件。
我经常使用std :: stringstream充当文本的临时存储库,即ss将所有输出保存并捆绑到“缓冲区”(文本字符串)中,以延迟输出到文件。
对于您的test_func,您可以添加ss参考参数:
void test_func(std::stringsteam& ss)
{
int a[] = {20,42,41,40};
int b[] = {2,4,2,1};
cout << "Below is the result: "<< endl;
for (int i=0; i<4; i++){
ss << "***********************" << endl;
ss << a[i] << " : " << b[i] <<endl;
ss << "-----------------------" << endl;
}
}
std :: stringstream本质上是基于ram的ofile(没有任何硬盘开销)。
因此您可以运行许多test_func,将所有输出集中到一个ss中,然后将ss内容清空到一个文件中。
或者,您可以调用1个test_func,将/ ss内容输出/附加到您的文件中,然后清除ss以供重用。
您还可以调用1个测试函数,将ss内容输出到唯一的文件,然后清除ss并执行下一个测试函数,等等。
注意:a)std :: stringstream使用一个std :: string作为工作缓冲区,b)std :: string将其数据保留在动态内存中。我很少担心ss有多大。但是,如果您担心并有所估计,则可以轻松地使用reserve来设置字符串大小。知道此大小后,您就可以计划控制非常大的输出文件。
接下来,考虑将stringstream保留在test_func之外,而应将其保留在外部数据收集函数中:
void write_to_file(function<void()>test_func)
{
std::stringstream ss; // temporary container
test_func(ss); // add contributions
test_func2(ss); // add contributions
test_func3(ss); // add contributions
// ...
test_funcN(ss); // add contributions
// when all testing is complete,output concatenated result to single file
ofstream ofile;
ofile.open("abc.txt");
ofile << ss.str();
ofile.close();
}
int main()
{
write_to_file(test_func);
return 0;
}
注意:要清空ss,我使用2个步骤:
void ssClr(stringstream& ss) { ss.str(string()); ss.clear(); }
// clear data clear flags
注意:我将编码工作封装到一个或多个c ++类中。在我的代码中,ss对象被声明为类的数据属性,因此该类的所有函数属性都可以访问,包括每个test_funci(即,无需传递ss)