0

I am questioning my solution to the last exercise in Accelerated 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?

Trojan
  • 2,256
  • 28
  • 40
Kevin
  • 2,617
  • 29
  • 35
  • 4
    Using an `ifstream` violates the "Such a program is one that does no input" rule. – James McNellis Feb 19 '11 at 20:21
  • 1
    Very smart solution. Reading the source file, and outputing it. And that is done after reading "Accelerated C++". I must say, that is too *"accelerated"* solution.:D – Nawaz Feb 19 '11 at 20:24
  • I think there is something really unfair in this "modern" computer programming. I have been a programmer for 20 years and I don't know how to write a Quine and these... these newbies read an Accelerated manual of 350 pages (checked on Amazon) and can write Quines... I'm sad, very sad. So sad that I'll become a cook or something similar and steal the work to someone else! :-) – xanatos Feb 19 '11 at 20:28
  • @James: Ah... I took "no input" to mean "takes no arguments". – Kevin Feb 19 '11 at 20:32

5 Answers5

8

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.

peoro
  • 25,562
  • 20
  • 98
  • 150
4

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).

Mikael Persson
  • 18,174
  • 6
  • 36
  • 52
0

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

Dmitri Shuralyov
  • 1,032
  • 2
  • 13
  • 18
0

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;
}
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

user877329
  • 6,717
  • 8
  • 46
  • 88