2013-05-31 3 views
1

Я слышал, что когда библиотеки сокетов BSD разрабатывались, программированию на C не хватало определенного шаблона или функции, которые привели к дизайну, который у нас есть сегодня.Альтернатива структурам «подклассификации» в C

Например, struct socketaddr_in отлита до struct socketaddr при передаче системных вызовов, таких как bind.

из заголовочных файлов Libc GNU:

/* POSIX.1g specifies this type name for the `sa_family' member. */ 
    typedef unsigned short int sa_family_t; 

/* This macro is used to declare the initial common members 
    of the data types used for socket addresses, `struct sockaddr', 
    `struct sockaddr_in', `struct sockaddr_un', etc. */ 

#define __SOCKADDR_COMMON(sa_prefix) \ 
    sa_family_t sa_prefix##family 

#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int)) 

/* Structure describing a generic socket address. */ 
struct sockaddr 
    { 
    __SOCKADDR_COMMON (sa_); /* Common data: address family and length. */ 
    char sa_data[14];  /* Address data. */ 
    }; 

/* Structure describing an Internet socket address. */ 
struct sockaddr_in 
    { 
    __SOCKADDR_COMMON (sin_); 
    in_port_t sin_port;   /* Port number. */ 
    struct in_addr sin_addr;  /* Internet address. */ 

    /* Pad to size of `struct sockaddr'. */ 
    unsigned char sin_zero[sizeof (struct sockaddr) - 
       __SOCKADDR_COMMON_SIZE - 
       sizeof (in_port_t) - 
       sizeof (struct in_addr)]; 
    }; 

/* Internet address. */ 
typedef uint32_t in_addr_t; 
struct in_addr 
    { 
    in_addr_t s_addr; 
    }; 

Пример программы:

int main(int argc, char *argv[]) { 
    ... 

    // Initialize the server address 
    struct sockaddr_in serv_addr; 
    bzero((char *)&serv_addr, sizeof(serv_addr)); 

    serv_addr = (struct sockaddr_in) { 
     .sin_port = htons(port), 
     .sin_family = AF_INET , 
     .sin_addr.s_addr = INADDR_ANY 
    }; 

    // Bind the server address to the TCP/IP socket 
    int status = bind(server_socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 

    ... 
} 

Я не могу думать о том, как еще этот общий интерфейс может быть введена в действие. Есть идеи?

+0

Союзы ?. ....... –

+0

Что вы подразумеваете под последним предложением? Как/почему это работает? Почему они это сделают? Основные сведения о том, как еще можно реализовать аналогичную схему подкласса? – Kevin

+0

Я имею в виду, это выглядит как самый элегантный способ сделать это (см. Мою «примерную программу»). Я помню, как один из моих профессоров заметил несколько лет назад, что есть еще один способ добиться этого. – OregonTrail

ответ

2

Я бы не сказал, что C не было шаблона подкласса при разработке библиотек сокетов BSD. Напротив, разработчики API сокетов BSD специально использовали этот шаблон. Кастрирование структур в базовые структуры (более общие структуры с меньшим количеством полей) и обратно в специализированные структуры очень распространено и полностью поддерживается. C даже имеет некоторые гарантии, чтобы убедиться, что он работает портативно: если первым элементом определения структуры является другая структура, C гарантирует, что тип, адрес памяти и макет первого члена вложенной структуры совпадают с типом, памятью адрес и компоновку приложенной структуры к типу ее первого элемента.

+0

Ответ делегата в ответе Обезьяны интересен, и я всегда задавался вопросом, появляется ли он в производственном коде. Тем не менее, я согласен с вами в том, что это понятие кастинговых структур является законным, поскольку оно применяется правилами выравнивания структуры по стандарту C. – OregonTrail

+0

@ OregonTrail да, это абсолютно так! Родитель-плакат для этого шаблона - это переключатель VFS почти в каждом ядре операционной системы (это то, что принимает общие операции с файлами, такие как «открывать» и «удалять», и делегирует их фактическим реализациям файловой системы). Ниже перечислены определения для версии Linux: http://lxr.linux.no/#linux+v3.9.5/include/linux/fs.h#L1515 – Celada

3

Вы можете использовать шаблон делегата для поддержки формы наследования на C. Это выглядит довольно, но это довольно тяжело: у него есть много потенциальных точек отказа, требуется некоторое распределение кучи и т. Д. Я думаю, они не хотел иметь такой вес для OS API?

Вот пример этого

struct ShapeDelegate { 
    double (*getSurfaceArea)(void* data); 
    int (*isInside)(void* data, double x, double y); 
    void (*destroy)(void* data); 
} 

struct Shape { 
    void* data; 
    struct ShapeDelegate delegate; 
} 

double Shape_getSurfaceArea(Shape* self) { 
    return self->delegate.getSurfaceArea(self->data); 
} 

int Shape_isInside(Shape* self, double x, double y) { 
    return self->delegate.isInside(self->data, x, y); 
} 

void Shape_destroy(Shape* self) { 
    if (self->delegate.destroy != NULL) 
    self->delegate.destroy(self->data); 
} 

Теперь, скажем, вы хотите круг, как реализации Shape

struct CircleData { 
    double x, y, r; 
} 

double Circle_getSurfaceArea(void* data) { 
    CircleData* self = (CircleData*)data; 
    return 2 * M_PI * self->r * self->r; 
} 

int Circle_isInside(void* data, double x, double y) { 
    double dist; 
    CircleData* self = (CircleData*)data; 
    dist = sqrt(sqr(x - self->x) + sqr(y - self->y)); 
    return dist < self->r; 
} 

void Circle_destroy(void* data) { 
    free(data); 
} 

struct ShapeDelegate Circle_ShapeDelegate { 
    Circle_getSurfaceArea, 
    Circle_isInside, 
    Circle_destroy 
}; 

Вам нужно какой-то конструктор

void 
Shape_initAsCircle(Shape* self, double x, double y, double r) { 
    CircleData* data; 

    data = (CircleData*)malloc(sizeof(CircleData)); 
    data->x = x, data->y = y, data->r = r; 

    self->data = data; 
    self.delegate = Circle_ShapeDelegate; 
} 
Смежные вопросы