Анализировать некоторые заголовки C ++ и динамически генерировать другие заголовки, содержащие шаблонный код

Ссылка на рассматриваемый запрос на перенос.

Я поддерживаю библиотеку C ++ DLL / Game Maker, которая создает среду, позволяющую напрямую вызывать любой сценарий / функцию производителя игр из библиотеки DLL C ++. Он делает это, помещая все вызовы функций DLL во второй поток, который затем передает вызовы функций исходному потоку, после чего создатель игры выполняет вызовы и отправляет обратно любые возвращаемые значения.

Главной проблемой, к которой это привело, было множество функций-оберток и других шаблонов. Каждый раз, когда я добавлял поддержку функции создателя игр, мне приходилось идти и писать объявление заголовка, писать функцию для импорта идентификатора функции Game Maker и писать фактическое объявление функции, что несколько сбивает с толку вызов для добавления вызова функции. struct к связям потоков. Точно так же были и другие оболочки для экспорта данных для АКТУАЛЬНЫХ вызовов DLL и последующего вызова их в потоке DLL при получении сообщения.

Все это было лишь предысторией моей проблемы, которую решили мои недавние изменения кода. Я не ожидаю, что кто-то будет проверять этот интерфейсный код DLL (хотя я всегда открыт для улучшений!).

Чтобы решить эту проблему, я написал программу под названием «dynamicWrapperGeneration.c», которая при запуске анализирует различные заголовки, которые раньше содержали весь этот шаблон, но теперь не содержат ничего, кроме объявлений функций. Затем он генерирует необходимые файлы шаблонов. Я бы хотел просмотреть именно этот файл.

Обычно я бы не публиковал здесь такую ​​небольшую задачу (не буду лгать в какой-то момент, я думал разместить здесь всю библиотеку из 3000 строк в какой-то момент), но этот исполняемый файл для меня является первым в своем роде. Я никогда раньше не писал ничего подобного, поэтому даже не знаю, что вызов это не говоря уже о том, являются ли мои методы слишком грубыми и неуклюжими. В какой-то момент этот исполняемый файл будет фактически расширен для создания дополнительных скриптов Game Maker, поэтому я хочу убедиться, что иду по правильному пути с тем, что я сделал в настоящее время, чтобы таким образом я мог начать вести себя в правильном направлении. .

Еще одна вещь, которую следует упомянуть, заключается в том, что человек, который в первую очередь владеет репо, которое я запрашиваю, не знает C ++ вообще, поэтому все, что может помочь сделать код более удобным для новичков в программном обеспечении C ++, будет оценено.

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

#include <stdio.h>
#include <malloc.h>
#include <string.h>
//rs

int walk_function(FILE* inputFile, FILE* outputFile, FILE* outputFile2)
{
    int began = 0;
    int ended = 0;
    int c = fgetc(inputFile);

    while (!ended && c != EOF)
    {
        if (c == ' ')
        {
            ended = began;
            began = 1;
        }
        c = fgetc(inputFile);
    }

    if (c == EOF)
        return c == EOF;

    began = 0;
    ended = 0;

    char* functionName = (char*)malloc(sizeof(char));
    functionName[0] = 0;
    int functionNameLength = 0;

    while (!ended && c != EOF)
    {
        if (c == '(')
        {
            ended = 1;
        } else
        {
            char* tempFunctionName = (char*)malloc((functionNameLength+2)*sizeof(char));
            strcpy(tempFunctionName, functionName);
            tempFunctionName[functionNameLength] = c;
            tempFunctionName[functionNameLength+1] = 0;
            free(functionName);
            functionName = tempFunctionName;
            functionNameLength++;
        }
        c = fgetc(inputFile);
    }

    fprintf(outputFile, "GMEXPORT double get_%s()n", functionName);
    fprintf(outputFile, "{n");
    fprintf(outputFile, "    return (double)(int)%s;n", functionName);
    fprintf(outputFile, "}n");

    fprintf(outputFile2, "if (function_to_call == (void*)%s)n", functionName);
    fprintf(outputFile2, "    function_return_value = %s(", functionName);

    int argumentCount = 0;
    int waitingTillComma = 0;
    ended = 0;

    while (!ended && c != EOF)
    {
        if (c == ')')
        {
            ended = 1;
        } else if (c == 'd' && !waitingTillComma)
        {
            fprintf(outputFile2, "dll_input[%u].number", argumentCount);
            waitingTillComma = 1;
            argumentCount++;
        } else if (c == 'c' && !waitingTillComma)
        {
            fprintf(outputFile2, "dll_input[%u].text", argumentCount);
            waitingTillComma = 1;
            argumentCount++;
        } else if (c == ',' && waitingTillComma)
        {
            fprintf(outputFile2, ", ");
            waitingTillComma = 0;
        }
        c = fgetc(inputFile);
    }

    fprintf(outputFile2, ");n");
    return c == EOF;
}

