Generic programming in C


Generic programming is a style or paradigm of programming language. Generics allow programmers to use some later specified types when writing code in a strongly typed programming language, and specify these types as parameters during instantiation. C++ supports generic programming, that is, templates, such as:

// 来源:公众号【 编程珠玑】
// 作者:守望先生
#include <iostream>
template <class T>
T add(T a,T b){
    
    
  T ret = a + b;
  std::cout<< a << " + " << b <<" = " << ret << std::endl;
  return ret;
}
int main(){
    
    
  add(1,2);  // 整数相加
  add(1.2,2.3); // 浮点数相加
  return 0;
}

operation result:

1 + 2 = 3
1.2 + 2.3 = 3.5

As can be seen from the above results, for calling the add function, if the passed in is an integer, it will be calculated according to the integer addition, if it is a floating point number, it will be calculated according to the floating point number. In other words, the add function is not for a specific type (generic).

You can also use overloading to achieve the above functions, but there is a lot of duplicate code.

Does C language support generic programming?

Unfortunately, the C language itself does not support generic programming in the true sense, but it can "implement generic programming" to a certain extent.

_Generic keyword

_Generic is a keyword of C11, through this keyword can have a generic expression:

_Generic((value). int:"int", float:"float",char*:"char*",default:"other type")

What does that mean? If value is of type int, then the value of the expression is "int", and so on. Does it look similar to the switch statement?
According to this example, let's implement a function to print what type of variable or constant is:

// 来源:公众号【编程珠玑】
// 作者:守望先生
#include <stdio.h>
#define TYPE(v) _Generic((v), \
    int:"int", \
    char:"char", \
    float:"float", \
    double:"double", \
    char*:"char*", \
    default:"other type")
int main(void)
{
    
    
    printf("1 + 2 type: %s\n",TYPE(1 + 2));
    printf("1/3 type: %s\n",TYPE(1/3));
    printf("2/3 type: %s\n",TYPE((float)2/3));
    printf("xxx type: %s\n",TYPE("xxx"));
    return 0;
}

Here for convenience, we simplify the generic expression through the define keyword.

operation result:

1 + 2 type: int
1/3 type: int
2/3 type: float                                                        
xxx type: char*

You can see that the result type of the expression can be obtained through TYPE, which is really good news for beginners .

Generic Algorithm

Since the C language has the _Generic keyword, we try to implement the addition in the C++ sample code at the beginning. After reading the above example, I believe you will already know:

// 来源:公众号【编程珠玑】
// 作者:守望先生
#include <stdio.h>
// int类型加法
int addI(int a, int b)
{
    
    
    printf("%d + %d = %d\n",a,b, a + b );
    return (a + b);
}
// double类型加法
double addF(double a, double b)
{
    
    
    printf("%f + %f = %f\n",a,b, a + b );
    return (a + b);
}
void unsupport(int a,int b)
{
    
    
    printf("unsupport type\n");
}
#define ADD(a,b) _Generic((a), \
    int:addI(a,b),\
    double:addF(a,b), \
    default:unsupport(a,b))
int main(void)
{
    
    
    ADD(1 , 2);
    ADD(1.1,2.2);
    return 0;
}

Observing the above code, we noticed:

  • Here, we need to define two types of addition (in fact, through the C++ template, the compiler will help us to complete this thing), because the C language does not support overloading , the function names of the two additions are not same.
  • Since there are two parameters involved, if the two parameters are inconsistent when making type judgments, there may still be compilation problems
  • The caller can use ADD uniformly without distinguishing what type of object is added

Tgmath.h for C99

As mentioned earlier, the _Generic keyword is only available in C11, so what about C99? In fact, tgmath.h provides some generic type macros. If float, double and long double versions are defined in the function of math.h, tgmath will provide a generic type macro. The effect is the same as the previous example, for example:

// 来源:公众号【编程珠玑】
// 作者:守望先生
#include <stdio.h>
#include <tgmath.h>
int main(void)
{
    
    
    float f = 4.0f;
    long double d = 1.44;
    printf("%f\n",sqrt(f)); // 实际上调用了sqrtf
    printf("%Lf\n",sqrt(d)); // 实际上调用了sqrtl
    return 0;
}

Compile and run results:

2.000000
1.200000

But I have to say that the generic macros provided in tgmath are also limited.

void * pointer

As we all know, the void * pointer in the C language is an untyped pointer. From this point of view, it can be regarded as a generic pointer. And its use is very common in the C language. For example, in "Advanced Pointer Topic- Function Pointer ", we introduced the use of the quick sort interface, and its function declaration is like this:

#include <stdlib.h>
void qsort(void *base, size_t nmemb, size_t size,
                  int (*compar)(const void *, const void *));

The library function qsort is actually a generic sorting algorithm, it can sort any type of data. Of course, there is a prerequisite, that is, you need to implement a compar function in accordance with its protocol for comparing sizes.

There are many similar examples like this in the C language, but compared to other languages, such as templates in C++, this so-called generic is indeed a little insignificant.

to sum up

C language grammar basically does not support generic programming, but with the help of the _Generic keyword and some means, generic programming can be realized.

Source: Public Programming Pearls [No.]
Author: Mr. Rye
ID: shouwangxiansheng

Guess you like

Origin blog.csdn.net/weixin_42892101/article/details/111942662