You may need to understand what closures and callbacks are. Function pointers are useful, but might not be enough alone (read more to understand how function pointers are useful to implement closures).
You should learn more about C, so read good books about C programming. I recommend downloading the C11 standard n1570 and glancing into it. A very important notion is undefined behavior and you should be scared of it.
if is it somehow possible to compute (in a char array for example, but also by any other way) kind of code, which I will insert and use later. But not as string, but directly like a code.
This is not possible in purely standard C code (since the set of translation units comprising your program is fixed), and in principle any valid function pointer value should point to some existing function (so stricto sensu calling any other function pointer value would be undefined behavior). However, some implementations are able to construct (in some implementation specific way), somehow "valid" new function pointers (but that is outside of the C11 standard). On purely Harvard architectures with code sitting in ROM, that is not even possible.
generating and dynamically loading plugins
However, if you are running under a modern operating system (I recommend Linux) you might use dynamic loading and plugin facilities. I am focusing on Linux specifically (for Windows, details are very different, and the evil is in the details; read Levine's book Linkers and Loaders). Read Operating Systems: Three Easy Pieces to learn more about OSes.
So what you could do (on Linux) is to generate at runtime some C code in some temporary file, such as /tmp/generated.c
; you need to define your conventions about that file (e.g. that it defines a void pluginfun(int)
function having some additional desirable properties); then you fork a compilation command to compile that into a shared object (read How to write shared libraries by Drepper), that is a plugin. So your program might run (perhaps using system(3), or lower level system calls like fork(2), execve(2), waitpid(2) etc....) a compilation process like e.g. gcc -Wall -O -fPIC /tmp/generated.c -shared -o /tmp/generatedplugin.so
; later, your main program would load that plugin using 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);
}
Since you care about function pointers, your code would be more readable if you declare their signature as a type:
typedef void pluginfun_type(int);
Then a function pointer to functions of that signature is easily declared:
pluginfun_type* fptr = NULL;
and your program can get with dlsym(3) the pluginfun
function address in your plugin:
fptr = (pluginfun_type*) dlsym(dlh, "pluginfun");
if (!fptr) {
fprintf(stderr, "dlsym of pluginfun failed: %s\n", dlerror());
exit(EXIT_FAILURE);
}
and at last use (*fptr)(3)
to call that function.
You should use gcc -O -Wall foo.o bar.o -rdynamic -ldl -o foobarprog
to link your main program. You might call at last dlclose(3) but you should not do that if you have some call frame still active for a function inside your plugin.
Such a generating plugin approach works extremely well on Linux. My manydl.c is a toy program generating "random" C code, forking a compilation of that generated C code into a generated plugin, and loading that plugin (and can repeat that many times). It shows that in practice, a Linux program can generate and load many hundred thousands (and perhaps even millions, if you are patient enough) plugins. The code segments leak is in practice acceptable (and might be avoided by careful use of dlclose
)
And computer are quite fast today. You might generate a small plugin (of less than a thousand lines of C) on every REPL interaction, and compile it and load it (for such a small plugin, it usually takes less than 0.1 second), and that is practically compatible with human interaction (I did so in my obsolete GCC MELT project; and I will do so in my bismon project described in the bismon-chariot-doc.pdf draft report).
using a JIT-compiling library
To generate code dynamically at runtime, you might also use some JIT-compilation library. There are many of them, including libgccjit, LLVM (in C++), asmjit, libjit, GNU lightning, tinycc with its libtcc
. Some of them are able to emit machine code quickly, but then the performance of that code might not be very good. Other are doing optimizations like a good C compiler (in particular libgccjit which uses GCC inside). Of course, these optimizations take some time, so the machine code is emitted much slowly but its performance is as good as optimized C code.
Embedding an interpreter
In many cases, scripting languages offer some kind of eval. Several interpreters are designed to be easily embeddable in your application (with cautions and caveats), notably Lua or Guile (and Nim). Many more interpreters (Ocaml, Python, Perl, Parrot, Ruby, ...) can somehow be embedded in your application also (you may need to understand garbage collection terminology). Of course all require some coding conventions. And interpreters are in practice slower than compiled code.
Terminology
Can anyone tell me, if is it possible in C, or any different language, and how is this concept called?
You probably need to read more about metaprogramming, eval, multistage programming, homoiconicity, reflection, continuations, type introspection, stack trace (consider Ian Taylor's libbacktrace).
You may be interested by Lisp-like languages. Read first SICP, then Queinnec's book Lisp In Small Pieces and Scott's book Programming Language Pragmatics. Notice that SBCL (a Common Lisp implementation) is generating machine code at every REPL interaction.
Since you mention an interest in artificial intelligence, you could look into J.Pitrat's blog. His CAIA system is bootstrapped so generating the totality of its C code (in nearly 500KLOC).