简体   繁体   中英

Avoiding C++ polymorphism

I recently created my own scripting language. My code structures are heavily based on polymorphism. (I'm not really sure about how is this called. I've got a virtual function and then I derive the class and let the OS decide what to call on runtime):

class Statement 
{ 
    virtual void exec() = 0; 
};

class PrintStmt : public Statement
{ 
    void exec() 
    { 
        std::cout << expression->eval(); 
    };    

class AssignStmt : public Statement
{ 
    void exec() 
    { 
        vm->bind_var(name, expression->eval()) 
    };

Any ideas how I can rework this so it can be compiled by a pure C compiler? I know this is general question and there is no single answer, but how would you do this?

Note: I already downloaded the python code as a reference, but it will take time until I figure out how it is working.

Statement would be a struct . In addition to its data members, you will need a function pointer eg

struct Statement
{
    void(*exec)(Statement* this); // Function pointer
    // Other members
};

You would then have different implementations of the functions per statement type and a function for manufacturing objects of the right type eg

static void printExec(struct Statement* this)
{
    printf("%s", this->whatever);
}

struct Statement* createPrintStatement()
{
    struct Statement* statement = calloc(1, sizeof(struct Statement));
    statement->exec = printExec;
    return statement;
}

And you would invoke it like this:

statement->exec(statement);

The this pointer gives you access to the data members of the particular struct ie the instance whose exec method you invoked.

If you have lots of functions, consider using a vtable.

struct VTable
{
    void(*exec)(Statement* this); // Function pointer
    const char* (*stringValue)(Statement* this); // Function pointer
};

struct Statement
{
    struct VTable* vtable;
    // Other members
};

You build each a vtable for each kind of object only once

struct VTable printVTable =
{
    printExec,
    printStringValue
};

You create new objects thus:

struct Statement* createPrintStatement()
{
    struct Statement* statement = calloc(1, sizeof(struct Statement));
    statement->vtable = &printVTable;
    return statement;
}

and invoke the methods thus

statement->vtable->exec(statement);

The vtable method is more or less what C++ does behind the scenes.

The most straightforward way to convert this to C would probably be to use function pointers. As @DrewNorman said, you will need to understand how vtables work, class layouts etc, and reimplement it (at least partially) in C. The example code below is very limited but gives you a hint of what to expect.

struct Statement {
  void (*exec)(struct Statement* s);
};
struct PrintStmt {
  struct Statement statement;
  char* what;
};

void print_function(struct Statement* s) {
    struct PrintStmt* p = (struct PrintStmt*)s;
    printf(p->what);
}

// ...

struct PrintStmt p;
p.statement.exec = &print_function;
p.what = "Hello world";
p.statement.exec(p);

There are numerous C projects that use this kind of technique, GObject comes to my mind but it's far from the only one.

(Note: I'm used to C++ not really to C so this may not even be valid C but you get the idea anyway)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM