Вычислить, какую функцию (или вид кода) использовать в C - PullRequest
0 голосов
/ 30 ноября 2018

У меня немного необычный вопрос.Давайте представим, что у меня N функций:

void function1(){ //some code here }
void function2(){ //some code here }
...
void functionN(){ //some code here }

Есть ли способ, которым я мог бы динамически вычислить или выяснить, какую функцию использовать, без операторов IF?И в зависимости от названия имени функции, назвать его?Позвольте мне показать вам псевдокод, который может лучше описать ситуацию:

 for(int I=1;I<=N;I++){
    functionI();
 }

Я имею в виду, возможно ли каким-либо образом вычислить (например, в массиве символов, но и любым другим способом) код,который я буду вставлять и использовать позже.Но не как строка, а как код.Позвольте мне продемонстрировать на другом примере:

int num=3;
char functionInString[]="printf(num);
//Some code, that would for example change letters in 
functionInString, for example to different fuction 
consisting of letters

//And here, do whatever is written in functionToString

Извините, если я не был достаточно ясен.Может кто-нибудь сказать мне, если это возможно на C или любом другом языке, и как эта концепция называется?

Ответы [ 3 ]

0 голосов
/ 30 ноября 2018

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

Вы должны больше узнать о Си, поэтому читайте хорошие книги по программированию на Си.Я рекомендую загрузить C11 стандарт n1570 и взглянуть на него.Очень важным понятием является неопределенное поведение , и вы должны быть напуганы .

, если возможно каким-либо образом вычислить (например, в массиве char, но также любым другим способом), который я добавлю и использую позже.Но не как строка, а как код.

Это невозможно в чисто стандартном C-коде (поскольку набор переводных единиц , составляющих вашу программу, фиксирован), и в принципе любое допустимое значение указателя на функцию должно указывать на некоторое существующая функция (поэтому stricto sensu вызов любого другого значения указателя на функцию будет неопределенным поведением).Тем не менее, некоторые реализации могут создавать (каким-то образом, специфичным для реализации), каким-то образом "допустимые" новые указатели функций (но это вне стандарта C11).На чисто Гарвардской архитектуре с кодом, находящимся в ПЗУ, это даже невозможно.

создание и динамическая загрузка плагинов

Однако, если вы работаете под современным операционная система (я рекомендую Linux) вы можете использовать динамическую загрузку и плагин .Я сосредотачиваюсь именно на Linux (для Windows детали очень разные, а зло в деталях; прочитайте книгу Левина Линкеры и загрузчики ).Прочитайте Операционные системы: три простых компонента , чтобы узнать больше об ОС.

Итак, что вы можете сделать (в Linux), это сгенерировать во время выполнения некоторый C-код в некоторыхвременный файл, такой как /tmp/generated.c;вам нужно определить свои соглашения относительно этого файла (например, что он определяет функцию void pluginfun(int), имеющую некоторые дополнительные желательные свойства);затем вы разбираете команду компиляции, чтобы скомпилировать ее в общий объект (см. Как писать общие библиотеки от Drepper), то есть плагин.Таким образом, ваша программа может работать (возможно, используя system (3) или системные вызовы более низкого уровня, такие как fork (2) , execve (2) , waitpid (2) и т.д ....) процесс компиляции, например, gcc -Wall -O -fPIC /tmp/generated.c -shared -o /tmp/generatedplugin.so;позже ваша основная программа загрузит этот плагин, используя dlopen (3) :

void* dlh = dlopen("/tmp/generatedplugin.so", RTLD_NOW);
if (!dlh) { 
  fprintf(stderr, "dlopen /tmp/generatedplugin.so failed: %s\n",
          dlerror());
  exit(EXIT_FAILURE);
}

Поскольку вы заботитесь о указателях функций, ваш код будет более читабельным, если вы объявите их подпись каквведите:

typedef void pluginfun_type(int);

Затем указатель функции на функции этой сигнатуры будет легко объявлен:

pluginfun_type* fptr = NULL; 

, и ваша программа может получить с помощью dlsym (3) pluginfun адрес функции в вашем плагине:

fptr = (pluginfun_type*) dlsym(dlh, "pluginfun");
if (!fptr) {
   fprintf(stderr, "dlsym of pluginfun failed: %s\n", dlerror());
   exit(EXIT_FAILURE);
}

и, наконец, используйте (*fptr)(3) для вызова этой функции.

Вы должны использовать gcc -O -Wall foo.o bar.o -rdynamic -ldl -o foobarprog, чтобы связать вашу основную программу.Вы могли бы, наконец, вызвать dlclose (3) , но вам не следует этого делать, если у вас есть еще какой-то фрейм вызова для функции внутри вашего плагина.

Такой подход генерирования плагина работает очень хорошов линуксеMy manydl.c - игрушечная программа, генерирующая «случайный» код C, разветвляющая компиляцию этого сгенерированного кода C в сгенерированный плагин и загружающая этот плагин (и может повторяться много раз).Это показывает, что на практике программа Linux может генерировать и загружать многие сотни тысяч (и, возможно, даже миллионы, если вы достаточно терпеливы) плагины. сегменты кода утечка на практике приемлема (и ее можно избежать, если осторожно использовать dlclose)

И компьютер сегодня довольно быстрый.Вы можете сгенерировать небольшой плагин (менее тысячи строк C) при каждом взаимодействии REPL , скомпилировать его и загрузить (для такого маленького плагина это обычно занимает менее 0,1 секунды), иэто практически совместимо с человеческим взаимодействием (я сделал это в моем устаревшем проекте GCC MELT ; я сделаю это в моем проекте bismon , описанном в bismon-chariot-doc.pdf черновик отчета).


с использованием библиотеки JIT-компиляции

Для динамического генерирования кода во время выполнения вы также можете использовать некоторую JIT-компиляцию библиотека.Их много, включая libgccjit , LLVM (на C ++), asmjit , libjit , GNU lightning , tinycc со своим libtcc.Некоторые из них способны быстро генерировать машинный код, но тогда производительность этого кода может быть не очень хорошей.Другие делают оптимизации как хороший компилятор C (в частности libgccjit , который использует GCC внутри).Конечно, эти оптимизации занимают некоторое время, поэтому машинный код генерируется намного медленнее, но его производительность не уступает оптимизированному коду C.


Встраивание интерпретатора

Во многих случаяхязыки сценариев предлагают что-то вроде eval .Несколько интерпретаторов разработаны , чтобы быть легко встраиваемыми в ваше приложение (с предостережениями и предостережениями), в частности Lua или Guile Nim ).Многие другие интерпретаторы (Ocaml, Python, Perl, Parrot, Ruby, ...) также могут быть как-то встроены в ваше приложение (вам может понадобиться понять терминологию мусора ).Конечно, все требуют некоторых соглашений о кодировании.И интерпретаторы на практике работают медленнее, чем скомпилированный код.


Терминология

Может кто-нибудь сказать мне, если это возможно в C или любом другом языке, и как этоконцепция называется?

Возможно, вам нужно больше узнать о метапрограммировании , eval , многоступенчатом программировании , гомоизоничности , отражение , продолжения , тип самоанализа , трассировка стека (рассмотрим libbacktrace Яна Тейлора).

Вас могут заинтересовать Lisp -подобные языки.Сначала прочитайте SICP , затем книгу Квиннека Lisp In Small Pieces и книгу Скотта Прагматика языка программирования .Обратите внимание, что SBCL (реализация Common Lisp) генерирует машинный код при каждом взаимодействии REPL.

Поскольку вы упоминаете интерес к искусственному интеллекту, вы можете заглянуть в блог J.Pitrat.Его система CAIA загружена , поэтому генерирует полный код C (почти 500KLOC).

0 голосов
/ 30 ноября 2018

Использовать массив указателей на функции.Сначала определите формат:

typedef void func_t (void);

Затем создайте массив указателей на функции:

func_t* func[n] = {function1, function2, function3};

Пример:

#include <stdio.h>

void function1 (void) { puts(__func__); }
void function2 (void) { puts(__func__); }
void function3 (void) { puts(__func__); }

typedef void func_t (void);

#define n 3

int main()
{
  func_t* func[n] = {function1, function2, function3};
  for(size_t i=0; i<n; i++)
  {
    func[i]();
  }
}
0 голосов
/ 30 ноября 2018

Попробуйте поработать с массивами указателей функций;

#include <stdlib.h>
#include <stdio.h>

void myfunc1(){printf("1\n");};
void myfunc2(){printf("2\n");};
void myfunc3(){printf("3\n");};
void myfunc4(){printf("4\n");};
void myfunc5(){printf("5\n");};

void (*myfuncs[5])() = {myfunc1, myfunc2, myfunc3, myfunc4, myfunc5};

int main(int argc, char *argv[])
{
    for(int i=0;i<5;i++) {
            (myfuncs[i])();
    }
    exit(EXIT_SUCCESS);
}
...