I am questioning my solution to the last exercise in Acc开发者_如何学运维elerated C++:
Write a self-reproducing program. Such a program is one that does no input, and that, when run, writes a copy of its own source text on the standard output stream.
My solution:
using std::string;
using std::cout;
using std::endl;
using std::ifstream;
using std::getline;
void selfReproduce16_1()
{
ifstream thisFile("C:\\Users\\Kevin\\Documents\\NetBeansProjects\\Accelerated_C++_Exercises\\Chapter_16.cpp", ifstream::in);
string curLine;
bool foundHeader = false;
while(getline(thisFile, curLine))
{
if(!curLine.compare("void selfReproduce16_1()") || foundHeader)
{
foundHeader = true;
cout << curLine << endl;
}
}
}
This only prints out the source text of the solution (this function). Is this the solution that they had in mind?
I would like a dynamic solution that does not require hard-coding the location of the source file. However, I am not aware of a way to get the location of a source file automatically during runtime.
Another point related to that is the inclusion of "included" files, and (when encountering a function call), automatically obtaining the location of the source file that the function is stored in. To me, this would be a true "self-reproducing" program.
Is this possible in C++? If so, how?
A program that prints itself is called Quine.
I think your solution wouldn't be considered valid: quines usually aren't allowed to read files (nor to get any other kind of input). It's possible to writ a Quine C++ program, here you could find many quine implementations in several languages.
I would like more of a dynamic solution (one that does not require hard-coding the location of the source file)
You know, the arguments in the main function (i.e. argc and argv). Well the first argv is the filename of the program executable. So all you need is to strip the .exe and replace with .cpp. Or, you can extract the folder from the filename and find all source files and output them. I'll let you figure it out. Here is how to print the executable's name:
#include <iostream>
int main(int argc, char** argv) {
std::cout << argv[0] << std::endl;
return 0;
};
Check it out on your system to see what it gives. If it does not display a full-path, don't worry all file opening operations will be from the same starting relative directory, so getting the relative directory of the executable will also give the relative directory to the source (assuming they are in the same folder).
Here's a simple quine I wrote in C++. It doesn't use any input. I think the book was looking for something along these lines, since they explicitly ruled out input (other than that, your solution to read the source file is a good one, and I thought of that too, at first).
https://gist.github.com/3363087
I just finished that lesson. It is not hard to write one that does not open a text file. All you have to do is use a vector of strings push every line of code except for the pushes on the vector you then use to for loops one right after the other in fact you can look at my code and maybe that will be a better explanation. The only thing you might not get is the for loop I used it for(auto b:a) b is an iterator on a and auto is just a quick way of declaring it.Here is the source code.
#include "stdafx.h"
#include <vector>
#include <string>
#include <iostream>
using namespace std;
int main()
{
vector<string> a;
push:
a.push_back("#include \"stdafx.h\"");
a.push_back("#include <vector>");
a.push_back("#include <string>");
a.push_back("#include <iostream>");
a.push_back("using namespace std;");
a.push_back("using namespace std;");
a.push_back("int main()");
a.push_back("{");
a.push_back("vector<string> a;");
a.push_back("push:");
a.push_back("for(auto b:a)");
a.push_back("{");
a.push_back("cout << b << endl;");
a.push_back("if(b == \"push:\")");
a.push_back("{");
a.push_back("for(auto c:a)");
a.push_back("{");
a.push_back("cout << \"a.push_back(\\\"\" << c << \\\"\";\" << endl;");
a.push_back("}");
a.push_back("}");
a.push_back("}");
a.push_back("return 0;");
a.push_back("}");
for(auto b:a)
{
cout << b << endl;
if(b == "push:")
{
for(auto c:a)
{
cout << "a.push_back(\"" << c << "\");" << endl;
}
}
}
return 0;
}
If inline assembly is allowed, put this somewhere in the source file. It relies on the GNU assembler, which makes it possible to embed any data from outside.
#include <cstdint>
extern "C"
{
#if __gnu_linux__
#define BLOB(identifier,filename) \
asm(".pushsection .data\n" \
"\t.local " #identifier "_begin\n" \
"\t.type " #identifier "_begin, @object\n" \
"\t.align 16\n" \
#identifier "_begin:\n" \
"\t.incbin \"" filename "\"\n\n" \
\
"\t.local " #identifier "_end\n" \
"\t.type " #identifier "_end, @object\n" \
"\t.align 1\n" \
#identifier "_end:\n" \
"\t.byte 0\n" \
"\t.popsection\n"); \
\
extern const uint8_t identifier##_begin[];\
extern const uint8_t identifier##_end[]
#elif _WIN32
#define BLOB(identifier,filename) \
asm(".data\n" \
"\t.align 16\n" \
#identifier "_begin:\n" \
"\t.incbin \"" filename "\"\n\n" \
\
"\t.align 1\n" \
#identifier "_end:\n" \
"\t.byte 0\n" \
"\t.text\n"); \
\
extern const uint8_t identifier##_begin[];\
extern const uint8_t identifier##_end[]
#else
#error "Cannot include binary files"
#endif
}
BLOB(source,__FILE__);
Now you have two identifiers source_begin
and source_end
. Loop through the array and print data through your favourite interface.
int main()
{
auto ptr=source_begin;
auto ptr_end=source_end;
while(ptr!=ptr_end)
{
putchar(*ptr);
++ptr;
}
return 0;
}
Demo: http://coliru.stacked-crooked.com/a/d283f6dd9118b164
精彩评论