Code snippets, ideas and events from IT related projects

by Robert Gawron

Programowanie obiektowe w C

Różnie można określić to, czym jest programowanie obiektowe (a raczej zorientowane obiektowo), jednym z podejść jest określenie możliwych typów bytów w naszej aplikacji, utworzenie ich obiektów i zmuszenie ich do wykonania zadania poprzez wysyłanie doń komunikatów (tzn. wywołanie ich procedur).

Programowanie obiektowe, jak i imperatywne są metodami do osiągnięcia celu i w zależności od problemu czasem może być lesze jedno a czasem drugie. Dodatkowo paradygmatów prócz dwójki wymienionych jest jeszcze sporo więcej, np. SQL i Prolog jest przykładem programowania deklaratywnego. BTW użyłem w tym kontekście zamiennie słów programowanie i paradygmat, miałem racje? Zainteresowanych odsyłam do wyszukiwarki pod hasłem programming paradigm.

Wydawać by się mogło (przynajmniej ja bardzo długo tak sądziłem) iż obiektowo pisać można tylko w języku obiektowym, jednak to nie prawda - przykładem jest chociażby Perl, kiedyś w necie natknąłem się na tutorial o pr. obiektowym w asmie ale nie pamiętam linka.

Przykładem obiektowo napisanego projektu w C jest redland, jako, że jest to projekt open source, możemy ściągnąć jego kod ku bliższej analizie (a ja mogę tu pokazać przykład example3.c z katalogu examples):

   1  librdf_world* world;
   2    librdf_storage *storage;
   3    librdf_model* model;
   4    librdf_statement* statement;
   5    
   6    world=librdf_new_world();
   7    librdf_world_open(world);
   8  
   9    model=librdf_new_model(world, storage=librdf_new_storage(world, "hashes", "test", "hash-type='bdb',dir='.'"), NULL);
  10  
  11    librdf_model_add_statement(model, 
  12                               statement=librdf_new_statement_from_nodes(world, librdf_new_node_from_uri_string(world, (const unsigned char*)"http://www.dajobe.org/"),
  13                                                               librdf_new_node_from_uri_string(world, (const unsigned char*)"http://purl.org/dc/elements/1.1/creator"),
  14                                                               librdf_new_node_from_literal(world, (const unsigned char*)"Dave Beckett", NULL, 0)
  15                                                               )
  16                               );
  17  
  18    librdf_free_statement(statement);
  19  
  20    librdf_model_print(model, stdout);
  21    
  22    librdf_free_model(model);
  23    librdf_free_storage(storage);
  24  
  25    librdf_free_world(world);
  26  
  27  #ifdef LIBRDF_MEMORY_DEBUG
  28    librdf_memory_report(stderr);
  29  #endif
  30  	

Tworzymy obiekt (danej struktury, która posiada w sobie zaszyte pola, będące polami obiektu) i inicjujemy go (odpowiednik konstruktora). Od tego momentu możemy wykonywać operacje na tym obiekcie (funkcja przyjmująca jako pierwszy argument obiekt na który wykona swoją operację).

Oczywiście możemy mieć dużo różnych obiektów, które będą między na siebie oddziaływać i coś wykonywać wspólnie, jak w zwykłym programie obiektowym. Na końcu niszczymy obiekt (zwalniamy pamięć etc).

Żeby przyjrzeć się bliżej jak to działa napisałem sam (całkiem osobny) program/kod/jakkolwiek zwać - DAO ale zabrakło mi motywacji i jest tylko moduł do obsługi postgresa =(

rysunek DAO

Możesz ominąć ten akapit jeśli wiesz, po co jest DAO. Powyżej znajduje się schemat jak zbudowane jest takie coś, jest biblioteka,API umożliwiające wykonywanie zapytań SQL i zwracanie wyników niezależnie od zastosowanej bazy danych (przynajmniej w teorii). Plusem jest to, iż interfejs (w znaczeniu: to jakie mamy funkcje, ich parametry etc) jest wspólny dla wszystkich baz danych, nie trzeba się przekopywać przez dokumentację za każdym razem gdy korzystamy z nowej bazy.

Co do tego, jak zaimplementowana jest obiektowość w kodzie polecam książkę, która jest podana w bibliografii - nie będę jej tu przepisywał :) Dokumentację napisałem w kodzie i później wygenerowałem doxygenem do formatu HTML (nienawidzę LaTeXa) - jest dostępna online (link na końcu posta)

