Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using C/C++ as scripting language

Tags:

c++

c

scripting

I am trying to create an architecture that would use C/C++ as "scripting language". I already have a prototype working based on:

http://www.codeproject.com/Articles/29999/Compiling-C-code-at-runtime

and

http://runtimecompiledcplusplus.blogspot.com

My prototype allows me to recompile a dynamic link library / shared object and reload it at runtime. I only have a small problem lets take in example the following code:

[ bot.c ]

typedef struct
{
    float health;

    float speed;

    bool  alive;

} Bot;

Bot bot = { .health = 100.0f,
        .speed  = 1.0f,
        .alive  = true };

void OnCollision( void )
{
    bot.health -= 10.0f;

    if( bot.health <= 0.0f )
    { bot.alive = false; }
}


void OnUpdate( void )
{
    if( bot.alive )
    {
        /* Update animation */
    }
}

If the bot.c script is assigned to 1 bot its ok, but if I assign the callbacks to multiple bots they share the same data (1 get hit, they all get hit!). How can run the bot script "separately" for each bots?

I already research on coroutine in C/C++ (using setjmp/longjmp) but its risky for C++ constructors & destructors (and also force you to integrate macros inside your functions, which in my case is not an options for my users).

I was thinking about running multiple threads, but the same problem will occur (since the data comes from a dll/so it is shared). Im not too familiar with fork/co-process but it does not seems to apply for my case. And I do not think that pipes are an option as well... Im pretty much stuck.

Is there anyway to solve this problem?

ps: Yes I know that scripting language like Lua or Javascript (V8) have built-in coroutine or in the case of Lua lua_thread that would fix my issue but I want to stick with a C/C++ interface for the users to code since its for gaming performance is critical!

like image 629
McBob Avatar asked Nov 20 '12 14:11

McBob


People also ask

Why C is not a scripting language?

The theoretical difference between the two is that scripting languages do not require the compilation step and are rather interpreted. For example, normally, a C program needs to be compiled before running whereas normally, a scripting language like JavaScript or PHP need not be compiled.

Can you use C++ as a scripting language?

Syntax of C++ scripts is the same as the syntax of C# scripts (since C#Script is also based on JScript). They are the same as long as the scripts reside in TestComplete. If you export scripts to a Connected Application, they will be different.

What is a script in C?

The C scripts are linked to the startup code (see Tcc\src\Vgcrt2 and Tcc\Lib directories) including the functions to control the extended commands. They can used as in the REBOL scripts and in the batch files, but they must be called using the C wrapper: 13.1.5 Functions for C-scripts.


1 Answers

You don't need anything like coroutines. The problem is that you have a single global variable holding the data and you simply need multiple instances of it.

Now the standard shared library machinery won't allow you to duplicate the static data of the module, but you don't need to. Just don't allocate the data statically, but create multiple instances of it.

In C++ it would be easier to use, but somewhat more difficult to implement since you'd need to compute the mangled symbol names and correct incantation to do new and delete. The script would simply look like:

class Bot {
    float health;
    float speed;
    bool  alive;
    Bot()
    void OnCollision();
    void OnUpdate();
}
Bot::Bot() : health(100.0f), speed(1.0f), alive(true) {}
...

You'd need to know the class name, but that could be derived from the module name or something like that. You could write a template that would generate bunch of static functions to somewhat portably handle the initialization. Like:

struct ModuleBase {
    void *(*Init)();
    void (*Done)(void *);
    void (*Collision)(void *);
    void (*Update)(void *);
    ModuleBase(void *(*I)(), void (*D)(void *), void (*C)(void *), void (*U)(void *)) : Init(I), Done(D), Collision(C), Update(U) {}
};
template <typename T>
class Module : public ModuleBase {
    static void *InitFunc() { return static_cast<void *>(new T()); }
    static void DoneFunc(void *x) { delete static_cast<T *>(x); }
    static void CollisionFunc(void *x) { static_cast<T *>(x)->OnCollision(); }
    static void UpdateFunc(void *x) { static_cast<T *>(x)->OnUpdate(); }
public:
    Module() : ModuleBase(&InitFunc, &DoneFunc, &CollisionFunc, &UpdateFunc) {}
};

used like

Module<Bot> bot;

at the end of the "script". Than you just look that symbol up and call the function pointers in it.

In C it would be more difficult to use, because you'd need to explicitly write init and deinit functions, but you already know how to call it:

struct Bot { ... }
// NO INSTANCES HERE!
void *Init(void) {
    Bot *bot = malloc(sizeof(Bot));
    bot->health = 100.0f;
    bot->speed = 1.0f;
    bot->alive = true;
    return bot;
}
void Done(void *bot) {
    free(bot);
}
void OnCollision(void *void_bot)
{
    Bot *bot = void_bot;
    ...
}
void OnUpdate(void *void_bot)
{
    Bot *bot = void_bot;
    ...
}

In either case you simply create any number of instances, either with operator new[] and Bot constructor or with Init and call the functions/methods with appropriate argument.

like image 105
Jan Hudec Avatar answered Oct 08 '22 02:10

Jan Hudec