Подтвердить что ты не робот

Динамическое создание функций в C

Как я могу динамически создать функцию в C?

Я пытаюсь суммировать мою проблему C следующим образом:

  • У меня есть матрица, и я хочу иметь возможность использовать некоторую функцию для генерации ее элементов.

  • Функция не имеет аргументов

Следовательно, я определяю следующее:

typedef double(function)(unsigned int,unsigned int);

/* writes f(x,y) to each element x,y of the matrix*/
void apply(double ** matrix, function * f);

Теперь мне нужно генерировать постоянные функции внутри кода. Я думал о создании вложенной функции и возвращении ее указателя, но руководство GCC (которое позволяет вложенные функции) говорит:

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

который я бы ожидал от этого кода...

function * createConstantFunction(const double value){
 double function(unsigned int,unsigned int){
   return value;
 }
 return &function;
}

Итак, как я могу заставить его работать?

Спасибо!

4b9b3361

Ответ 1

C - это скомпилированный язык. Вы не можете создавать код во время выполнения "в C"; нет специальной поддержки C для извлечения инструкций в память и т.д. Разумеется, вы можете просто распределить память, убедиться, что она выполнима, и испустить там исходный машинный код. Затем вызовите его из C с помощью подходящего указателя функции.

Вы не получите никакой помощи от самого языка, хотя это похоже на генерацию кода и вызов его в BASIC на старой 8-разрядной машине.

Ответ 2

Вы должны быть знакомы с некоторым языком программирования, который поддерживает механизм закрытия, не так ли? К сожалению, C не поддерживает такое закрытие.

Вы можете узнать некоторые полезные библиотеки, которые имитируют закрытие на C, если вы настаиваете на закрытии. Но большинство из этих библиотек сложны и зависят от машин. Кроме того, вы можете изменить свое мнение, чтобы согласиться с C-style closure, если бы вы могли изменить подпись double ()(unsigned,unsigned);.

В C сами функции не имеют данных (или контекста), кроме параметров этого и статической переменной, к которым он мог получить доступ.
Таким образом, контекст должен быть передан самостоятельно. Вот пример использования дополнительного параметра:

// first, add one extra parameter in the signature of function.
typedef double(function)(double extra, unsigned int,unsigned int);

// second, add one extra parameter in the signature of apply
void apply(double* matrix,unsigned width,unsigned height, function* f, double extra)
{
        for (unsigned y=0; y< height; ++y)
            for (unsigned x=0; x< width ++x)
                    matrix[ y*width + x ] = f(x, y, extra);
        // apply will passing extra to f
}

// third, in constant_function, we could get the context: double extra, and return it
double constant_function(double value, unsigned x,unsigned y) { return value; }

void test(void)
{
        double* matrix = get_a_matrix();
        // fourth, passing the extra parameter to apply
        apply(matrix, w, h, &constant_function, 1212.0);
        // the matrix will be filled with 1212.0
}

Достаточно ли double extra? Да, но только в этом случае.
Как мы должны делать, если требуется дополнительный контекст?
В C параметр общего назначения void*, мы можем передать любой контекст хотя бы один параметр void *, передав адрес контекста.

Вот еще один пример:

typedef double (function)(void* context, int, int );
void apply(double* matrix, int width,int height,function* f,void* context)
{
        for (int y=0; y< height; ++y)
            for (int x=0; x< width ++x)
                    matrix[ y*width + x ] = f(x, y, context); // passing the context
}
double constant_function(void* context,int x,int y)
{
        // this function use an extra double parameter \
        //    and context points to its address
        double* d = context;
        return *d;
}
void test(void)
{
        double* matrix = get_a_matrix();
        double context = 326.0;
        // fill matrix with 326.0
        apply( matrix, w, h, &constant_function, &context);
}

(function,context) pair like &constant_function,&context - это C-style closure.
Каждая функция (F), которая нуждается в закрытии, должна иметь один параметр контекста, который будет передан в закрытие как его контекст. А вызывающий F должен использовать правильную пару (f, c).

Если вы можете изменить подпись функции в соответствии с закрытием C-стиля, ваш код будет простым и независимым от машины.
Если не удалось (функция и применение не написана вами), попробуйте убедить его изменить его код.
Если это не удается, у вас нет выбора, кроме как использовать некоторые библиотеки закрытия.

Ответ 3

Один из способов сделать это - написать стандартный файл C с набором требуемых функций, скомпилировать его через gcc и загрузить его как динамическую библиотеку, чтобы получить указатели на функции.

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

Ответ 4

Используя FFCALL, который обрабатывает трюк для конкретной платформы, чтобы сделать эту работу:

#include <stdio.h>
#include <stdarg.h>
#include <callback.h>

static double internalDoubleFunction(const double value, ...) {
    return value;
}
double (*constDoubleFunction(const double value))() {
    return alloc_callback(&internalDoubleFunction, value);
}

main() {
    double (*fn)(unsigned int, unsigned int) = constDoubleFunction(5.0);
    printf("%g\n", (*fn)(3, 4));
    free_callback(fn);
    return 0;
}

(Untested, так как у меня нет установленного FFCALL, но я помню, что он работает примерно так.)

Ответ 5

