Consider an easy-to-use object-oriented "like" representation in C. -Part 2 Interface override

0/2 Introduction void * 1/2 Class representation and inheritance 1/2 2018/07/01 Reconsidered Reconsidered: Class expression and inheritance 2/2 Interface, Override ← Here

This time it's an interface class and overrides. Honestly, after writing Realization of Interface Class There is a part that I am doing object-oriented deep digging because I want to write my favorite story to be described this time.

The concept and implementation method of C language and interface class are common! It is an impression.

** What is an interface class? **

Define only the method and leave the implementation class to each one. It is convenient for the user to change the behavior by selecting the implementation class.

Roughly written in C language,

Interface class ⇒ API definition of dynamic library Implementation class ⇒ Dynamic library body

is not it. The difference is that you can have more than one.

** About the usefulness of interface class representation in C language **

As a realization method, [representation of the previous public method](https://qiita.com/developer-kikikaikai/items/ee782a4865d9186c5d8c#%E3%82%AF%E3%83%A9%E3%82%B9%E3 Written in% 82% 92% E3% 81% A9% E3% 81% 86% E8% A1% A8% E7% 8F% BE% E3% 81% 99% E3% 82% 8B% E3% 81% 8B) I will omit it, but As mentioned there ** When calling a class method in C, if you use this, you have to pass it as an argument ** I wrote.

Then, if you think about implementing many interface class implementation classes, Whether to express the API as a public API or as an interface class with a function pointer is quite different.

Let's do it because it can be done simply with the interface class! However, it's a waste if it's the same as the existing system. Then what kind of case is effective?

** Case 1: When the person who actually uses the interface does not need to be aware of the substance at all **

For example, in a system where modules with the same interface are added or removed, you may create a management class without making the user aware of the interface implementation class. In such a case, it makes sense to introduce an interface class in C language. The example written in [Realization of interface class](https://qiita.com/developer-kikikaikai/items/47f92acbc0a19172b6d4:'interface class') is like that.

The interviewer receives the information of the interviewees as an interface class and asks fixed questions one by one. The interviewer only needs to answer the questions, so it's just like leaving the management of the interviewee to another person.

Organize and explain the code. First, define an interface class called culture_if.

culture.h


struct culture_if {
        char *(*introduce)();//Self-introduction
        char *(*get_name)();//name
        char *(*answer)(int id);//Question
};

Next, define a public API called the people_manager class.

people_manager.h


#include "culture_if"
void * people_manager_new();
int people_manager_get_any_people(void *, struct culture_if *peoples);
void * people_manager_free(void *);

New the culture_if implementation class of those who came today when people_manager was new, Use people_manager_get_any_people to pass the list.

So, the implementation of the interviewer looks like this.

main.c


#include <stdio.h>
#include "people_manager.h"
#include "culture_if"

int main() {
	struct {
		int id;
		char * question;
	} questions[] =
	{
		{FOOD, FOOD_QUESTION},
		{HAPPY, HAPPY_QUESTION},
		{CONPUTER, CONPUTER_QUESTION},
	};

	struct culture_if max_members[10];
	void * people_manager = people_manager_new();
	int member_num = people_manager_get_any_people(max_members);

	printf("[Interviewer]Hello everyone. I look forward to working with you today. First of all, please briefly introduce yourself.\n");

	int i=0;
	for( i = 0 ; i < member_num; i ++ ) {
		printf("  [%Self-introduction of the dth person]\n", i+1);
		printf("\t%s\n", max_members[i].introduce() );
	}

	printf("\n");
	printf("[Interviewer]Thank you very much. Then, I would like to ask questions one by one.\n Please feel free to answer.\n\n");

	int j=0;
	for ( i = 0 ; i < sizeof(questions)/sizeof(questions[0]); i ++ ) {
		printf("Q %d: %s\n", i+1, questions[i].question);
		for( j = 0 ; j < member_num ; j ++ ) {
			printf("  [A:%s]\n", max_members[i].get_name());
			printf("\t%s\n", max_members[i].answer(i));
		}
		printf("\n");
	}

	printf("[Interviewer]That concludes the question. Thank you everyone!\n");
	people_manager_free(people_manager);
	return 0;
}

With this, even if the person to be interviewed changes, the implementation on the interviewer side can be kept as it is. Isn't this kind of usage good?

** Case 2: Add static library plugin by dlopen **

Is it only for Linux? There is a standard function called dlopen / dlsym.

dlfcn.h


#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
void *dlsym(void *handle, const char *symbol);

Excerpt from manpage.

--The function dlopen () loads a dynamic library with the filename specified by the null-terminated string filename and returns an internal "handle" to that dynamic library. --The function dlsym () takes the dynamic library "handle" returned by dlopen () and the null-terminated symbol name string as an argument and returns the address of the memory in which the symbol was loaded.

In short, if you specify the library file with dlopen and then specify the function name with dlsym, you will get a function pointer corresponding to the function. After that, the function of the library can be executed using the function pointer. It's an existing mechanism, but it's exactly the idea of an interface class.

Actually how to use it as an interface implementation class is like this

  1. Define a function pointer that will be an interface class. Let's go with culture.h.
  2. Determine the API name corresponding to each method of the interface class.
  3. Load the library that implements the above 3 APIs with dlopen and set it to the corresponding culture_if with dlsym.

For example, decide the name as follows and ask the person in charge of interface implementation to implement each API and make it a library. --void (* introduce) ()void culture_if_introduce (); // Self-introduction -- char * (* get_name) ()void culture_if_get_name (); // name -- char * (* answer) (int id)void culture_if_answer (); // Question

If you can, load each library with dlopen, and the methods of the interface implementation class are completed.

python


struct culture_if * culture_if_new( chhar * lname /*"Library file name"*/) {
    void * handle = dlopen(lname , flag);
    struct culture_if * culture_if = malloc(sizeof(struct culture_if ));

    culture_if->introduce = dlsym(handle, "culture_if_introduce");
    culture_if->get_name= dlsym(handle, "culture_if_get_name");
    culture_if->answer= dlsym(handle, "culture_if_answer");
    return culture_if;
}

The nice thing about this is that the method name on the implementation class side can also be fixed, and the logic on the management side is also decided. If done well, you can increase the number of interface implementation classes without touching the management side. Perhaps the common OSS with additional plug-ins looks like this.

I'm absolutely addicted to some design pattern, so I think I'll make a sample soon.

** Interface class representation in C language Summary **

--For C language that can handle function pointers freely, interface class representation is an effective means with many uses. ――Applying without thinking has no advantage over the existing C mechanism. ――It's good to have an interface, not just a class.

override

In C language, function pointers were used to represent interfaces. That means overriding is super easy. Just overwrite the pointer.

So, if you define a public method with overrides in mind, it's better to use a function pointer. The sample is omitted because it is only necessary to overwrite it.

** Impression **

It is the impression that I wrote for a long time in the 0-2.

--The application of class design in C is very good because it also makes the code easier to read. Implementation representation is a matter of taste --The interface class design in C is very useful. Or rather, you should already be using it without being aware of it. --I'm not good at class inheritance. Isn't it better to express it in the form of aggregation?

I'm not good at it, and there are some parts that aren't written in the object-oriented language itself, If you use the alternative expression method, you can do object-oriented design even in C language! That is my impression.

It's been long, but that's it.

Recommended Posts

Consider an easy-to-use object-oriented "like" representation in C. -Part 2 Interface override
Consider If Programming Was An Anime in Python and C
Realize interface class in C language