1

我正在开发一个应用程序,我的想法是将“应用程序”存储在文件中,例如可执行文件。现在我有了:AppWriter.c

#include <vector>
#include <time.h>
#include <functional>
struct PROGRAM
{
    std::vector<int> RandomStuff;
    std::vector<std::function<void()>> Functions;
    std::function<void()> MAIN;
} CODED;
void RANDOMFUNC()
{
    srand(time(NULL));
    for(int i = 0; i < 40; i++)
        CODED.RandomStuff.push_back(rand() % 254);
}
void LOGARRAY()
{
    for(int i = 0; i < CODED.RandomStuff.size(); i++)
        std::cout << "["<< i + 1 <<"]: "<< CODED.RandomStuff[i] << std::endl;
}
void PROGRAMMAIN()
{
    std::cout << "Hello i call random function!" << std::endl;
    CODED.Functions[0]();
    CODED.Functions[1]();
}
void main()
{
    CODED.MAIN = PROGRAMMAIN;
    CODED.Functions.push_back(RANDOMFUNC);
    CODED.Functions.push_back(LOGARRAY);
    std::cout << "Testing MAIN" << std::endl;
    CODED.MAIN();
    FILE *file = fopen("TEST_PROGRAM.TRI","wb+");
    fwrite(&CODED,sizeof(CODED),1,file);
    fclose(file);
    std::cout << "Program writted correctly!" << std::endl;
    _sleep(10000);
}

应用阅读器.c

#include <iostream>
#include <vector>
#include <time.h>
#include <functional>
struct PROGRAM
{
    std::vector<int> RandomStuff;
    std::vector<std::function<void()>> Functions;
    std::function<void()> MAIN;
} DUMPED;
void main()
{
    FILE *file = fopen("TEST_PROGRAM.TRI","rb+");
    fseek(file,0,SEEK_END);
    int program_len = ftell(file);
    rewind(file);
    fread(&DUMPED,sizeof(PROGRAM),1,file);
    std::cout 
        << "Function array size: " << DUMPED.Functions.size() << std::endl
        << "Random Stuff Array size: " << DUMPED.RandomStuff.size() << std::endl;
    DUMPED.MAIN();
}

当我运行 AppReader 时,函数不起作用(也许为什么 std::function 它就像 void 指针?),但是在数组中或者如果我添加变量,我可以使用调试器看到数据被正确存储(为此我尝试了函数向量) ,但无论什么不起作用,都会在功能文件上抛出我的错误。¿任何想法我怎么能做到这一点?

4

2 回答 2

2

这永远行不通。完全没有。曾经。std::function 是一种复杂类型。二进制读取和写入不适用于复杂类型。他们永远做不到。您必须要求使用预定义的可序列化格式的函数,例如 LLVM IR。

于 2013-09-30T16:45:12.123 回答
1

您的问题是您正在存储有关存在于一个可执行文件中的函数的信息,然后尝试在单独的可执行文件中运行它们。除此之外,您的代码确实有效,但正如 DeadMG 所说,您不应该将复杂类型存储在文件中。以下是我如何修改您的代码以证明您的代码在单个可执行文件中运行时有效:

#include <iostream>
#include <vector>
#include <time.h>
#include <functional>
struct PROGRAM
{
  std::vector<int> RandomStuff;
  std::vector<std::function<void()>> Functions;
  std::function<void()> MAIN;
} CODED;
void RANDOMFUNC()
{
  srand(time(NULL));
  for(int i = 0; i < 40; i++)
     CODED.RandomStuff.push_back(rand() % 254);
}
void LOGARRAY()
{
  for(int i = 0; i < CODED.RandomStuff.size(); i++)
     std::cout << "["<< i + 1 <<"]: "<< CODED.RandomStuff[i] << std::endl;
}
void PROGRAMMAIN()
{
  std::cout << "Hello i call random function!" << std::endl;
  CODED.Functions[0]();
  CODED.Functions[1]();
}
int main()
{
  CODED.MAIN = PROGRAMMAIN;
  CODED.Functions.push_back(RANDOMFUNC);
  CODED.Functions.push_back(LOGARRAY);
  std::cout << "Testing MAIN" << std::endl;
  CODED.MAIN();
  FILE *file = fopen("TEST_PROGRAM.TRI","wb+");
  fwrite(&CODED,sizeof(CODED),1,file);
  fclose(file);
  std::cout << "Program writted correctly!" << std::endl;
  //      _sleep(10000);

  std::cout << "---------------------\n";

  file = fopen("TEST_PROGRAM.TRI","rb+");
  fseek(file,0,SEEK_END);
  int program_len = ftell(file);
  rewind(file);
  fread(&CODED,sizeof(PROGRAM),1,file);
  std::cout
     << "Function array size: " << CODED.Functions.size() << std::endl
     << "Random Stuff Array size: " << CODED.RandomStuff.size() << std::endl;
  CODED.MAIN();
}

问题不在于您本身通过二进制读/写来存储复杂类型。(尽管这是一个问题,但这不是您发布此问题的问题的原因。)您的问题是您的数据结构正在存储有关“编写器”可执行文件中存在的函数的信息。这些相同的功能甚至不存在于您的“阅读器”可执行文件中,但即使它们存在,它们也可能不会位于相同的地址。您的数据结构通过 std::function 存储指向您的“编写器”可执行文件中函数所在地址的指针。当您尝试在“阅读器”可执行文件中调用这些不存在的函数时,您的代码会愉快地尝试调用它们,但您会遇到段错误(或您的操作系统给出的任何错误),因为那

现在关于以二进制格式将复杂类型(例如 std::vector)直接写入文件:这样做在上面的示例代码中“有效”只是因为 std::vectors 的二进制副本具有指针,一旦读回在,仍然指向您写出的原始 std::vectors 中的有效数据。请注意,您没有编写 std::vector 的实际数据,您只编写了它们的元数据,其中可能包括向量的长度、当前为向量分配的内存量以及指向向量数据的指针。当你读回来时,元数据是正确的,除了一件事:其中的任何指针都指向你写数据时有效的地址,但现在可能无效。在上面的示例代码的情况下,指针最终指向原始向量中的相同(仍然有效)数据。但是这里仍然存在一个问题:您现在拥有多个认为自己拥有该内存的 std::vector。当其中一个被删除时,它将删除另一个向量期望仍然存在的内存。而当另一个向量被删除时,会导致双重删除。这为各种 UB 打开了大门。例如,到那时该内存可能已分配给其他用途,现在第二次删除将删除该其他用途的内存,否则该内存尚未分配给其他用途,第二次删除可能会破坏堆。为了解决这个问题,你必须序列化每个向量的本质,而不是它们的二进制表示,并且在读回它时,你必须重建一个等效的副本,

于 2013-09-30T16:51:19.727 回答