Dynamic type casting in C

和自甴很熟 提交于 2021-02-10 06:31:09

问题


I'm trying to write a function taking as a parameter a buffer (void *), the type size, the type name and the number of elements. The buffer may contain values of a limited number of basic types (int, float, double, ...). Within this function, I would like to be able to, let's say, increment each value. It looks like that :

void increment (void *buffer, int type_size, char *type_name, int n_elements)
{
    for (int i=0;  i<n_elements; i++)
        ((MACRO(type_name))(buffer))[i]++; // ???

    return;
}   

My question is as simple as: how can I "dynamically" cast my buffer? I can add more function parameters if needed. Maybe something can be done using macros but I can't figure this out.


回答1:


If you are dealing in a finite number of specific types for this to work with, you could write a separate function for each type. If you don't want to have to pick the right function for each type, you can use C11's _Generic to select based on the type. As a basic example:

#include <stdio.h>
#include <stdlib.h>

void increment_int(int *, int);
void increment_long_long_int(long long int *, int);

#define increment(buffer, n_elements) _Generic((buffer), \
                int *:  increment_int, \
      long long int *:  increment_long_long_int \
      )(buffer, n_elements)

void increment_long_long_int (long long int *buffer, int n_elements)
{
    for (int i=0;  i<n_elements; i++)
    {
        buffer[i]++;
    }
    return;
}   

void increment_int (int *buffer, int n_elements)
{
    for (int i=0;  i<n_elements; i++)
    {
        buffer[i]++;
    }
    return;
}

int main(void) {
    int buff[20] = {0};
    long long int buff2[20] = {0};
    increment(buff, 20);
    increment(buff2, 20);
    printf("%d\n", buff[5]);
    printf("%lld\n", buff2[8]);
    return EXIT_SUCCESS;
}



回答2:


Use a macro instead:

INCREMENT (buffer, n_elements) \
{\
    for (typeof(n_elements) i=0;  i < n_elements; i++)\
        (buffer)[i]++;\
}

or better (works also for floating point types):

INCREMENT (buffer, n_elements) \
{\
    for (typeof(n_elements) i=0;  i < n_elements; i++)\
        (buffer)[i] += 1;\
}

The typeof(n_elements)(*) is to avoid signed/unsigned comparison problems. The parenthesis around buffer are to prevent problem in case you invoke your macro with, for example, INCREMENT (buffer + 4, 12).

On the calling side it would look something like this:

double buffer[100];
//[...] Initialize here your buffer
INCREMENT (buffer, 100); //Semicolon is optional, but I prefer having it

or

#define BUFFER_SIZE 200
unsigned int* p = malloc(sizeof(*p) * BUFFER_SIZE);
//[...] Initialize here your buffer
INCREMENT (p, BUFFER_SIZE); //Semicolon is optional, but I prefer having it


(*) typeof is actually non standard, a workaround is given by _Generic in C11, details available here.


回答3:


You can only have something silly like this (gcc version):

#include <stdint.h>

void incrementAlmostAnySize(void *ptr, size_t size, size_t element, int sign)
{
    union 
    {
       uint8_t  u8; 
       uint16_t u16;
       uint32_t u32;
       uint64_t u64;
       int8_t  i8; 
       int16_t i16;
       int32_t i32;
       int64_t i64;
    }*uptr = ptr + size * element;   //non gcc : (void *)((uint8_t *)ptr + size * element); 
    if(sign)
    {
        switch(size)
        {
            case 1:
                uptr -> i8++;
                break;
            case 2:
                uptr -> i16++;
                break;
            case 4:
                uptr -> i32++;
                break;
            case 8:
                uptr -> i64++;
                break;
            default:
                break;
        }
    }
    else
    {
                switch(size)
        {
            case 1:
                uptr -> u8++;
                break;
            case 2:
                uptr -> u16++;
                break;
            case 4:
                uptr -> u32++;
                break;
            case 8:
                uptr -> u64++;
                break;
            default:
                break;
        }

    }
}

usage:

char x[1000];

void foo()
{
    incrementAlmostAnySize(x, sizeof(long long), 10, 1);
    incrementAlmostAnySize(x, sizeof(uint32_t), 2, 0);

    for(size_t i = 0; i < sizeof(x) / sizeof(uint64_t); i ++)
        incrementAlmostAnySize(x, sizeof(uint64_t), i, 0);
}


来源:https://stackoverflow.com/questions/51657763/dynamic-type-casting-in-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!