Если вы хотите написать код "на лету" для выполнения, nanojit может быть хорошим способом.

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

Ответ 6

Как уже упоминалось unwind, "создание кода во время выполнения" не поддерживается языком и будет много работать.

Я не использовал его сам, но один из моих коллег клянется Lua, "встроенным языком". Существует Lua C API, который (теоретически, по крайней мере) позволит вам выполнять динамические (сценарированные) операции.

Конечно, недостатком было то, что конечному пользователю может понадобиться какая-то подготовка в Lua.

Это может быть глупый вопрос, но почему функция должна быть сгенерирована в вашем приложении? Точно также, какое преимущество конечный пользователь получает от создания самой функции (в отличие от выбора из одной или нескольких предопределенных функций, которые вы предоставляете)?

Ответ 7

Этот механизм называется отражением, где код изменяет свое поведение во время выполнения. Java поддерживает отражение api для выполнения этой задачи.
Но я думаю, что эта поддержка недоступна в C.

Веб-сайт Sun говорит:

     

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

     

Недостатки отражения

     

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

Ограничения безопасности

Отражение требует времени выполнения   разрешение, которое может отсутствовать   при запуске под управлением безопасности.   Это важно   для кода, который должен выполняться в   ограниченный контекст безопасности, такой как   в апплете.

Экспонирование внутренних

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

Ответ 8

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

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

Ответ 9

Если вам действительно нужно динамически создавать функции, возможно, может помочь встроенный интерпретатор C. Я просто искал "встроенный C-интерпретатор" и получил Ch в результате:

http://www.softintegration.com/

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

Ответ 10

Поскольку вы хотите сгенерировать функцию, которая следует за простым рецептом, это не должно быть слишком сложно сделать с некоторыми встроенными сборками и блок исполняемой/записываемой памяти.

Этот подход немного взломан, поэтому я бы не рекомендовал его в производственном коде. Благодаря использованию встроенной сборки это решение работает только на Intel x86-64/AMD64 и должно быть переведено для работы с другими архитектурами.

Вы можете предпочесть это другим решениям на основе JIT, поскольку они не зависят от какой-либо внешней библиотеки.

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

Из соображений безопасности кодовая страница должна быть отмечена PROT_READ|PROT_EXEC после создания функции (см. mprotect).

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/mman.h>

int snippet_processor(char *buffer, double value, int action);

enum snippet_actions {
    S_CALC_SIZE,
    S_COPY,
};

typedef double (*callback_t) (unsigned int, unsigned int);

int main(int argc, char **argv) {

    unsigned int pagesize = 4096;
    char *codepage = 0;
    int snipsz = 0;

    callback_t f;

    /* allocate some readable, writable and executable memory */
    codepage = mmap(codepage,
        pagesize,
        PROT_READ | PROT_WRITE | PROT_EXEC,
        MAP_ANONYMOUS | MAP_PRIVATE,
        0,
        0);

    // generate one function at `codepage` and call it
    snipsz += snippet_processor(codepage, 12.55, S_COPY);
    f = (callback_t) (codepage);
    printf("result :: %f\n", f(1, 2));

    /* ensure the next code address is byte aligned
     * - add 7 bits to ensure an overflow to the next byte.
     *   If it doesn't overflow then it was already byte aligned.
     * - Next, throw away any of the "extra" bit from the overflow,
     *   by using the negative of the alignment value 
     *   (see how 2 complement works.
     */
    codepage += (snipsz + 7) & -8;

    // generate another function at `codepage` and call it
    snipsz += snippet_processor(codepage, 16.1234, S_COPY);
    f = (callback_t) (codepage);
    printf("result :: %f\n", f(1, 2));
}

int snippet_processor(char *buffer, double value, int action) {
    static void *snip_start = NULL; 
    static void *snip_end = NULL; 
    static void *double_start = NULL; 
    static int double_offset_start = 0;
    static int size;

    char *i, *j;
    int sz;

    char *func_start;
    func_start = buffer;

    if (snip_start == NULL) {
        asm volatile(
            // Don't actually execute the dynamic code snippet upon entry
            "jmp .snippet_end\n"

            /* BEGIN snippet */
            ".snippet_begin:\n"
            "movq .value_start(%%rip), %%rax\n"
            "movd %%rax, %%xmm0\n"
            "ret\n"

            /* this is where we store the value returned by this function */
            ".value_start:\n"
            ".double 1.34\n"
            ".snippet_end:\n"
            /* END snippet */

            "leaq .snippet_begin(%%rip), %0\n"
            "leaq .snippet_end(%%rip), %1\n"
            "leaq .value_start(%%rip), %2\n"
            : 
            "=r"(snip_start),
            "=r"(snip_end),
            "=r"(double_start)
        );
        double_offset_start = (double_start - snip_start);
        size = (snip_end - snip_start);
    }

    if (action == S_COPY) {
        /* copy the snippet value */
        i = snip_start;
        while (i != snip_end) *(buffer++) = *(i++); 

        /* copy the float value */
        sz = sizeof(double);
        i = func_start + double_offset_start; 
        j = (char *) &value;

        while (sz--) *(i++) = *(j++); 
    }

    return size;
}