Question

I was reading a book OOC by A.T. Schreiner, and I'm stuck at the following line in this code:

struct Class {
    size_t size;
    void *(* ctor) (void *self, va_list *app);
};

struct String {
    const void *class;  // must be first
    char *text;
};


void *new(const void *_class, ...) {
    const struct Class *class = _class;     // assign the address of `struct String` class
    void *p = calloc(1, class->size);       // allocate the sizeof(struct String);

    assert(p);
    *(const struct Class **)p = class;      // Force the conversion of p and set the argument `class` as the value of this pointer.
    if(class->ctor) {
        va_list ap;
        va_start(ap, _class);
        p = class->ctor(p, &ap);        // Now what is `p` here, a `struct String` or `struct Class`.
                                        // and if it is `struct Class` then how it convert to `struct String` in `String_ctor` function 
                                        // given below.
        va_end(ap);
    }
    return p;
}


static void *String_ctor(void *_self, va_list *app) {
    struct String *self = _self;        
    const char *text = va_arg(*app, const char *);

    self->text = malloc(strlen(text) + 1);
    assert(self->text);
    strcpy(self->text, text);
    return self;
}


// Initialization
static const struct Class _String  = {
    sizeof(struct String),
    String_ctor
};

const void *String = &_String;



// Call like this:
int main(void) {
 void *a = new(String, "some text");
}

Now, as you can see, in the new function, the following line p = class->ctor(p, &ap) confused me a lot. You can see the comment described above.

Also, I want to know how const void *class of struct String is initialized by new function as the book said.

Was it helpful?

Solution

  1. p is being assigned to the return value of class->ctor which is declared as void * so it's a void pointer. Looking at the definition of String_ctor you can see this returns self which is a String *, so in this case you're getting a void * which can safely be cast to a String *

  2. This is achieved with *(const struct Class **)p = class;. Since class is the first member of String, a pointer to a String will have the same address as a pointer to its class field. So when you convert p to a Class ** and write to it, you're writing to its class field.

OTHER TIPS

p is neither struct String nor struct Class. It's a pointer. It points to a block of memory. How that memory is interpreted is an interesting question.

Assuming we pass the address of a struct Class object that implements a string, the first calloc call allocates a chunk of memory the size of struct String. So it's entirely reasonable to assume p is a pointer to struct String.

The new function knows little about struct String or any other "class". Almost nothing really, except the fact that the first element of any such struct is a pointer to a struct Class object. Since a pointer to any struct is the same as the pointer to its first element (modulo type), this makes p also a pointer to a pointer to struct Class. So this line:

*(const struct Class **)p = class;

assigns the proper value to that pointer-to-struct-Class which p is pointing to.

Now this line

p = class->ctor(p, &ap); 

calls the constructor, passing the "self" pointer to it as in any OO environment. It also assigns the result of the constructor to p again, presumably because the ctor can (but doesn't have to) reallocate the object.

To recap: p is a pointer to struct String that new interprets as a pointer to the first element of struct String, which must be the pointer to struct Class.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top