Yusuf Bülbül

An Engineer

Using C Language Like Object Oriented

Türkçe için tıkla

You can describe the engineering in many diffirent perspective, But if I were describing the engineering, I would say that, If you could shematic your work and make parametric adjustments to it in terms of efficiency or design, you’re doing engineering in this case. So even if you’re a master of something, it is a different situation than doing engineering.

 The most important reason of the software world to come to these days is of course the development of object-oriented software. Because an object-oriented design makes easy the  engineering. When you realize this, you are promoted to the rank of expert software developer. If you ask why, in order to make this distinction, you have to be involved in two big projects, one with good and one with bad code design. I’m not talking about a maximum of five to ten thousand lines of code that do simple things. I’m talking about maybe hundreds of thousands of lines of code here. In a project of this magnitude, when you find yourself drowning in a pile of code, you understand the value of object-oriented logic and software design patterns.

Today, embedded systems and kernel programs still use C primarily. Moreover, since unix systems are written in C and constituted the base of many popular languages, it is not possible to decrease the popularity of C Language. However, since C is not object-oriented, it has disadvantages in terms of code design, especially in large projects. So if we can use C++, it makes sense to use C++ instead of C. But most embedded platforms still do not support C++. In this case, even though the C language is not object oriented, we can use it by simulating object oriented languages. This, of course, allows you to schematize and design your code in a more meaningful way. In this article, I will consider using C language in an object-oriented way like C ++.

Let’s start with defining a simple object in C. For example, in C ++, get an object like this;

person.h file;Public:

class Person
{

Public:
    Person();
    ~Person();
    
    std::string name;
    virtual void talk(int Opinion);
    

Private:
    
    const int own_opinion = 1;
    void think(int opinion);

};

person.cpp file;

#include "person.h"

//private function
void Person::think(int Opinion)
{
  if(own_opinion != Opinion)
    std::cout << "person " << name << "disagree" << std::endl;
  else
    std::cout << "person " << name << "agree" << std::endl;

}
    

//public function
void Person::talk(int Opinion)
{
   think(Opinion);
   std::cout << "person " << name << "talking" << endl;
}


Using of object;

#include "person.h"

int main()
{   
    Person person1;
    Person person2;    

    person1.name = "Alice";
    person2.name = "Bob";

    person1.talk(1);
    person2.talk(2);

    return 0;

}

Now, considering that you are familiar with C++ and object oriented programming, let’s try to create this object in C language.

The file person.h will look like this;

#define __person_H
#ifdef __person_H

struct Person
{
    const char *name;   //public variable
    void (*talk)(void *Person, int Opinion); //public function
};

static void think(struct Person *Person, int Opinion); //private
static void talk(void *Person, int Opinion);  //public

// constructer and deconstructer
void initPerson(struct Person *Person);
void deinitPerson(struct Person *Person);

#endif

person.c will look like;

person.c dosyası şu şekilde olacak;

#include "person.h"
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

//private variable defination
static const int own_opinion = 1;



//private function
static void think(struct Person *Person, int Opinion)
{
    if(own_opinion == Opinion)
       printf("Person %s agree\n", Person->name);
    else
       printf("Person %s disagree\n", Person->name);
}



//public function defination
static void talk(void *Person, int Opinion)
{
    struct Person *temp = (struct Person *) Person;
    think(Person, Opinion);
    printf("Person %s talking...\n", temp->name);
}



// constructer and deconstructer
void initPerson(struct Person *Person)
{
    Person->talk = talk;          //define where is the public function in object
}



void deinitPerson(struct Person *Person)  //Deconstructer
{
}

Using of above object in C laguage;

#include <stdio.h>
#include "person.h"

int main()
{
    struct Person person1;
    struct Person person2;

    initPerson(&person1);
    initPerson(&person2);

    person1.name = "Alice";
    person2.name = "Bob";

    person1.talk(&person1, 1);
    person2.talk(&person2, 2);


    deinitPerson(&person1);
    deinitPerson(&person2);

    return 0;
}

output is;

Person Alice agree
Person Alice talking...
Person Bob disagree
Person Bob talking...

We define the public functions of the object with function pointers in the struct.These functions also need to be defined statically in files with the extension “.h” and “.c”. Because functions and variables that are defined statically in C language cannot be accessed from outside the file. The only access to the general functions has to be via the Object Pointer.So we put them in the struct. However, private functions and variables must not be accessible through the object. They should only be accessible from the file or object in which the object is located.T herefore, these functions and variables are not placed in the struct, but are defined as static only in the file “.c”. Eespecially in C we see static functions quite a lot. This is because the static definition in the “.c” file for the private functions and variables is the best match. Therefore, we define everything that within the object as static. The only functions outside this definition are the constructive and destructive functions of the object. Because we are initialize the object with constructive function and destroying in deconstructor function. We can use function like “malloc” for creation on heap or directly on stack. I choosed stack.

Once the object constructor is defined, it is necessary to specify where the public functions are. At the same time, all member functions of the object to which the member function belongs must be taken as parameters. Because the other member functions and variables of the object must be accessed from this function.

That’s how we defined the object. Is that enough? Of course, in order to be sufficient, it is necessary to define parent and child objects called “inheritance”. We can do it ourselves in C. For example, let’s create a Student object that is child object of Person  object.

This operation is pretty easy in C++. We can define this object in C++ like that;

class Student : public Person
{

private:
    void cheat(int Lesson)
    {
        std::cout << "Student "<< name << "cheating on lesson" << Lesson << std::endl;
    }
    std::string favorite_lesson;

public:
    Student();
    ~Student();

    std::string degree;
    void study(int Lesson)
    {
        if(Lesson != favorite_lesson)
            cheat(Lesson);
        else
            std::cout << "Student "<< name << "studying on lesson" << Lesson << std::endl;

    }

    void talk(int Opinion)
    {
        std::cout << name << "talking at " << degree << std::endl;

    }

};

We can write this program in C like that;

student .h will look like this,

#define __student_H
#ifdef __student_H

#include "person.h"

struct Student
{
    struct Person person;

    const char *degree;
    void (*study)(struct Student *Student, int Lesson);
};


static void talk(void *Student, int Opinion);
static void study(struct Student *Student, int Lesson);
static void cheat(struct Student *Student, int Lesson);

void initStudent(struct Student *Student);
void deinitStudent(struct Student *Student);



#endif

student.c will look like;

#include "student.h"
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

const int fovarite_lesson = 1;

static void talk(void *Student, int Opinion)
{
    struct Student *student = (struct Student *)Student;
    printf("%s talking at %s", student->person.name, student->degree);
}



static void cheat(struct Student *Student, int Lesson)
{
    printf("Student %s cheating on lesson %d...\n", Student->person.name, Lesson);
}



static void study(struct Student *Student, int Lesson)
{
    if(Lesson != fovarite_lesson)
        cheat(Student, Lesson);
    else
        printf("Student %s studying on lesson %d...\n", Student->person.name, Lesson);


}



void initStudent(struct Student *Student)
{
    initPerson(&Student->person);
    Student->study = study;
    Student->person.talk = talk;
}


void deinitStudent(struct Student *Student)
{
    deinitPerson(&Student->person);
}

output is;

Student Alice cheating on lesson 3...
Alice talking at High School

Thus, software design patterns can be applied in C language. This brings quality code and hence quality engineering. The above codes and designs are the object designs in C that I have written and created by combining them with different perspectives from many articles. These codes are compiled with “gcc (Debian 7.2.0-19) 7.2.0” and tested on “Kali GNU/Linux Rolling 64-bit”

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Copyright © Tüm Hakları Saklıdır.