通过二进制读取运行std :: function getted

时间:2013-09-30 16:34:30

标签: c++ binaryfiles std-function

我正在开发一个应用程序,我的想法是将“apps”存储在文件中,比如可执行文件。现在我有: 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);
}

AppReader.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指针?),但是在数组中或者如果我添加变量我可以看到调试器数据存储正确(为此我尝试了向量功能),但无论什么不起作用抛出我的功能文件错误。 ¿任何想法我怎么能这样做?

2 个答案:

答案 0 :(得分:2)

这永远无法奏效。完全没有。永远。 std :: function是一个复杂的类型。二进制读取和写入不适用于复杂类型。他们永远不可能。您必须以预定义的可序列化格式请求函数,例如LLVM IR。

答案 1 :(得分: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存储指向“writer”可执行文件中存在函数的地址的指针。当您尝试在“reader”可执行文件中调用这些不存在的函数时,您的代码会愉快地尝试调用它们,但是您会得到一个段错误(或者您的操作系统提供的任何错误),因为这不是您的“读者”中有效函数的开头'可执行。

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