Quais são algumas maneiras de escrever código orientado a objeto em C? Especialmente no que diz respeito ao polimorfismo.
Veja também esta pergunta Stack Overflow Object-orientação no C .
Quais são algumas maneiras de escrever código orientado a objeto em C? Especialmente no que diz respeito ao polimorfismo.
Veja também esta pergunta Stack Overflow Object-orientação no C .
Respostas:
Sim. De fato, Axel Schreiner fornece seu livro "Programação Orientada a Objetos em ANSI-C" de graça, que cobre o assunto completamente.
Desde que você está falando sobre polimorfismo, sim, você pode, estávamos fazendo esse tipo de coisa anos antes do C ++ surgir.
Basicamente você usa um struct
para armazenar os dados e uma lista de indicadores de função para apontar para as funções relevantes para esses dados.
Portanto, em uma classe de comunicação, você teria uma chamada de abertura, leitura, gravação e fechamento que seria mantida como quatro ponteiros de função na estrutura, juntamente com os dados de um objeto, algo como:
typedef struct {
int (*open)(void *self, char *fspec);
int (*close)(void *self);
int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
// And data goes here.
} tCommClass;
tCommClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;
tCommClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;
Obviamente, esses segmentos de código acima estariam realmente em um "construtor", como rs232Init()
.
Quando você 'herda' dessa classe, basta alterar os ponteiros para apontar para suas próprias funções. Todos que chamavam essas funções o faziam através dos ponteiros de função, fornecendo seu polimorfismo:
int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");
Mais ou menos como uma tabela manual.
Você pode até ter classes virtuais definindo os ponteiros como NULL - o comportamento seria um pouco diferente do C ++ (um dump principal em tempo de execução em vez de um erro em tempo de compilação).
Aqui está um pedaço de código de exemplo que o demonstra. Primeiro, a estrutura de classe de nível superior:
#include <stdio.h>
// The top-level class.
typedef struct sCommClass {
int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;
Então temos as funções para a 'subclasse' do TCP:
// Function for the TCP 'class'.
static int tcpOpen (tCommClass *tcp, char *fspec) {
printf ("Opening TCP: %s\n", fspec);
return 0;
}
static int tcpInit (tCommClass *tcp) {
tcp->open = &tcpOpen;
return 0;
}
E o HTTP também:
// Function for the HTTP 'class'.
static int httpOpen (tCommClass *http, char *fspec) {
printf ("Opening HTTP: %s\n", fspec);
return 0;
}
static int httpInit (tCommClass *http) {
http->open = &httpOpen;
return 0;
}
E, finalmente, um programa de teste para mostrá-lo em ação:
// Test program.
int main (void) {
int status;
tCommClass commTcp, commHttp;
// Same 'base' class but initialised to different sub-classes.
tcpInit (&commTcp);
httpInit (&commHttp);
// Called in exactly the same manner.
status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
status = (commHttp.open)(&commHttp, "http://www.microsoft.com");
return 0;
}
Isso produz a saída:
Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com
para que você possa ver que as diferentes funções estão sendo chamadas, dependendo da subclasse.
tCommClass
nome seria renomeado tCommVT
e uma tCommClass
estrutura só teria campos de dados e um único tCommVT vt
campo apontando para a "única e única" tabela virtual. Carregar todos os ponteiros com cada instância adiciona uma sobrecarga desnecessária e se assemelha mais a como você faria coisas em JavaScript do que em C ++, IMHO.
Os namespaces geralmente são feitos da seguinte maneira:
stack_push(thing *)
ao invés de
stack::push(thing *)
Para transformar uma estrutura C em algo como uma classe C ++, você pode transformar:
class stack {
public:
stack();
void push(thing *);
thing * pop();
static int this_is_here_as_an_example_only;
private:
...
};
Para dentro
struct stack {
struct stack_type * my_type;
// Put the stuff that you put after private: here
};
struct stack_type {
void (* construct)(struct stack * this); // This takes uninitialized memory
struct stack * (* operator_new)(); // This allocates a new struct, passes it to construct, and then returns it
void (*push)(struct stack * this, thing * t); // Pushing t onto this stack
thing * (*pop)(struct stack * this); // Pops the top thing off the stack and returns it
int this_is_here_as_an_example_only;
}Stack = {
.construct = stack_construct,
.operator_new = stack_operator_new,
.push = stack_push,
.pop = stack_pop
};
// All of these functions are assumed to be defined somewhere else
E fazer:
struct stack * st = Stack.operator_new(); // Make a new stack
if (!st) {
// Do something about it
} else {
// You can use the stack
stack_push(st, thing0); // This is a non-virtual call
Stack.push(st, thing1); // This is like casting *st to a Stack (which it already is) and doing the push
st->my_type.push(st, thing2); // This is a virtual call
}
Não fiz o destruidor ou excluí, mas segue o mesmo padrão.
this_is_here_as_an_example_only é como uma variável de classe estática - compartilhada entre todas as instâncias de um tipo. Todos os métodos são realmente estáticos, exceto que alguns fazem isso *
st->my_type->push(st, thing2);
vez dest->my_type.push(st, thing2);
struct stack_type my_type;
vez destruct stack_type * my_type;
Class
estrutura ? Isso tornaria o OO C mais dinâmico que o C ++. Que tal isso? A propósito, +1.
Acredito que, além de ser útil por si só, implementar OOP em C é uma excelente maneira de aprender OOP e entender seu funcionamento interno. A experiência de muitos programadores mostrou que, para usar uma técnica de maneira eficiente e confiável, o programador deve entender como os conceitos subjacentes são finalmente implementados. Emular classes, herança e polimorfismo em C ensina exatamente isso.
Para responder à pergunta original, aqui estão alguns recursos que ensinam como fazer POO em C:
Postagem no blog EmbeddedGurus.com "Programação baseada em objeto em C" mostra como implementar classes e herança única em C portátil: http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c /
Nota da aplicação "" C + "- Programação Orientada a Objetos em C" mostra como implementar classes, herança única e ligação tardia (polimorfismo) em C usando macros de pré-processador: http://www.state-machine.com/resources/cplus_3. 0_manual.pdf , o código de exemplo está disponível em http://www.state-machine.com/resources/cplus_3.0.zip
Eu já vi isso. Eu não recomendaria. O C ++ começou originalmente dessa maneira como um pré-processador que produzia o código C como uma etapa intermediária.
Essencialmente, o que você acaba fazendo é criar uma tabela de despacho para todos os seus métodos nos quais você armazena suas referências de função. A derivação de uma classe implicaria copiar esta tabela de expedição e substituir as entradas que você desejava substituir, com seus novos "métodos" tendo que chamar o método original se desejar invocar o método base. Eventualmente, você acaba reescrevendo C ++.
glib
escrito em C de maneira objetiva?
Claro que isso é possível. É nisso que GObject , a estrutura na qual todo o GTK + e GNOME se baseia, faz.
A sub-biblioteca C stdio FILE é um excelente exemplo de como criar abstração, encapsulamento e modularidade em C. não adulterado.
Herança e polimorfismo - os outros aspectos muitas vezes considerados essenciais para OOP - não necessariamente fornecer os ganhos de produtividade que eles prometem e razoáveis argumentos têm sido feitos que eles podem realmente prejudicar o desenvolvimento e pensar sobre o domínio do problema.
Exemplo trivial com um animal e um cachorro: Você reflete o mecanismo de tabela de tabelas do C ++ (de qualquer maneira). Você também separa alocação e instanciação (Animal_Alloc, Animal_New) para não chamarmos malloc () várias vezes. Também devemos passar explicitamente o this
ponteiro.
Se você executasse funções não virtuais, isso é uma coisa rival. Você simplesmente não as adiciona à vtable e as funções estáticas não requerem um this
ponteiro. A herança múltipla geralmente requer várias vtables para resolver ambiguidades.
Além disso, você deve poder usar setjmp / longjmp para manipular exceções.
struct Animal_Vtable{
typedef void (*Walk_Fun)(struct Animal *a_This);
typedef struct Animal * (*Dtor_Fun)(struct Animal *a_This);
Walk_Fun Walk;
Dtor_Fun Dtor;
};
struct Animal{
Animal_Vtable vtable;
char *Name;
};
struct Dog{
Animal_Vtable vtable;
char *Name; // Mirror member variables for easy access
char *Type;
};
void Animal_Walk(struct Animal *a_This){
printf("Animal (%s) walking\n", a_This->Name);
}
struct Animal* Animal_Dtor(struct Animal *a_This){
printf("animal::dtor\n");
return a_This;
}
Animal *Animal_Alloc(){
return (Animal*)malloc(sizeof(Animal));
}
Animal *Animal_New(Animal *a_Animal){
a_Animal->vtable.Walk = Animal_Walk;
a_Animal->vtable.Dtor = Animal_Dtor;
a_Animal->Name = "Anonymous";
return a_Animal;
}
void Animal_Free(Animal *a_This){
a_This->vtable.Dtor(a_This);
free(a_This);
}
void Dog_Walk(struct Dog *a_This){
printf("Dog walking %s (%s)\n", a_This->Type, a_This->Name);
}
Dog* Dog_Dtor(struct Dog *a_This){
// Explicit call to parent destructor
Animal_Dtor((Animal*)a_This);
printf("dog::dtor\n");
return a_This;
}
Dog *Dog_Alloc(){
return (Dog*)malloc(sizeof(Dog));
}
Dog *Dog_New(Dog *a_Dog){
// Explict call to parent constructor
Animal_New((Animal*)a_Dog);
a_Dog->Type = "Dog type";
a_Dog->vtable.Walk = (Animal_Vtable::Walk_Fun) Dog_Walk;
a_Dog->vtable.Dtor = (Animal_Vtable::Dtor_Fun) Dog_Dtor;
return a_Dog;
}
int main(int argc, char **argv){
/*
Base class:
Animal *a_Animal = Animal_New(Animal_Alloc());
*/
Animal *a_Animal = (Animal*)Dog_New(Dog_Alloc());
a_Animal->vtable.Walk(a_Animal);
Animal_Free(a_Animal);
}
PS. Isso é testado em um compilador C ++, mas deve ser fácil fazê-lo funcionar em um compilador C.
typedef
dentro de um struct
não é possível em C.
Confira GObject . Ele deve ser OO em C e uma implementação do que você está procurando. Se você realmente deseja OO, vá com C ++ ou alguma outra linguagem OOP. Às vezes, é muito difícil trabalhar com o GObject se você estiver acostumado a lidar com idiomas OO, mas, como qualquer coisa, você se acostumará às convenções e fluxo.
Isso foi interessante de ler. Eu mesmo ponderei a mesma pergunta, e os benefícios de pensar nisso são os seguintes:
Tentar imaginar como implementar conceitos OOP em uma linguagem não OOP me ajuda a entender os pontos fortes da linguagem OOp (no meu caso, C ++). Isso me ajuda a avaliar melhor se deve usar C ou C ++ para um determinado tipo de aplicativo - onde os benefícios de um superam o outro.
Ao navegar na web para obter informações e opiniões sobre isso, encontrei um autor que escrevia o código para um processador incorporado e tinha apenas um compilador C disponível: http://www.eetimes.com/discussion/other/4024626/Object-Oriented -C-Criando-Foundation-Classes-Parte-1
No caso dele, analisar e adaptar os conceitos de POO na linguagem C era uma busca válida. Parece que ele estava aberto a sacrificar alguns conceitos de OOP devido à sobrecarga de desempenho resultante da tentativa de implementá-los em C.
A lição que aprendi é que sim, isso pode ser feito até certo ponto, e sim, existem algumas boas razões para tentar.
No final, a máquina está girando os bits do ponteiro da pilha, fazendo o contador do programa pular e calcular as operações de acesso à memória. Do ponto de vista da eficiência, quanto menos desses cálculos forem feitos pelo seu programa, melhor ... mas às vezes temos que pagar esse imposto para que possamos organizar nosso programa de maneira a torná-lo menos suscetível a erros humanos. O compilador de linguagem OOP se esforça para otimizar ambos os aspectos. O programador deve ter muito mais cuidado ao implementar esses conceitos em uma linguagem como C.
Talvez seja útil consultar a documentação da Apple para o conjunto de APIs da Core Foundation. É uma API C pura, mas muitos dos tipos são conectados a equivalentes de objeto Objective-C.
Você também pode considerar útil o design do próprio Objective-C. É um pouco diferente do C ++, pois o sistema de objetos é definido em termos de funções C, por exemplo, objc_msg_send
para chamar um método em um objeto. O compilador converte a sintaxe entre colchetes nessas chamadas de função, para que você não precise saber disso, mas, considerando sua pergunta, pode ser útil aprender como ele funciona sob o capô.
Existem várias técnicas que podem ser usadas. O mais importante é mais como dividir o projeto. Usamos uma interface em nosso projeto que é declarada em um arquivo .h e a implementação do objeto em um arquivo .c. A parte importante é que todos os módulos que incluem o arquivo .h veem apenas um objeto como void *
ae o arquivo .c é o único módulo que conhece os elementos internos da estrutura.
Algo assim para uma classe que chamamos de FOO como exemplo:
No arquivo .h
#ifndef FOO_H_
#define FOO_H_
...
typedef struct FOO_type FOO_type; /* That's all the rest of the program knows about FOO */
/* Declaration of accessors, functions */
FOO_type *FOO_new(void);
void FOO_free(FOO_type *this);
...
void FOO_dosomething(FOO_type *this, param ...):
char *FOO_getName(FOO_type *this, etc);
#endif
O arquivo de implementação C será algo parecido.
#include <stdlib.h>
...
#include "FOO.h"
struct FOO_type {
whatever...
};
FOO_type *FOO_new(void)
{
FOO_type *this = calloc(1, sizeof (FOO_type));
...
FOO_dosomething(this, );
return this;
}
Então, eu dou o ponteiro explicitamente a um objeto para todas as funções desse módulo. Um compilador C ++ faz isso implicitamente, e em C o escrevemos explicitamente.
Eu realmente uso this
nos meus programas para garantir que meu programa não seja compilado em C ++ e tenha a propriedade de estar em outra cor no meu editor de destaque de sintaxe.
Os campos da FOO_struct podem ser modificados em um módulo e outro módulo nem precisa ser recompilado para ainda ser utilizável.
Com esse estilo, eu já manejo grande parte das vantagens do OOP (encapsulamento de dados). Usando ponteiros de função, é ainda fácil implementar algo como herança, mas, honestamente, é realmente raramente útil.
typedef struct FOO_type FOO_type
vez de um typedef para anular no cabeçalho, obterá o benefício adicional da verificação de tipo, embora ainda não exponha sua estrutura.
Você pode fingir usando ponteiros de função e, de fato, acho que é teoricamente possível compilar programas C ++ em C.
Entretanto, raramente faz sentido forçar um paradigma em um idioma, em vez de escolher um idioma que use um paradigma.
C orientado a objetos, pode ser feito, eu já vi esse tipo de código em produção na Coréia, e foi o monstro mais horrível que eu já vi em anos (foi como no ano passado (2007) que vi o código). Então, sim, isso pode ser feito, e sim, as pessoas já fizeram isso antes, e ainda o fazem até hoje em dia. Mas eu recomendaria C ++ ou Objective-C, ambas são linguagens nascidas de C, com o objetivo de fornecer orientação a objetos com diferentes paradigmas.
Se você está convencido de que uma abordagem OOP é superior para o problema que você está tentando resolver, por que você tentaria resolvê-la com um idioma que não seja OOP? Parece que você está usando a ferramenta errada para o trabalho. Use C ++ ou alguma outra linguagem variante C orientada a objeto.
Se você está perguntando porque está começando a codificar um projeto grande já existente, escrito em C, não deve tentar forçar seus próprios paradigmas de OOP (ou de qualquer outra pessoa) na infraestrutura do projeto. Siga as diretrizes que já estão presentes no projeto. Em geral, APIs limpas e bibliotecas e módulos isolados irá percorrer um longo caminho para ter um OOP- limpo ish design.
Se, depois de tudo isso, você realmente estiver decidido a fazer OOP C, leia isto (PDF).
Sim você pode. As pessoas estavam escrevendo C orientado a objetos antes de C ++ ou Objective-C entrar em cena. O C ++ e o Objective-C foram, em partes, tentativas de pegar alguns dos conceitos de OO usados em C e formalizá-los como parte da linguagem.
Aqui está um programa realmente simples que mostra como você pode fazer algo parecido / é uma chamada de método (existem maneiras melhores de fazer isso. Isso é apenas uma prova de que a linguagem suporta os conceitos):
#include<stdio.h>
struct foobarbaz{
int one;
int two;
int three;
int (*exampleMethod)(int, int);
};
int addTwoNumbers(int a, int b){
return a+b;
}
int main()
{
// Define the function pointer
int (*pointerToFunction)(int, int) = addTwoNumbers;
// Let's make sure we can call the pointer
int test = (*pointerToFunction)(12,12);
printf ("test: %u \n", test);
// Now, define an instance of our struct
// and add some default values.
struct foobarbaz fbb;
fbb.one = 1;
fbb.two = 2;
fbb.three = 3;
// Now add a "method"
fbb.exampleMethod = addTwoNumbers;
// Try calling the method
int test2 = fbb.exampleMethod(13,36);
printf ("test2: %u \n", test2);
printf("\nDone\n");
return 0;
}
Um pequeno código OOC para adicionar:
#include <stdio.h>
struct Node {
int somevar;
};
void print() {
printf("Hello from an object-oriented C method!");
};
struct Tree {
struct Node * NIL;
void (*FPprint)(void);
struct Node *root;
struct Node NIL_t;
} TreeA = {&TreeA.NIL_t,print};
int main()
{
struct Tree TreeB;
TreeB = TreeA;
TreeB.FPprint();
return 0;
}
Venho cavando isso há um ano:
Como o sistema GObject é difícil de usar com C puro, tentei escrever algumas macros agradáveis para facilitar o estilo OO com C.
#include "OOStd.h"
CLASS(Animal) {
char *name;
STATIC(Animal);
vFn talk;
};
static int Animal_load(Animal *THIS,void *name) {
THIS->name = name;
return 0;
}
ASM(Animal, Animal_load, NULL, NULL, NULL)
CLASS_EX(Cat,Animal) {
STATIC_EX(Cat, Animal);
};
static void Meow(Animal *THIS){
printf("Meow!My name is %s!\n", THIS->name);
}
static int Cat_loadSt(StAnimal *THIS, void *PARAM){
THIS->talk = (void *)Meow;
return 0;
}
ASM_EX(Cat,Animal, NULL, NULL, Cat_loadSt, NULL)
CLASS_EX(Dog,Animal){
STATIC_EX(Dog, Animal);
};
static void Woof(Animal *THIS){
printf("Woof!My name is %s!\n", THIS->name);
}
static int Dog_loadSt(StAnimal *THIS, void *PARAM) {
THIS->talk = (void *)Woof;
return 0;
}
ASM_EX(Dog, Animal, NULL, NULL, Dog_loadSt, NULL)
int main(){
Animal *animals[4000];
StAnimal *f;
int i = 0;
for (i=0; i<4000; i++)
{
if(i%2==0)
animals[i] = NEW(Dog,"Jack");
else
animals[i] = NEW(Cat,"Lily");
};
f = ST(animals[0]);
for(i=0; i<4000; ++i) {
f->talk(animals[i]);
}
for (i=0; i<4000; ++i) {
DELETE0(animals[i]);
}
return 0;
}
Aqui está o site do meu projeto (não tenho tempo suficiente para escrever en. Doc, no entanto, o documento em chinês é muito melhor).
Há um exemplo de herança usando C na palestra de Jim Larson em 1996, apresentada no Seminário de Programação da Hora do Almoço da Seção 312 aqui: C de nível alto e baixo .
Quais artigos ou livros são bons para usar os conceitos de POO em C?
As interfaces e implementações C de Dave Hanson são excelentes em encapsulamento e nomeação e muito boas no uso de ponteiros de função. Dave não tenta simular herança.
Uma coisa que você pode querer fazer é analisar a implementação do kit de ferramentas Xt para X Window . Claro que está demorando muito tempo no dente, mas muitas das estruturas usadas foram projetadas para funcionar de maneira OO no C. tradicional. Geralmente, isso significa adicionar uma camada extra de indireção aqui e ali e projetar estruturas para se sobreporem.
Você pode realmente fazer muitas coisas no caminho de OO situadas em C dessa maneira, mesmo que pareça que algumas vezes, os conceitos de OO não tenham surgido totalmente da mente de #include<favorite_OO_Guru.h>
. Eles realmente constituíam muitas das melhores práticas estabelecidas da época. As linguagens e sistemas OO apenas destilam e amplificam partes do zeitgeist de programação do dia.
A resposta para a pergunta é 'Sim, você pode'.
O kit C (OOC) orientado a objetos é para aqueles que desejam programar de maneira orientada a objetos, mas também mantém o bom e velho C. OOC implementa classes, herança única e múltipla, tratamento de exceções.
Recursos
• Utiliza apenas macros e funções C, sem necessidade de extensões de idioma! (ANSI-C)
• Código-fonte fácil de ler para o seu aplicativo. Foi tomado cuidado para tornar as coisas o mais simples possível.
• Herança única de classes
• Herança múltipla por interfaces e mixins (desde a versão 1.3)
• Implementando exceções (em puro C!)
• Funções virtuais para aulas
• Ferramenta externa para fácil implementação de classe
Para mais detalhes, visite http://ooc-coding.sourceforge.net/ .
Parece que as pessoas estão tentando emular o estilo C ++ usando C. Minha opinião é que fazer programação orientada a objetos C é realmente fazer programação orientada a estruturas. No entanto, você pode obter coisas como ligação tardia, encapsulamento e herança. Para herança, você define explicitamente um ponteiro para as estruturas básicas em sua subestrutura e esta é obviamente uma forma de herança múltipla. Você também precisará determinar se o seu
//private_class.h
struct private_class;
extern struct private_class * new_private_class();
extern int ret_a_value(struct private_class *, int a, int b);
extern void delete_private_class(struct private_class *);
void (*late_bind_function)(struct private_class *p);
//private_class.c
struct inherited_class_1;
struct inherited_class_2;
struct private_class {
int a;
int b;
struct inherited_class_1 *p1;
struct inherited_class_2 *p2;
};
struct inherited_class_1 * new_inherited_class_1();
struct inherited_class_2 * new_inherited_class_2();
struct private_class * new_private_class() {
struct private_class *p;
p = (struct private_class*) malloc(sizeof(struct private_class));
p->a = 0;
p->b = 0;
p->p1 = new_inherited_class_1();
p->p2 = new_inherited_class_2();
return p;
}
int ret_a_value(struct private_class *p, int a, int b) {
return p->a + p->b + a + b;
}
void delete_private_class(struct private_class *p) {
//release any resources
//call delete methods for inherited classes
free(p);
}
//main.c
struct private_class *p;
p = new_private_class();
late_bind_function = &implementation_function;
delete_private_class(p);
ajuntar com c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj
.
Portanto, o conselho é seguir um estilo C puro e não tentar forçar um estilo C ++. Além disso, dessa maneira se presta a uma maneira muito limpa de criar uma API.
Consulte http://slkpg.byethost7.com/instance.html para mais uma reviravolta no OOP em C. Ele enfatiza os dados da instância para reentrada usando apenas o C. nativo. A herança múltipla é feita manualmente usando wrappers de função. A segurança do tipo é mantida. Aqui está uma pequena amostra:
typedef struct _peeker
{
log_t *log;
symbols_t *sym;
scanner_t scan; // inherited instance
peek_t pk;
int trace;
void (*push) ( SELF *d, symbol_t *symbol );
short (*peek) ( SELF *d, int level );
short (*get) ( SELF *d );
int (*get_line_number) ( SELF *d );
} peeker_t, SlkToken;
#define push(self,a) (*self).push(self, a)
#define peek(self,a) (*self).peek(self, a)
#define get(self) (*self).get(self)
#define get_line_number(self) (*self).get_line_number(self)
INSTANCE_METHOD
int
(get_line_number) ( peeker_t *d )
{
return d->scan.line_number;
}
PUBLIC
void
InitializePeeker ( peeker_t *peeker,
int trace,
symbols_t *symbols,
log_t *log,
list_t *list )
{
InitializeScanner ( &peeker->scan, trace, symbols, log, list );
peeker->log = log;
peeker->sym = symbols;
peeker->pk.current = peeker->pk.buffer;
peeker->pk.count = 0;
peeker->trace = trace;
peeker->get_line_number = get_line_number;
peeker->push = push;
peeker->get = get;
peeker->peek = peek;
}
Estou um pouco atrasado para a festa, mas quero compartilhar minha experiência sobre o assunto: hoje trabalho com coisas incorporadas e o único compilador (confiável) que tenho é C, para que eu queira aplicar a orientação a objetos abordagem em meus projetos incorporados escritos em C.
A maioria das soluções que eu vi até agora usa tipografias pesadamente, por isso perdemos a segurança de tipos: o compilador não ajudará se você cometer um erro. Isso é completamente inaceitável.
Requisitos que tenho:
Expliquei minha abordagem em detalhes neste artigo: Programação orientada a objetos em C ; além disso, existe um utilitário para geração automática de código padrão para classes base e derivadas.
Construí uma pequena biblioteca onde tentei isso e para mim funciona muito bem. Então, pensei em compartilhar a experiência.
https://github.com/thomasfuhringer/oxygen
A herança única pode ser implementada facilmente usando uma estrutura e estendendo-a para todas as outras classes filho. Uma conversão simples para a estrutura pai torna possível usar métodos pai em todos os descendentes. Desde que você saiba que uma variável aponta para uma estrutura que contém esse tipo de objeto, você sempre pode converter para a classe raiz e fazer introspecção.
Como foi mencionado, os métodos virtuais são um pouco mais complicados. Mas eles são factíveis. Para simplificar, eu apenas uso uma matriz de funções na estrutura de descrição de classe que cada classe filho copia e repovoa slots individuais, quando necessário.
A herança múltipla seria bastante complicada de implementar e tem um impacto significativo no desempenho. Então eu deixo. Considero desejável e útil, em alguns casos, modelar de maneira limpa as circunstâncias da vida real, mas provavelmente 90% dos casos a herança única cobre as necessidades. E a herança única é simples e não custa nada.
Também não me importo com a segurança do tipo. Eu acho que você não deve depender do compilador para evitar erros de programação. E protege você apenas de uma parte bastante pequena dos erros de qualquer maneira.
Normalmente, em um ambiente orientado a objetos, você também deseja implementar a contagem de referências para automatizar o gerenciamento de memória na medida do possível. Então, eu também coloquei uma contagem de referência na classe raiz "Object" e algumas funcionalidades para encapsular a alocação e desalocação da memória heap.
É tudo muito simples e enxuto e me fornece o essencial do OO sem me forçar a lidar com o monstro que é C ++. E mantenho a flexibilidade de permanecer na terra C, o que, entre outras coisas, facilita a integração de bibliotecas de terceiros.
Proponho usar o Objective-C, que é um superconjunto de C.
Embora o Objective-C tenha 30 anos, ele permite escrever um código elegante.
Sim, mas nunca vi alguém tentar implementar qualquer tipo de polimorfismo com C.