tags:

views:

149

answers:

7

I hear that C isn't so type-safe and I think that I could use that as an advantage for my current project.

I'm designing an interpreter with the goal for the VM to be extremely fast, much faster than Ruby and Python, for example.

Now I know that premature optimization "is the root of all evil" but this is rather a conceptual problem.

  • I have to use some sort of struct to represent all values in my language (from number over string to list and map)

Would the following be possible?

struct Value {
 ValueType type;
 void* value;
}
  • I would store the actual values elsewhere, e.g: a separate array for strings and integers, value* would then point to some member in this table.

  • I would always know the type of the value via the type variable, so there wouldn't be any problems with type errors.

Now:

Is this even possible in terms of syntax and typing?

+5  A: 

Yes, you can use a void* to point to anything, and then cast it to the proper type when needed (that's how malloc and such can work).

void* is basically "pointer to an arbitrary block of memory".

Amber
A: 

There are varying degrees of type safety, but since C is a strongly typed language, it's actually on the safer end of the spectrum. That won't stop you from doing what you propose. The example you give is syntactically valid, and could be used to implement the system you describe. Keep in mind, though, that if you go ahead and try to reinvent the wheel by creating your own VM, you're very unlikely to beat the performance of existing languages like Ruby and Java.

warrenm
Java is precompiled bytecode, it has to be faster than run-time interpreter by definition. Anyway, "dragme" does not talk about Java so I don't understand why do you mention it?
Milan Babuškov
Java also has a JIT, which is faster than precompiled bytecode by definition :)
David Titarenco
(Apart from the JIT aspects, which I concede,) Java bytecode runs on a VM. VMs are not "by definition" faster than run-time interpreters. In fact, VMs ARE run-time interpreters.
warrenm
@Roger No, it's not **that** strongly-typed, which is why I emphasized the fact that there was a continuum in the first place. Any sane compiler will warn on your example.
warrenm
A: 

The good news: someone else thought of that. See Variant. (for example http://en.wikipedia.org/wiki/Variant_type and http://msdn.microsoft.com/en-us/library/x295h94e(VS.80).aspx .) The bad news: those of us who worked with them generally came to hate them.

Kate Gregory
+6  A: 

If you know the range of types you want to support, this could easily be done with a union to avoid casts all over the place:

struct Value
{
    ValueType type;
    union
    {
        int*        iptr;
        char*       sptr;
        float*      fptr;
        struct map* mptr;

        /* ... */

        void* vptr; /* catch all, extensions */

    } ptrs;
};
Nikolai N Fetissov
Granted, this isn't necessarily any shorter than typing out a cast, especially if the conversion is early on.
Amber
Of course it's not necessary, it just allows you to write bunch of convenience macros.
Nikolai N Fetissov
And somebody looking at the code can see the different value types rather than just void *. Using unions in an AST/interpreter is a very common pattern.
Mike Weller
A: 

Sure it is. You will only need a big switch on type to distinguish the type of a particular value. The best type to use for your field type will probably be an enum with a constant for each of the types of your language, like so:

typedef enum type {
    Integer,
    String,
    /* and so on... */
} ValueType;

Also remember you have to cast the void* pointer to a particular type before using it.

Michał Trybus
A: 

It's okay to write that code, but maybe you've to design your "language" (ok, interpreter :) ) before other things.

Btw, I suggest you to read the object oriented programming in c book. When you've understood the main concepts, you can look at the implementation of Python Objects, so that you can think about how methods interact with objects and how they are stored, and so on.

Bye bye!

Markon
A: 

Check out CCAN's type safe callbacks for macros that help you avoid bad casts. I know you aren't writing a callback, but lots of callbacks look like this:

void my_callback(void *context)

And several types may be cast to (void *) and passed to context. Casting within that function gets tricky, especially if the callback launches a thread that takes a single (void *) argument.

Anyway, you might find some useful bits if you follow the link.

Tim Post