问题
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