How to reuse the following codes without macro?

Viewed 65

(C lang) I have the following two functions to compare strings , like memcmp. Using uint8_t[] and uint16_t[] to store different strings(8 bit strings and wide strings).

static int memcmp16_8(const uint16_t *s1, const uint8_t *s2, int len)
{
    int c, i;
    for(i = 0; i < len; i++) {
        c = s1[i] - s2[i];
        if (c != 0) {
            return c;
        }
    }
    return 0;
}

static int memcmp16(const uint16_t *s1, const uint16_t *s2, int len)
{
    int c, i;
    for(i = 0; i < len; i++) {
        c = s1[i] - s2[i];
        if (c != 0) {
            return c;
        }
    }
    return 0;
}

The same code looks bad. But I don't know how to reuse codes without macro.

3 Answers

With a 4th parameter specifying the actual interpretation of s2's datatype...

static int memcmp16(const uint16_t *s1, const uint16_t *s2, size_t len, int s2Actual) {
    int rval = 0;
    size_t i = 0;
    if( s2Actual == 2 ) {
        while( i < len && s1[i] == s2[i] ) i++;
        rval = i == len ? 0 : s1[i] - s2[i];
    } else { // else 1
        uint8_t *ss2 = (uint8_t *)s2; // downcasting s2 to a single byte pointer
        while( i < len && s1[i] == ss2[i] ) i++;
        rval = i == len ? 0 : s1[i] - ss2[i];
    }
    return rval;
}

EDIT:
How to distinguish first solution as slightly easier to scan & maintain.

Then the calling function could be quite easy to validate at a glance:

// param 3 - count of array elements
// param 4 - 1 for uint8_t, 2 for uint16_t
res = memcmp16( a1, a2, sizeof a1/sizeof a1[0], sizeof a2[0] );

Or, just making your code more compact...

static int memcmp16(const uint16_t *s1, const uint16_t *s2, size_t len) {
    size_t i = 0;
    while( i < len && s1[i] == s2[i] ) i++;
    return i == len ? 0 : s1[i] - s2[i];
}

static int memcmp16_8(const uint16_t *s1, const uint8_t *s2, size_t len) {
    size_t i = 0;
    while( i < len && s1[i] == s2[i] ) i++;
    return i == len ? 0 : s1[i] - s2[i];
}

Reusing code without a macro is achieved in a dialect of C called C++ using templates.

#include <cstdio>

template <typename left_char, typename right_char>
int compare(left_char *l, right_char *r, size_t n)
{
   if (n == 0)
     return 0;
     
   if (*r < *l)
     return -1;

   if (*r > *l)
     return 1;

   return compare(l + 1, r + 1, n - 1);     
}


int main()
{
   std::printf("%d\n", compare(L"abc", "abc", 3));
   std::printf("%d\n", compare("abc", L"abc", 3));
   std::printf("%d\n", compare("alpha", L"bravo", 5));
   std::printf("%d\n", compare(L"alpha", "bravo", 5));
   std::printf("%d\n", compare("bravo", L"alpha", 5));
   std::printf("%d\n", compare(L"bravo", "alpha", 5));
}

Output:

0
0
1
1
-1
-1

If you want to reuse code syntax for different types in C, you will have to resort to macros.

Starting in the C11 dialect of ISO C, there is something called _Generic to help with generic programming; however, macros will still be involved in order to hide it.

You could extract the different code into separate functions, and pass that to the general function as a parameter. It would look like this:

#include <stdint.h>
#include <stdio.h>

typedef int (*cmpxy_func)(const void *s1, const void *s2, const int i);

static int cmp16_8(const void *s1, const void *s2, const int i) {
    const uint16_t *ss1 = (uint16_t *)s1;
    const uint8_t *ss2 = (uint8_t *)s2;

    return ss1[i] - ss2[i];
}

static int cmp16_16(const void *s1, const void *s2, const int i) {
    const uint16_t *ss1 = (uint16_t *)s1;
    const uint16_t *ss2 = (uint16_t *)s2;

    return ss1[i] - ss2[i];
}

static int memcmpxy(
    const void *s1,
    const void *s2,
    const size_t len,
    cmpxy_func cmpxy
) {
    int c, i;
    for(i = 0; i < len; i++) {
        c = cmpxy(s1, s2, i);
        if (c != 0) {
            return c;
        }
    }
    return 0;
}

int main(const int argc, const char * const argv[]) {
    const uint16_t s1[] = {1, 2, 3, 4};
    const uint8_t s2_8[] = {1, 2, 3, 4};
    const uint16_t s2_16[] = {1, 2, 3, 4};

    printf("cmp16_8: %d\n", memcmpxy(s1, s2_8, 4, cmp16_8));
    printf("cmp16_16: %d\n", memcmpxy(s1, s2_16, 4, cmp16_16));
}
Related