Класс type_info

67
08 апреля 2021, 08:40

Записать в файл имя функции, версию и параметр. Если с того момента исходный код функции изменился, вернуть сообщение что "версия функций не совпадают".

Например есть 100 функций. Для проверки имен и версии я думаю воспользоваться классом typeid (в первый раз). Поэтому из функций делаю классы. Пример как я думаю реализовать:

class Base { void f(int seed) = 0; }
class C1   : public Base { void f(int seed) override; }
class C2   : public Base { void f(int seed) override; }
// ...
class C100 : public Base { void f(int seed) override; }
void saveToFile(Base *b, int seed) {
    // псевдокод. Открываю и записываю в файл
    auto t = typeid(*b);
    Fout << t.name()      // имя функции
         << t.hash_code() // версия функции
         << seed;         // параметр функции
}
void readFromFile(Base *b, int seed) {
    // тут не знаю еще как делать.
}

Вопросы:

  1. Можно ли использовать typeid() для сохранения и сравнения типов классов?
  2. Будет ли корректно работать, если я буду сохранять/открывать в разных версиях компиляторов С++?
  3. Как красиво реализовать readFromFile?
  4. Советы, предложения, замечания?

UDP: ответ - нельзя использовать typeid(). А как лучше сделать, что бы особо не запариваться. Что бы автоматически посчитать хеш функции/класса и сравнить имеются ли изменения. И желательно (но не обязательно) мультиплатформенность/на разных компиляторах

Answer 1

По моим тестам, в Visual C++ результат вывода type_info.hash_code() не меняется при внесении изменений в код функций класса (он меняется только при изменении его имени). Так что идея уже сомнительная.

Хэш самого тела функции можно получить, скажем, как-то так:

#include <stdio.h>
#include <stdint.h>
#include <tchar.h>
#include <Windows.h>
#include <DbgHelp.h>
#pragma comment(lib, "Dbghelp.lib")
struct Function {
    const char* name;   
    uint64_t addr;
    unsigned int size;
    bool success;
};
BOOL CALLBACK EnumSymProc(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID context) {
    Function* pfstruct = (Function*)context; 
    if (strcmp(pSymInfo->Name, pfstruct->name) == 0) {      
        pfstruct->addr = pSymInfo->Address; 
        pfstruct->size = SymbolSize;
        pfstruct->success = true;
        return FALSE; //закончить поиск
    }
    return TRUE; //продолжить поиск
}
bool GetFuncBounds(const char* fname, uint64_t& addr, unsigned int& size) {
    bool ret;
    Function fstruct;
    fstruct.name = fname;
    fstruct.size = 0;
    fstruct.success = false;
    HANDLE hProcess = GetCurrentProcess(); //текущий процесс
    char Mask[] = "*!*"; 
    BOOL status;
    status = SymInitialize(hProcess, NULL, TRUE); //загрузка символов
    if (status == FALSE)
    {
        printf("SymInitialize failed. Error code: 0x%x\n", (UINT)GetLastError());
        return false;
    }
    //поиск символов
    if (SymEnumSymbols(hProcess, 0, Mask, &EnumSymProc, (void*)&fstruct))
    {
        if (fstruct.success != false) {
            //возвращаем адрес и размер функции
            addr = fstruct.addr;
            size = fstruct.size; 
            ret = true;
        }
        else {
            printf("Symbol [%s] not found\n", fname);
            ret = false;
        }
    }
    else
    {
        printf("SymEnumSymbols failed. Error code: 0x%x\n", (UINT)GetLastError());
        ret = false;
    }
    SymCleanup(hProcess);
    return ret;
}
uint32_t GetMemHash(uint64_t addr, int size) {
    uint32_t hash = 1234;
    uint32_t k = 9870;
    for (int i=0; i < size; i+=sizeof(uint32_t)) {
        uint32_t* p = (uint32_t*)((char*)addr + i);
        hash = hash * k + (*p);
    }
    hash += size;
    return hash;
}
//**********************************
void Func() {   
    printf("Hello, World!\n");  
}
int _tmain(int argc, _TCHAR* argv[])
{   
    uint64_t addr = 0;
    unsigned int size = 0;
    UINT hash;  
    if (GetFuncBounds("Func", addr, size)) {
        hash = GetMemHash(addr, size);
        printf("Hash: 0x%x\n", (UINT)hash);
    }
    else {
        printf("Cannot calculate hash!\n");
    }
    getchar();
    return 0;
}

Но я не думаю, что это поможет решить задачу. Значение этого хэша в большинстве случаев будет меняться даже при простой пересборке проекта без изменений, так как компилятор каждый раз генерирует новые адреса объектов, что приводит к различиям в машинном коде.

Как же решить задачу? Если вам нужно отслеживать изменения в исходном коде, то и зайти логичнее со стороны исходного кода. Сделайте, чтобы тело всех функций, изменения в которых надо отслеживать, было вынесено в отдельный файл, подключаемый через include. Напишите программу, которая будет считать хэши от содержимого этих файлов, и записывать результат в XML-файл. Прикрутите ее к вашим инструментам сборки (например, в Visual Studio - Custom build step), и распространяйте получаемый XML-файл с хэшами функций вместе с программой, тогда вы всегда можете определить, какая функция изменилась. Для проверки, что XML-файл действительно соответствует текущему бинарнику, можно включить в него и хэш от бинарника.

Answer 2

Насколько я понимаю typeid() вычисляется на этапе компиляции, поэтому:

auto t = typeid(*b); 

t - будет всегда хранить данные о типе Base.

Метод name может возвращать разные имена при использование разных компиляторов.

READ ALSO
Почему не срабатывает submitHandler?

Почему не срабатывает submitHandler?

Скажите пожалуйста, почему не появляется alert после заполнения формы и нажатия на кнопку

106
правильно кодировать домен

правильно кодировать домен

есть домен http://wwwschreinerei

96