int walk_GML_function(FILE* inputFile, FILE* outputFile, FILE* outputFile2)
{
    int ended = 0;
    int functionType = 2;
    int c = fgetc(inputFile);

    while (!ended && c != EOF)
    {
        if (c == ' ')
        {
            ended = 1;
        } else if (c == 'v' && functionType == 2)
        {
            functionType = 0;
        } else if (c == 'd' && functionType == 2)
        {
            functionType = 1;
        }
        c = fgetc(inputFile);
    }

    if (c == EOF)
        return c == EOF;

    ended = 0;

    char* functionName = (char*)malloc(sizeof(char));
    functionName[0] = 0;
    int functionNameLength = 0;

    while (!ended && c != EOF)
    {
        if (c == '(')
        {
            ended = 1;
        } else
        {
            char* tempFunctionName = (char*)malloc((functionNameLength+2)*sizeof(char));
            strcpy(tempFunctionName, functionName);
            tempFunctionName[functionNameLength] = c;
            tempFunctionName[functionNameLength+1] = 0;
            free(functionName);
            functionName = tempFunctionName;
            functionNameLength++;
        }
        c = fgetc(inputFile);
    }

    fprintf(outputFile, "ADD_FUNCTION(%s)n", functionName);

    int argumentCount = 0;
    int* argumentType = NULL;
    int waitingTillComma = 0;
    ended = 0;

    while (!ended && c != EOF)
    {
        if (c == ')')
        {
            ended = 1;
        } else if (c == 'd' && !waitingTillComma)
        {
            int* argumentTypeTemp = (int*)malloc((argumentCount+1)*sizeof(int));
            memcpy(argumentTypeTemp, argumentType, argumentCount*sizeof(int));
            argumentTypeTemp[argumentCount] = 0;
            free(argumentType);
            argumentType = argumentTypeTemp;
            waitingTillComma = 1;
            argumentCount++;
        } else if (c == 'c' && !waitingTillComma)
        {
            int* argumentTypeTemp = (int*)malloc((argumentCount+1)*sizeof(int));
            memcpy(argumentTypeTemp, argumentType, argumentCount*sizeof(int));
            argumentTypeTemp[argumentCount] = 1;
            free(argumentType);
            argumentType = argumentTypeTemp;
            argumentCount++;
        } else if (c == ',' && waitingTillComma)
        {
            waitingTillComma = 0;
        }

        c = fgetc(inputFile);
    }

    if (functionType == 0)
    {
        fprintf(outputFile2, "void %s(", functionName);
    } else if (functionType == 1)
    {
        fprintf(outputFile2, "double %s(", functionName);
    }

    for (int i = 0; i < argumentCount; i++)
    {
        if (argumentType[i] == 0)
        {
            fprintf(outputFile2, "double input%u", i);
        } else if (argumentType[i] == 1)
        {
            fprintf(outputFile2, "const char* input%u", i);
        }

        if (i+1 < argumentCount)
        {
            fprintf(outputFile2, ", ");
        }
    }

    fprintf(outputFile2, ")n");
    fprintf(outputFile2, "{n");

    if (functionType == 0)
    {
        fprintf(outputFile2, "    addDelayedFunctionCall(FP_%s, 0", functionName);
    } else if (functionType == 1)
    {
        fprintf(outputFile2, "    return addDelayedFunctionCall(FP_%s, 1", functionName);
    }

    for (int i = 0; i < argumentCount; i++)
    {
        fprintf(outputFile2, ", input%u", i);
    }

    fprintf(outputFile2, ");n");
    fprintf(outputFile2, "}n");
    return c == EOF;
}

void parse_GML_header(FILE* outputFile, char* inputFileName)
{
    FILE* inputFile = fopen(inputFileName, "r");

    while (1)
    {
        if (walk_GML_function(inputFile, outputFile, outputFile))
        {
            return;
        }
    }
}