Całość składa się z klasy DAO, która jest interfejsem tego przybytku, pobiera ona zapytania z kodu klienckiego (tu prosty main.c). Następie wykonuje to zapytanie przesyła już do modułu konkretnej bazy danych. I tu zonk bo nie ma żadnej logiki, jest na sztywno jeden jedyny moduł bo nie chciało mi się już tego dalej rozwijać =(

Wynik zwracany jest w osobnym obiekcie (to jak jest trzymam woła o pomstę do nieba :P). W kodzie aplikacji mając obiekt w którym jest zapisany wynik możemy się do niego dobrać korzystając z iteratora.

Na koniec jeszcze plik main.c i przykład użycia:

   1  #include <stdio.h>
   2  #include "query-result.h"
   3  #include "dao.h"
   4  
   5  int main(){
   6      // tworze obiekt klasy dao i inicjuje go
   7      void * dao = new(Dao, "psql", "testrollback","rgawron", "", "");
   8      // tworze obiekt bedacy wynikiem zapytania i inicjuje go
   9      void * result = new(QueryResult);
  10  
  11      // na obiekcie dao wykonuje zapytaie do bazy, a wynik umieszczam 
  12      // w obiekcie klsay wynik zapytania
  13      query(dao, "select nazwisko from Osoba", result);
  14  
  15      int isEmpty;
  16      for(isEmpty = 0; isEmpty!=1; ) {
  17          char answer[1000];
  18          // iteruje po krotkach ktore zawiera wynik zapytania
  19          // by wysietlic wszytkie wyniki ktore zwrocilo zapytanie
  20          QueryResult_iterator(result, &answer[0], &isEmpty);
  21          printf("%s\n", answer);
  22      }
  23  
  24      // nie ptrzeba mi juz polaczenia z baza danych ani wynikow 
  25      // zaptania usuwam je wiec by nie bylo wycieku pameci i by 
  26      // nie obciazac bazy danych zbednym polaczeniem
  27      delete(result);
  28      delete(dao);
  29  
  30      printf("La fin de mon monde..\n"); 
  31  }
  32  
rgawron@foo:~/objects/handler$ ./handler_test
Kowalski
Michnik
La fin de mon monde..

Gdyby ktoś chciał rozwinąć w/w DAO by przyjrzeć się pisaniu obiektowo w C nie zaczynając od kodu od zera to można dodać np:

  • napisanie własnego skryptu do generowania zbędnych rzeczy, by podać informacje o tym, jakie pola ma mieć klasa i jakie metody, a reszta by wygenerowała się automatycznie (i by zostało nam "tylko" napisanie ciał tych metod)
  • obsługę innych baz
  • cacheowanie danych, np do pliku
  • wycieki pamięci :P

Bibliografia

Materiały ode mnie

Pingbacks

No pingbacks yet

Comments

avatar
czarodziej , 1.11.2008 21:17, reply
nie no, jak rzadko jestem pod wrazeniem tak dzis jestem. genialne, jeszcze przebadam Twoj kod dokladniej bo to jest bardzo ciekawe, i dziekuje za link do ksiazki, juz wiem co bede czytal w wolnym czasie :) jakkowiek nie bylbym soba gdybym sie do czegos nie dopieprzyl, co ma znaczyc: * (const struct Class **) p = class; czy nie za duzo kombinujesz? ;p
avatar
czarodziej , 1.11.2008 21:18, reply
lipa, 'enterow' nie widac w komentarzu, trzeba dawac ?
avatar
Robert , 2.11.2008 9:09, reply
Fajnie, że notka komuś się przydała =) Co do konstrukcji, wzorowałem się na książce którą, podałem. Entery były dawniej bo pamiętam, że dopisałem ich wyświetlanie w silniku bloga, coś się popsuło przy zmianie skina chyba.. anyway poprawie niedługo. Pozdrawiam!
avatar
czarodziej , 2.11.2008 9:24, reply
coz, widze ze wzorujesz sie na ksiazce, chyba nawet za bardzo, gdyz moim zdaniem jest tam pare rzeczy niegodnych nasladowania, np void * p = costam, to wprowadza komplikacje, bo mnozenie przeciez wyglada tak samo, a mozna zapisac void *p = i wtedy to jest jednoznaczne, dwa pliki naglowkowe, dajesz w nich extern przez co nie mozesz zalaczyc pliku naglowkowego do odpowiadajacego mu pliku .c, niby nic takiego ale: tracisz kontrole zgodnosci deklaracji z definicja funkcji i *dwa razy* musisz pisac ta sama strukture, raz w .c raz w .h - potencjalne zrodlo bledow. ja bym nie dawal tam tego externa a zalaczal plik naglowkowy do odpowiadajacego mu pliku .c a potem tam gdzie extern ma byc uzyty (czyli w innym .c) dopiero go dawal - odrazu jest wiadomo skad to sie wzielo, a nie ze musialem kopac po plikach ;) a co do tej konstrukcji, to uwazam ja za przekombinowana bo to jest rownoznaczne z (const struct Class *)p = calss, prawda ze odrazu widac o co chodzi? (to sa rzeczy ktore mi sie nie spodobaly, ale to jak oprawiasz kod komentarzami jest super! chwala Ci za to, no i sam temat tez jest ciekawy)
avatar
czarodziej , 2.11.2008 9:25, reply
znow zegarka na blogu nie przestawiles, i znow dopiero ja to zauwazam ;)
avatar
Robert , 2.11.2008 8:47, reply
Co do uwag o kodzie - racja, dzięki za nie! Zegarek już poprawiłem :P
avatar
Robert , 2.11.2008 8:48, reply
Patrząc na godziny to odpowiedziałem na komentarz którego jeszcze nie napisano =)

Leave your reply

Let me know what you think

Required. 30 chars of fewer.

Required.

captcha image