Spicing C с классами - PullRequest
       1

Spicing C с классами

9 голосов
/ 19 января 2011

Отказ от ответственности: я новичок в C, но я играл с ним, пытаясь имитировать некоторые особенности классов.Хорошо, Я знаю, что если я хочу идти по этому пути, я должен изучить C ++, но рассмотрим следующий небольшой эксперимент .

Шрайнер, в книге Объектно-ориентированное программирование сANSI-C предлагает способ использовать указатели для получения объектов ориентации объектов в C. Я должен признать, что я только пролистал книгу, но мне не очень нравится его подход.В основном, он использует указатели на функции, чтобы организовать, что

func(foo);

на самом деле приводит к вызову

foo.methods->func();

, где foo.methods - это структура, содержащая указатели на функции.В этом подходе мне не нравится то, что в любом случае нужно иметь глобальную функцию foo;то есть методы не находятся в пространстве имен класса, в котором они живут. Мне кажется, что это скоро приведет к беспорядку: представьте, что два объекта foo и bar, оба имеют метод func, но с другим количеством параметров.

Поэтому я попытался найти что-то более подходящее на мой вкус.Первая попытка заключается в следующем (я опускаю декларации для краткости)

#include <stdio.h>

//Instances of this struct will be my objects
struct foo {
    //Properties
    int bar;

    //Methods
    void (* print)(struct foo self);
    void (* printSum)(struct foo self, int delta);
};

//Here is the actual implementation of the methods
static void printFoo(struct foo self) {
    printf("This is bar: %d\n", self.bar);
}

static void printSumFoo(struct foo self, int delta) {
    printf("This is bar plus delta: %d\n", self.bar + delta);
}

//This is a sort of constructor
struct foo Foo(int bar) {
    struct foo foo = {
        .bar = bar,
        .print = &printFoo,
        .printSum = &printSumFoo
    };
    return foo;
}

//Finally, this is how one calls the methods
void
main(void) {
    struct foo foo = Foo(14);
    foo.print(foo); // This is bar: 14
    foo.printSum(foo, 2); // This is bar plus delta: 16
}

Это неудобно, но вроде работает.Однако мне не нравится то, что вы должны явно добавить сам объект в качестве первого аргумента.С некоторой работой препроцессора я могу сделать немного лучше:

#include <stdio.h>
#define __(stuff)     stuff.method(* stuff.object)

//Instances of this struct will be my objects
struct foo {
    //Properties
    int bar;

    //Methods
    //Note: these are now struct themselves
    //and they contain a pointer the object...
    struct {
        void (* method)(struct foo self);
        struct foo * object;
    } print;
};

//Here is the actual implementation of the methods
static void printFoo(struct foo self) {
    printf("This is bar: %d\n", self.bar);
}

//This is a sort of constructor
struct foo Foo(int bar) {
    struct foo foo = {
        .bar = bar,
        //...hence initialization is a little bit different
        .print = {
            .method = &printFoo,
            .object = &foo
        }
    };
    return foo;
}

//Finally, this is how one calls the methods
void
main(void) {
    struct foo foo = Foo(14);
    //This is long and unconvenient...
    foo.print.method(* foo.print.object); // This is bar: 14
    //...but it can be shortened by the preprocessor
    __(foo.print); // This is bar: 14
}

Это насколько я могу получить.Проблема здесь в том, что он не будет работать для методов с аргументами, так как макросы препроцессора не могут принимать переменное число аргументов.Конечно, можно определять макросы _0, _1 и т. Д. В зависимости от количества аргументов (пока не устаешь), но это вряд ли хороший подход.

Есть ли способчтобы улучшить это и позволить C использовать более объектно-ориентированный синтаксис?

Я должен добавить, что на самом деле Шрайнер делает гораздо больше, чем я говорил в его книге, но я думаю, что базовая конструкция не меняется.

Ответы [ 3 ]

2 голосов
/ 19 января 2011

Различные рамки уже существуют. Смотри например http://ldeniau.web.cern.ch/ldeniau/html/oopc.html

1 голос
/ 06 сентября 2013

Вы смотрели на Google Go? По сути, это модернизированный C, в котором все делается так, как вы предлагали. Имеет параметрические полиморфизмы. Так что вам не нужно делать это:

static void printFoo(struct foo self) {
    printf("This is bar: %d\n", self.bar);
}

В Go это можно сделать так:

static void print(Foo self) {
    printf("This is foo: %d\n", self.foo);
}

static void print(Bar self) {
    printf("This is bar: %d\n", self.bar);
}

В Go Foo и Bar также будут структуры. Таким образом, вы в основном идете по пути с дизайнерами языка Go.

Обзор Go см. http://golang.org/doc/effective_go.html Это описание основного языка Go: http://golang.org/doc/effective_go.html

1 голос
/ 19 января 2011

Книга (в формате PDF), которая объясняет, как это сделать, - объектно-ориентированное программирование в ANSI C Это старое (1993), но все еще содержит некоторые правильные идеи и советы, ИМХО.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...