"Se você realmente quer açúcar OO - vá usar C ++" - foi a resposta imediata que recebi de um dos meus amigos quando perguntei isso. Eu sei que duas coisas estão completamente erradas aqui. Primeiro OO NÃO é 'açúcar', e segundo, C ++ NÃO absorveu C.
Precisamos escrever um servidor em C (o front-end para o qual será em Python) e, portanto, estou explorando maneiras melhores de gerenciar grandes programas em C.
Modelar um sistema grande em termos de objetos e interações com objetos o torna mais gerenciável, sustentável e extensível. Mas quando você tenta traduzir esse modelo em C, que não contém objetos (e tudo o mais), você é desafiado a tomar algumas decisões importantes.
Você cria uma biblioteca personalizada para fornecer as abstrações de OO necessárias ao seu sistema? Coisas como objetos, encapsulamento, herança, polimorfismo, exceções, pub / sub (eventos / sinais), namespaces, introspecção etc. (por exemplo, GObject ou COS ).
Ou, você apenas usa as construções ( struct
e funções) básicas de C para aproximar todas as suas classes de objetos (e outras abstrações) de maneiras ad-hoc. (por exemplo, algumas das respostas para esta pergunta no SO )
A primeira abordagem fornece uma maneira estruturada de implementar todo o modelo em C. Mas também adiciona uma camada de complexidade que você precisa manter. (Lembre-se de que a complexidade era o que queríamos reduzir usando objetos em primeiro lugar).
Não sei sobre a segunda abordagem e qual é a eficácia em aproximar todas as abstrações que você pode precisar.
Portanto, minhas perguntas simples são: Quais são as melhores práticas para realizar um design orientado a objetos no C. Lembre-se de que não estou perguntando COMO fazê-lo. Esta e esta pergunta falam sobre isso, e há até um livro sobre isso. O que mais me interessa são alguns conselhos / exemplos realistas que abordam os problemas reais que aparecem quando isso acontece.
Nota: por favor, não avise por que C não deve ser usado em favor de C ++. Passamos bem além desse estágio.
fonte
extern "C"
e possa ser usada no python. Você pode fazer isso manualmente ou o SWIG pode ajudá-lo com isso. Portanto, o desejo de frontend em python não é motivo para não usar C ++. Isso não é tão dizer que não existem razões válidas para querer ficar com C.Respostas:
Da minha resposta a Como devo estruturar projetos complexos em C (não OO, mas sobre o gerenciamento da complexidade em C):
Da minha resposta a Quais são as convenções de nomenclatura típicas para funções públicas e privadas do OO C (acho que essa é uma prática recomendada):
Além disso, muitas ferramentas UML são capazes de gerar código C a partir de diagramas UML. Um de código aberto é o Topcased .
fonte
Eu acho que você precisa diferenciar OO e C ++ nesta discussão.
A implementação de objetos é possível em C e é muito fácil - basta criar estruturas com ponteiros de função. Essa é a sua "segunda abordagem", e eu iria com ela. Outra opção seria não usar ponteiros de função na estrutura, mas passar a estrutura dos dados para as funções em chamadas diretas, como um ponteiro de "contexto". Isso é melhor, IMHO, pois é mais legível, rastreável com mais facilidade e permite adicionar funções sem alterar a estrutura (fácil de herdar se nenhum dado for adicionado). Na verdade, é assim que o C ++ geralmente implementa o
this
ponteiro.O polimorfismo se torna mais complicado porque não há suporte embutido à herança e abstração; portanto, você terá que incluir a estrutura pai na classe filho ou fazer muitas pastas de cópias, qualquer uma dessas opções é francamente horrível, mesmo que tecnicamente simples. Enorme quantidade de bugs esperando para acontecer.
As funções virtuais podem ser facilmente alcançadas através de ponteiros de função apontando para diferentes funções, conforme necessário, novamente - muito propenso a erros quando feito manualmente, muito trabalho tedioso na inicialização desses ponteiros corretamente.
Quanto a namespaces, exceções, modelos, etc - acho que se você estiver limitado a C -, deve desistir deles. Eu trabalhei em escrever OO em C, não faria isso se eu tivesse uma escolha (naquele local de trabalho, literalmente, lutei para introduzir o C ++, e os gerentes ficaram "surpresos" com a facilidade de integrá-lo com o restante dos módulos C no final.).
Escusado será dizer que se você pode usar C ++ - use C ++. Não há motivo real para não fazê-lo.
fonte
Aqui estão os conceitos básicos de como criar orientação a objetos em C
1. Criando objetos e encapsulamento
Geralmente - cria-se um objeto como
object_instance = create_object_typex(parameter);
Os métodos podem ser definidos de uma das duas maneiras aqui.
Observe que, na maioria dos casos,
object_instance (or object_instance_private_data)
é retornado é do tipovoid *.
O aplicativo não pode ser referência a membros ou funções individuais disso.Além disso, cada método usa esses object_instance para o método subsequente.
2. Polimorfismo
Podemos usar muitas funções e ponteiros de função para substituir determinadas funcionalidades em tempo de execução.
por exemplo - todos os object_methods são definidos como um ponteiro de função que pode ser estendido aos métodos público e privado.
Também podemos aplicar a sobrecarga de funções em um sentido limitado, usando um método
var_args
muito semelhante ao número variável de argumentos definidos em printf. Sim, isso não é tão flexível em C ++ - mas é o caminho mais próximo.3. Definindo herança
Definir herança é um pouco complicado, mas é possível fazer o seguinte com estruturas.
aqui o
doctor
eengineer
é derivado da pessoa e é possível tipcast para um nível superior, digamosperson
.O melhor exemplo disso é usado no GObject e objetos derivados.
4. Criando classes virtuais Estou citando um exemplo da vida real de uma biblioteca chamada libjpeg usada por todos os navegadores para decodificação de jpeg. Ele cria uma classe virtual chamada error_manager que o aplicativo pode criar instância concreta e fornecer de volta -
Observe aqui que o JMETHOD se expande em um ponteiro de função por meio de uma macro que precisa ser carregada com os métodos corretos, respectivamente.
Eu tentei dizer muitas coisas sem muitas explicações individuais. Mas espero que as pessoas possam experimentar suas próprias coisas. No entanto, minha intenção é apenas mostrar como as coisas são mapeadas.
Além disso, haverá muitos argumentos de que isso não será exatamente a propriedade verdadeira do equivalente em C ++. Eu sei que OO em C - não será tão rigoroso com sua definição. Mas trabalhar dessa maneira entenderia alguns dos princípios fundamentais.
O importante não é que o OO seja tão rigoroso quanto em C ++ e JAVA. É que se pode organizar estruturalmente o código com o pensamento OO em mente e operá-lo dessa maneira.
Eu recomendo fortemente que as pessoas vejam o design real do libjpeg e os seguintes recursos
uma. Programação orientada a objetos em C
b. este é um bom lugar onde as pessoas trocam idéias
c. e aqui está o livro completo
fonte
A orientação a objetos se resume a três coisas:
1) Projeto de programa modular com classes autônomas.
2) Proteção de dados com encapsulamento privado.
3) Herança / polimorfismo e várias outras sintaxes úteis, como construtores / destruidores, modelos etc.
1 é de longe o mais importante, e também é completamente independente do idioma, é tudo sobre o design do programa. Em C, você faz isso criando "módulos de código" autônomos que consistem em um arquivo .h e um arquivo .c. Considere isso como o equivalente a uma classe OO. Você pode decidir o que deve ser colocado dentro deste módulo através do senso comum, UML ou qualquer outro método de design de OO que você está usando para programas C ++.
2 também é bastante importante, não apenas para proteger contra o acesso intencional a dados privados, mas também para proteger contra acesso não intencional, ou seja, "confusão de espaço de nome". O C ++ faz isso de maneiras mais elegantes que o C, mas ainda pode ser alcançado em C usando a palavra-chave static. Todas as variáveis que você declararia como privadas em uma classe C ++, deveriam ser declaradas como estáticas em C e colocadas no escopo do arquivo. Eles são acessíveis apenas dentro de seu próprio módulo de código (classe). Você pode escrever "setters / getters" como faria em C ++.
3 é útil, mas não necessário. Você pode escrever programas OO sem herança ou sem construtores / destruidores. É bom ter essas coisas, elas certamente podem tornar os programas mais elegantes e talvez também mais seguros (ou o contrário, se usados de maneira descuidada). Mas eles não são necessários. Como o C não suporta nenhum desses recursos úteis, você simplesmente terá que ficar sem eles. Os construtores podem ser substituídos por funções init / destruct.
A herança pode ser feita através de vários truques de estrutura, mas eu desaconselharia, pois provavelmente tornará seu programa mais complexo sem nenhum ganho (a herança em geral deve ser cuidadosamente aplicada, não apenas em C, mas em qualquer idioma).
Finalmente, todos os truques de OO podem ser feitos no livro de C. Axel-Tobias Schreiner "Programação orientada a objetos com ANSI C", do início dos anos 90, prova isso. No entanto, eu não recomendaria esse livro a ninguém: ele adiciona uma complexidade desagradável e estranha aos seus programas C que simplesmente não vale a pena. (O livro está disponível gratuitamente aqui para aqueles que ainda estão interessados, apesar do meu aviso.)
Portanto, meu conselho é implementar 1) e 2) acima e pular o resto. É uma maneira de escrever programas em C que tem se mostrado bem-sucedido há mais de 20 anos.
fonte
Tomando emprestada alguma experiência dos vários tempos de execução do Objective-C, escrever um recurso de OO dinâmico e polimórfico em C não é muito difícil (tornando-o rápido e fácil de usar, por outro lado, parece ainda estar em andamento após 25 anos). No entanto, se você implementar um recurso de objeto no estilo Objective-C sem estender a sintaxe da linguagem, o código com o qual você termina é bastante confuso:
objc_msgSend(object, selector, …)
. Ao saber de qual classe o objeto é uma instância, ele pode encontrar a implementação correspondente ao seletor e, assim, executar a função correta.Isso tudo faz parte de uma biblioteca OO de uso geral projetada para permitir que vários desenvolvedores usem e estendam as classes uns dos outros, portanto, pode ser um exagero para o seu próprio projeto. Muitas vezes, projetei projetos C como projetos orientados a classe "estáticos", usando estruturas e funções: - cada classe é a definição de uma estrutura C que especifica o layout ivar - cada instância é apenas uma instância da estrutura correspondente - os objetos não podem ser "messaged", mas funções semelhantes a métodos
MyClass_doSomething(struct MyClass *object, …)
são definidas. Isso torna as coisas mais claras no código do que a abordagem ObjC, mas tem menos flexibilidade.O ponto de equilíbrio das mentiras depende do seu próprio projeto: parece que outros programadores não estarão usando suas interfaces C, portanto a escolha se resume às preferências internas. Obviamente, se você decidir algo como a biblioteca de tempo de execução objc, existem bibliotecas de tempo de execução objc entre plataformas que serão atendidas.
fonte
O GObject não esconde realmente nenhuma complexidade e apresenta uma complexidade própria. Eu diria que a coisa ad-hoc é mais fácil do que o GObject, a menos que você precise do material avançado do GObject, como sinais ou o mecanismo de interface.
A coisa é um pouco diferente com o COS, pois ele vem com um pré-processador que estende a sintaxe C com algumas construções OO. Existe um pré-processador semelhante ao GObject, o G Object Builder .
Você também pode experimentar a linguagem de programação Vala , que é uma linguagem de alto nível que é compilada em C e de uma maneira que permite o uso de bibliotecas Vala a partir de código C simples. Ele pode usar o GObject, sua própria estrutura de objetos ou a maneira ad-hoc (com recursos limitados).
fonte
Primeiro, a menos que seja um dever de casa ou não exista um compilador C ++ para o dispositivo de destino. Não acho que você precise usar C, você pode fornecer uma interface C com ligação C a partir do C ++ com bastante facilidade.
Em segundo lugar, veria o quanto você dependerá de polimorfismo e exceções e os outros recursos que as estruturas podem fornecer, se não forem estruturas muito simples com funções relacionadas, será muito mais fácil do que uma estrutura com todos os recursos, se uma parte significativa de seu o design precisa deles, em seguida, explique e use uma estrutura para que você não precise implementar os recursos por conta própria.
Se você ainda não tem um design para tomar a decisão, faça um pico e veja o que o código diz.
por fim, não precisa ser uma opção ou outra (embora, se você for um framework desde o início, você também possa segui-lo), deve ser possível começar com estruturas simples para as partes simples e adicionar apenas bibliotecas como necessário.
EDIT: Então é uma decisão da gerência certa, vamos ignorar o primeiro ponto então.
fonte