void parse_GML_libraries()
{
    char* outputFileName = "gameMakerGenLibrary.hpp";
    FILE* outputFile = fopen(outputFileName, "w");

    parse_GML_header(outputFile, "gameMakerFunctions\3DGraphics\d3d_model.hpp");
    parse_GML_header(outputFile, "gameMakerFunctions\3DGraphics\d3d_primitive.hpp");
    parse_GML_header(outputFile, "gameMakerFunctions\3DGraphics\d3d_shape.hpp");
    parse_GML_header(outputFile, "gameMakerFunctions\3DGraphics\d3d_transform.hpp");

    parse_GML_header(outputFile, "gameMakerFunctions\gameGraphics\fontsAndText.hpp");

    parse_GML_header(outputFile, "gameMakerFunctions\userInteraction\mouse.hpp");
    parse_GML_header(outputFile, "gameMakerFunctions\userInteraction\keyboard.hpp");
    parse_GML_header(outputFile, "gameMakerFunctions\userInteraction\joystick.hpp");

    parse_GML_header(outputFile, "gameMakerFunctions\gamePlay\rooms.hpp");
}

int main()
{
    char* inputFileName = "gameLoop.hpp";
    FILE* inputFile = fopen(inputFileName, "r");

    char* outputFileName = "getGameLoop.hpp";
    FILE* outputFile = fopen(outputFileName, "w");

    char* outputFileName2 = "callGameLoop.hpp";
    FILE* outputFile2 = fopen(outputFileName2, "w");

    while (1)
    {
        if (walk_function(inputFile, outputFile, outputFile2))
        {
            break;
        }
    }

    parse_GML_libraries();
}

Пример ввода

Пример GameLoop.hpp:

GMEXPORT double gameLoopInit(char* program_directory);
GMEXPORT double gameLoopStep();
GMEXPORT double gameLoopDraw();

Пример gameMakerFunctions 3DGraphics d3d_model.hpp:

double d3d_model_create();
void d3d_model_destroy(double ind);
void d3d_model_load(double ind, const char* fname);
void d3d_model_draw(double ind, double x, double y, double z, double texid);
void d3d_model_primitive_begin(double ind, double kind);
void d3d_model_vertex_texture(double ind, double x, double y, double z, double xtex, double ytex);
void d3d_model_primitive_end(double ind);

Пример вывода

Пример GetGameLoop.hpp:

GMEXPORT double get_gameLoopInit()
{
    return (double)(int)gameLoopInit;
}
GMEXPORT double get_gameLoopStep()
{
    return (double)(int)gameLoopStep;
}
GMEXPORT double get_gameLoopDraw()
{
    return (double)(int)gameLoopDraw;
}

Пример callGameLoop.hpp:

if (function_to_call == (void*)gameLoopInit)
    function_return_value = gameLoopInit(dll_input[0].text);
if (function_to_call == (void*)gameLoopStep)
    function_return_value = gameLoopStep();
if (function_to_call == (void*)gameLoopDraw)
    function_return_value = gameLoopDraw();

Пример gameMakerGenLibrary.hpp:

ADD_FUNCTION(d3d_model_create)
double d3d_model_create()
{
    return addDelayedFunctionCall(FP_d3d_model_create, 1);
}
ADD_FUNCTION(d3d_model_destroy)
void d3d_model_destroy(double input0)
{
    addDelayedFunctionCall(FP_d3d_model_destroy, 0, input0);
}
ADD_FUNCTION(d3d_model_load)
void d3d_model_load(double input0, const char* input1, const char* input2)
{
    addDelayedFunctionCall(FP_d3d_model_load, 0, input0, input1, input2);
}
ADD_FUNCTION(d3d_model_draw)
void d3d_model_draw(double input0, double input1, double input2, double input3, double input4)
{
    addDelayedFunctionCall(FP_d3d_model_draw, 0, input0, input1, input2, input3, input4);
}
ADD_FUNCTION(d3d_model_primitive_begin)
void d3d_model_primitive_begin(double input0, double input1)
{
    addDelayedFunctionCall(FP_d3d_model_primitive_begin, 0, input0, input1);
}
ADD_FUNCTION(d3d_model_vertex_texture)
void d3d_model_vertex_texture(double input0, double input1, double input2, double input3, double input4, double input5)
{
    addDelayedFunctionCall(FP_d3d_model_vertex_texture, 0, input0, input1, input2, input3, input4, input5);
}
ADD_FUNCTION(d3d_model_primitive_end)
void d3d_model_primitive_end(double input0)
{
    addDelayedFunctionCall(FP_d3d_model_primitive_end, 0, input0);
}

Предположим, что все не включенные файлы либо пусты, либо содержат только пробелы. Я не планировал пропускать файлы, но другие заголовки «gameMakerFunctions * .hpp» используются идентично.

2 ответа
2

malloc vs. realloc

Вместо того, чтобы выделять, копировать, добавлять … … рассмотрите возможность перераспределения, добавления.

