自我复制程序

时间:2011-02-19 20:19:10

标签: c++ dynamic runtime quine

我在质疑我对Accelerated C ++上一次练习的解决方案:

  

编写自我复制程序。这样的程序是没有输入的程序,并且在运行时,在标准输出流上写入自己的源文本的副本。

我的解决方案:

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

    }

}

这只打印出解决方案的源文本(此功能)。这是他们想到的解决方案吗?

我想要一个动态解决方案,不需要对源文件的位置进行硬编码。但是,我不知道在运行时自动获取源文件位置的方法。

与此相关的另一点是包含“包含”文件,并且(当遇到函数调用时),自动获取存储函数的源文件的位置。对我来说,这将是一个真实的“自我复制“程序。

这在C ++中是否可行?如果是这样,怎么样?

5 个答案:

答案 0 :(得分:8)

自行打印的程序称为Quine

我认为您的解决方案不会被视为有效:通常不允许quines读取文件(也不允许获取任何其他类型的输入)。编写Quine C ++程序是可能的,here你可以找到许多语言的许多quine实现。

答案 1 :(得分:4)

  
    

我想要更多动态解决方案(不需要对源文件的位置进行硬编码)

  

你知道,main函数中的参数(即argc和argv)。那么第一个argv是程序可执行文件的文件名。所以你需要的是剥离.exe并替换为.cpp。或者,您可以从文件名中提取文件夹并查找所有源文件并输出它们。我会让你搞清楚的。以下是打印可执行文件名称的方法:

#include <iostream>

int main(int argc, char** argv) {
  std::cout << argv[0] << std::endl;
  return 0;
};

在您的系统上查看它以查看它提供的内容。如果它没有显示完整路径,请不要担心所有文件打开操作都来自相同的起始相对目录,因此获取可执行文件的相对目录也会将相对目录提供给源(假设它们位于同一文件夹)。

答案 2 :(得分:0)

这是我用C ++编写的一个简单的quine。它不使用任何输入。我认为这本书正在寻找这些方面的东西,因为他们明确排除了输入(除此之外,您阅读源文件的解决方案是一个很好的解决方案,起初我也想到了这一点。)

https://gist.github.com/3363087

答案 3 :(得分:0)

我刚完成那一课。编写一个不打开文本文件的文件并不难。所有你需要做的就是使用一个字符串向量推送每一行代码,除了推动向量然后用于一个接一个的循环,实际上你可以查看我的代码,也许这将是一个更好的解释。你可能得不到的唯一东西是我用它的for循环(auto b:a)b是a上的迭代器而auto是一种快速声明它的方法。这是源代码。

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

答案 4 :(得分:0)

如果允许内联汇编,请将其放在源文件中的某处。它依赖于GNU汇编程序,可以从外部嵌入任何数据。

#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__);

现在您有两个标识符source_beginsource_end。循环遍历数组并通过您喜欢的界面打印数据。

int main()
    {
    auto ptr=source_begin;
    auto ptr_end=source_end;
    while(ptr!=ptr_end)
        {
        putchar(*ptr);
        ++ptr;
        }
    return 0;
    }

演示:http://coliru.stacked-crooked.com/a/d283f6dd9118b164