// char* tempFunctionName = (char*)malloc((functionNameLength+2)*sizeof(char));
// strcpy(tempFunctionName, functionName);
// tempFunctionName[functionNameLength] = c;
// tempFunctionName[functionNameLength+1] = 0;
// free(functionName);
// functionName = tempFunctionName;
// functionNameLength++;

functionName = realloc(functionName, functionNameLength + 2);
functionName[functionNameLength++] = c;
functionName[functionNameLength] = '';

// Or better, test allocation success

char* tempFunctionName = realloc(functionName, functionNameLength + 2);
if (tempFunctionName == NULL) Handle_OutOfMemory_TBD_code();
functionName = tempFunctionName;
functionName[functionNameLength++] = c;
functionName[functionNameLength] = '';

Подумайте о том, чтобы перераспределение было больше, чем 1.


Вместо размера по типу, размер по указанной переменной

// ptr = malloc(n * sizeof(type_of_what_p_points_to));
ptr = malloc(sizeof *ptr * n);

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

Начиная умножение с sizeof *ptr застраховать хотя бы size_t используется математика. Полезно при более сложных вычислениях.

В C приведение не требуется. Кодируйте по мере необходимости, если код также предназначен для C ++.


Альтернативный while

Используйте 1 fgetc()

// int c = fgetc(inputFile);
// while (!ended && c != EOF) {
//    ...
//    c = fgetc(inputFile);
//}

int c;
while ((c = fgetc(inputFile)) != EOF && !ended) {
    ...
}

bool vs. int

bool имеет смысл здесь

#include <stdbool.h>

//int began = 0;
//int ended = 0;
bool began = false;
bool ended = false;

Может быть fscanf()

Код выглядит так, будто ищет 2 ' ':

//int began = 0;
//int ended = 0;
//int c = fgetc(inputFile);
//while (!ended && c != EOF) {
//    if (c == ' ') {
//        ended = began;
//        began = 1;
//    }
//    c = fgetc(inputFile);
//}
//if (c == EOF) return c == EOF;

// Replaceable with
//                     vvvvvv 1 or more non-spaces     
if (fscanf(inputFile, "%*[^ ])" == EOF) return EOF;
//                     vvvvv  1 space
if (fscanf(inputFile, "%1[ ]%*[^ ])" == EOF) return EOF;
if (fgetc(inputFile) != ' ') return EOF;

    • СУХОЙ

      Блок

        while (!ended && c != EOF)                                                                
        {                                                                                         
            if (c == '(')                                                                         
            {                                                                                     
                ended = 1;                                                                        
            } else                                                                                
            {                                                                                     
                char* tempFunctionName = (char*)malloc((functionNameLength+2)*sizeof(char));      
                strcpy(tempFunctionName, functionName);                                           
                tempFunctionName[functionNameLength] = c;                                         
                tempFunctionName[functionNameLength+1] = 0;                                       
                free(functionName);                                                               
                functionName = tempFunctionName;                                                  
                functionNameLength++;                                                             
            }                                                                 
            c = fgetc(inputFile);
        }                                                                                         
      

      повторяется дважды. Разложите его на функцию.

    • Нет голых петель. Чтобы дополнить вышеприведенный пункт, каждый цикл выполняет важную работу, которая заслуживает названия. Например, указанный выше блок на самом деле char * parse_function_name()не правда ли? Точно так же довольно сложно понять, что делает этот блок:

        while (!ended && c != EOF)
        {   
            if (c == ' ')
            {
                ended = began;
                began = 1;
            }
            c = fgetc(inputFile);
        }
      

      Похоже, что он пропускает все до (включительно) самого первого пробела. Почему? Это void parse_and_discard_type_declaration()?

      То же самое и для анализа аргументов.

    • Правильные инструменты для правильной работы. Код предполагает очень жесткий формат исходного файла. Вы были бы в гораздо лучшей форме, если бы flex/bison маршрут.

    • Разное

      • Не бросайте то, что malloc возвращается. Это может привести к загадочным сбоям.
      • Всегда проверяйте, что malloc возвращается.
      • Я настоятельно рекомендую realloc, а не вручную копировать содержимое.
      • По той же схеме увеличьте выделенный размер «геометрически» (allocated_size *= 2). Это резко сократит количество звонков до malloc и strcpy.
      • sizeof(char) гарантированно будет 1. В любом случае предпочтите sizeof(variable), как в sizeof(*tempFunctionName). Это позволяет избежать проблемы двойного обслуживания: если вы хотите переключиться на другой тип (скажем, wchar_t), будет только одно место для редактирования.

      Добавить комментарий

      Ваш адрес email не будет опубликован. Обязательные поля помечены *