I'm writing a function which just make the sum of two values,
but the C type of these values is specified in parameter,
it can be int, unsigned int, char, unsigned char, float ...
I search a solution to do that without making a lot of C code to process to all different mixed cases.
To be clear the code is :
void addition(unsigned char type_data_1_uc, void *value_data_1_ptr,
unsigned char type_data_2_uc, void *value_data_2_ptr,
unsigned char type_result_uc, void *value_result_ptr)
{
/* types :
0 : TYPE_BIT
1 : TYPE_CHAR
2 : TYPE_UNSIGNED_CHAR
3 : TYPE_INT
4 : TYPE_UNSIGNED_INT
5 : TYPE_SHORT_INT
6 : TYPE_UNSIGNED_SHORT_INT
7 : TYPE_LONG
8 : TYPE_UNSIGNED_LONG
9 : TYPE_FLOAT */
/* INT + INT = INT */
if ((type_data_1_uc == 3)
&& (type_data_2_uc == 3)
&& (type_result_uc == 3))
{
*((int *) value_result_ptr) = *((int *) value_data_1_ptr) + *((int *) value_data_2_ptr);
}
/* FLOAT + FLOAT = FLOAT */
if ((type_data_1_uc == 9)
&& (type_data_2_uc == 9)
&& (type_result_uc == 9))
{
*((float *) value_result_ptr) = *((int *) value_data_1_ptr) + *((int *) value_data_2_ptr);
}
/* UNSIGNED CHAR + INT = INT */
if ((type_data_1_uc == 2)
&& (type_data_2_uc == 3)
&& (type_result_uc == 3))
{
*((int *) value_result_ptr) = *((unsigned char *) value_data_1_ptr) + *((int *) value_data_2_ptr);
}
/* ......... */
}
int main(int argc, char **argv)
{
int data_1;
int data_2;
int result;
data_1 = 26;
data_2 = 32;
addition(3, &data_1, 3, &data_2, 3, &result);
printf("result = %d\n", result);
return 0;
}
I have think to use the union structure but it doesn't solve the problem, because union also need to be statically casted :
/* UNION DATA */
union data_union
{
char tab_c[4];
unsigned char tab_uc[4];
int i;
unsigned int ui;
short int si;
unsigned short int usi;
long l;
unsigned long ul;
float f;
};
void addition(unsigned char type_data_1_uc, union data_union *value_data_1_ptr,
unsigned char type_data_2_uc, union data_union *value_data_2_ptr,
unsigned char type_result_uc, union data_union *value_result_ptr)
{
/* types :
0 : TYPE_BIT
1 : TYPE_CHAR
2 : TYPE_UNSIGNED_CHAR
3 : TYPE_INT
4 : TYPE_UNSIGNED_INT
5 : TYPE_SHORT_INT
6 : TYPE_UNSIGNED_SHORT_INT
7 : TYPE_LONG
8 : TYPE_UNSIGNED_LONG
9 : TYPE_FLOAT */
/* INT + INT = INT */
if ((type_data_1_uc == 3)
&& (type_data_2_uc == 3)
&& (type_result_uc == 3))
{
(*value_result_ptr).i = (*value_data_1_ptr).i + (*value_data_2_ptr).i;
}
/* FLOAT + FLOAT = FLOAT */
if ((type_data_1_uc == 9)
&& (type_data_2_uc == 9)
&& (type_result_uc == 9))
{
(*value_result_ptr).f = (*value_data_1_ptr).f + (*value_data_2_ptr).f;
}
/* UNSIGNED CHAR + INT = INT */
if ((type_data_1_uc == 2)
&& (type_data_2_uc == 3)
&& (type_result_uc == 3))
{
(*value_result_ptr).i = (*value_data_1_ptr).uc + (*value_data_2_ptr).i;
}
}
int main(int argc, char **argv)
{
static union data_union data_1_union;
static union data_union data_2_union;
static union data_union result_union;
memset(&data_1_union, 0, sizeof(union data_union));
memset(&data_2_union, 0, sizeof(union data_union));
data_1_union.i = 26;
data_2_union.i = 32;
addition(3, &data_1_union, 3, &data_2_union, 3, &result_union);
printf("result_union.i = %d\n", result_union.i);
return 0;
}
Any idea to solve this ?
You cannot do that without some "pain", since C is statically typed language. The compiler needs to know the types of variables in order to generate the proper instructions. Most CPU:s have distinct instructions for adding 8-bit integers, 32-bit integers, floats, and so on.
That said, you can certainly improve on the interface: I would use variable arguments to make the prototype:
typedef enum {
TYPE_BIT = 0,
TYPE_CHAR,
TYPE_UNSIGNED_CHAR,
TYPE_INT,
TYPE_UNSIGNED_INT,
TYPE_SHORT_INT,
TYPE_UNSIGNED_SHORT_INT,
TYPE_LONG,
TYPE_UNSIGNED_LONG,
TYPE_FLOAT,
} Type;
void addition(Type type, void *result, ...);
This expects to be called with four arguments, the two latter of which should have the type indicated by the type argument. The result is stored at result, which should be a pointer to the same type as the arguments.
Not sure how to represent single-bit values, probably as unsigned char but it's kind of pointless: single bits is not a type that you can do arithmetic with in C so you're just going to end up doing the add with more bits, then masking some of them off. You also can't have a pointer to a single bit in memory on most machines.
The idiomatic C approach to this problem is to use macros to eliminate code duplication as much as possible. The rest will unfortunately have to be done manually. For example:
enum typecode {
TC_INT = 3,
TC_FLOAT = 9,
/* ... */
};
void addition(enum typecode type_data_1_uc, void *value_data_1_ptr,
enum typecode type_data_2_uc, void *value_data_2_ptr,
enum typecode type_result_uc, void *value_result_ptr)
{
#define DO_ADD(tc1, tc2, tcret, type1, type2, typeret) do { \
if (type_data_1_uc == tc1 && type_data_2_uc == tc2 \
&& type_result_uc == tcret) { \
*(typeret *)value_result_ptr = \
*(type1 *)(value_data_1_ptr) + *(type2 *)(value_data_2_ptr) \
return; \
} while (0)
/* INT + INT = INT */
DO_ADD(TC_INT, TC_INT, TC_INT, int, int, int);
/* FLOAT + FLOAT = FLOAT */
DO_ADD(TC_FLOAT, TC_FLOAT, TC_FLOAT, float, float, float);
/* UCHAR + INT = INT */
DO_ADD(TC_UCHAR, TC_INT, TC_INT, unsigned char, int, int);
/* ... */
#undef DO_ADD
/* none matched: abort() or set an error code, as appropriate */
}
Related
This relates to C.
I am wondering if I can dynamically cast a pointer.
I have a struct called Value that encapsulates different data (for example, int and float arrays):
typedef struct {
uint64_t count; // array length
int8_t type; // array type
uint8_t data[]; // array
} *Value;
ints have type = 1. floats have type = 2. (More types - including negative types - exist but are omitted in this example for simplicity.)
data[] can contain different types. The values are accessed by casting data, eg ((int *)x->data)[0].
Here is the code I use to add 2 Values:
#define ADD(x, y) ((x) + (y))
#define APPLY_OP(op) \
if (1 == ret->type){ \
APPLY_OP_ACCESSORS(op, ((int *)ret->data)); \
} \
else { \
APPLY_OP_ACCESSORS(op, ((float *)ret->data)); \
}
#define APPLY_OP_ACCESSORS(op, ra)
if (1 == x->type && 1 == y->type) { APPLY_OP_ITER(op, ra, ((int* )x->data), ((int* )y->data)) } \
else if (1 == x->type && 2 == y->type) { APPLY_OP_ITER(op, ra, ((int* )x->data), ((float*)y->data)) } \
else if (2 == x->type && 1 == y->type) { APPLY_OP_ITER(op, ra, ((float*)x->data), ((int* )y->data)) } \
else if (2 == x->type && 2 == y->type) { APPLY_OP_ITER(op, ra, ((floar*)x->data), ((float*)x->data)) }
#define APPLY_OP_ITER(op, ra, xa, ya) /* op, return/x/y accessors */ \
if (x->count == y->count) for (uint64_t i = 0; i < x->count; ++i) ra[i] = op( xa[i], ya[i] ); \
else if (x->count > y->count) for (uint64_t i = 0; i < x->count; ++i) ra[i] = op( xa[i], ya[0] ); \
else { for (uint64_t i = 0; i < y->count; ++i) ra[i] = op( xa[0], ya[i] );}
Value add(Value x, Value y){
// allocate an object for the return value
Value ret = getReturnObject(x, y);
// ...
// handle if error
// ...
// calculate
APPLY_OP(ADD);
return ret;
}
The APPLY_OP macro has 2 branches, each having the same code except for the pointer type to the ret->data struct member. Is there some way i can avoid this branching/duplication, and dynamically change the ret->data pointer type instead? With more Value->types and more functions (subtract, multiply, divide, etc) the generated binary bloats up significantly.
NB this is simplified significantly from my own project. I omitted what I could. Hopefully the question is clear.
I pass in a hex number into hex2bin and it prints out the binary number correctly but I don't want it to print out the number I want to return the number so I can use it to find the cardinality of the number. How would I store the number instead of printing it out?
int hex2bin (int n){
int i,k,mask;
for(i = sizeof(int) * 8 - 1; i >= 0; i--){
mask = 1 << i;
k = n & mask;
k == 0 ? printf("0"):printf("1");
}
return 0;
}
Perhaps something like this?
int result = 0;
int i, k...
...
result = result | (((k == 0) ? 0 : 1) << i;
...
return result;
Instead of being clever with an int, you could of course also simply use an array of variables instead.
Store the number in a string whose space is provided by a compound literal (Available since C99).
It works like OP's flow: Loop up to sizeof(int) * 8 times, finding the value of 1 bit and print/save it.
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
// Maximum buffer size needed
#define UTOA_BASE_2 (sizeof(unsigned)*CHAR_BIT + 1)
char *utoa_base2(char *s, unsigned x) {
s += UTOA_BASE_2 - 1;
*s = '\0';
do {
*(--s) = "01"[x % 2];
x /= 2;
} while (x);
return s;
}
#define TO_BASE2(x) utoa_base2((char [UTOA_BASE_2]){0} , (x))
void test(unsigned x) {
printf("base10:%10u base2:%5s ", x, TO_BASE2(x));
char *s = TO_BASE2(x);
// do stuff with `s`, it is valid for until the end of this block
printf("%s\n", s);
}
int main(void) {
test(0);
test(25);
test(UINT_MAX);
}
Sample output
base10: 0 base2: 0 0
base10: 25 base2:11001 11001
base10:4294967295 base2:11111111111111111111111111111111 11111111111111111111111111111111
This is a variation of this base-n answer.
You can use the strcat function to do that.
Note that the new hex2bin function in this answer assumes that the parameter char *buf has already been allocated and can hold at least 1+sizeof(int)*8 bytes including the null terminator:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// assume: buf is at least length 33
int hex2bin (int n, char *buf)
{
int i,k,mask;
for(i = sizeof(int) * 8 - 1; i >= 0; i--){
mask = 1 << i;
k = n & mask;
k == 0 ? strcat(buf, "0") : strcat(buf, "1");
}
return 0;
}
int main()
{
int n = 66555;
char buffer[1+sizeof(int)*8] = { 0 } ;
hex2bin(n, buffer);
printf("%s\n", buffer);
return 0;
}
I hope you will find this helpful :)
bool convertDecimalBNR(INT32 nDecimalValue, UINT32 * punFieldValue, INT32 nBitCount, DecimalBNRType * pDecimalSpecification)
{
bool bBNRConverted = false;
INT32 nBitIndex = nBitCount - 1;
INT32 nBitValue = anTwoExponents[nBitIndex];
*punFieldValue = 0;
if ((nDecimalValue >= pDecimalSpecification->nMinValue) && (nDecimalValue <= pDecimalSpecification->nMaxValue))
{
// if the value is negative, then add (-1 * (2 ^ (nBitCount - 1))) on itself and go on just like a positive value calculation.
if (nDecimalValue < 0)
{
nDecimalValue += nBitValue;
nBitIndex--;
nBitValue /= 2;
*punFieldValue |= BIT_0_ONLY_ONE;
}
while (nBitIndex >= 0)
{
*punFieldValue = (*punFieldValue << 1);
if (nDecimalValue >= nBitValue)
{
nDecimalValue -= nBitValue;
*punFieldValue |= BIT_0_ONLY_ONE;
}
nBitIndex--;
nBitValue /= 2;
}
if (nDecimalValue <= nBitValue)
{
bBNRConverted = true;
}
}
return (bBNRConverted);
}
Let ib be the input base and ob the output base. str is the ASCII representation of some arbitrary large integer x. I need to define f such as:
f(str="1234567890", ib=10, ob=16) = {4, 9, 9, 6, 0, 2, 13, 2}
... where the return type of f is an int array containing the base ob digits of this integer. We assume that 2 >= ob <= MAX_INT and 2 >= ib <= 10, and str will always be a valid string (no negative needed).
Something to get OP started, but enough to leave OP to enjoy the coding experience.
// form (*d) = (*d)*a + b
static void mult_add(int *d, size_t *width, int ob, int a, int b) {
// set b as the carry
// for *width elements,
// x = (Multiply d[] by `a` (using wider than int math) and add carry)
// d[] = x mod ob
// carry = x/ob
// while (carry <> 0)
// widen d
// x = carry
// d[] = x mod ob
// carry = x/ob
}
int *ql_f(const char *src, int ib, int ob) {
// Validate input
assert(ib >= 2 && ib <= 10);
assert(ob >= 2 && ob <= INT_MAX);
assert(src);
// Allocate space
size_t length = strlen(src);
// + 2 + 4 is overkill, OP to validate and right-size later
size_t dsize = (size_t) (log(ib)/log(ob)*length + 2 + 4);
int *d = malloc(sizeof *d * dsize);
assert(d);
// Initialize d to zero
d[0] = 0;
size_t width = 1;
while (*src) {
mult_add(d, &width, ob, ib, *src - '0');
src++;
}
// add -1 to end, TBD code
return d;
}
I wrote this with older specifications, so it's not valid any more, but it might be useful as a starting point.
The code can handle long long magnitudes. Going to arbitrary precision numbers in C is a big leap!
Note using -1 as the ending marker instead of 0. Can accept ib from 2 to 36 and any ob.
Includes example main.
Function f is not reentrant as-is. To make it thread-safe, it could allocate the required memory then return a pointer to it. The simplest protocol would be having the caller responsible for freeing the memory afterwards.
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
int *f(const char *str, int ib, int ob) {
static int result[CHAR_BIT * sizeof(long long) + 1];
int i = sizeof(result) / sizeof(int) - 1;
long long l = strtoll(str, NULL, ib);
result[i--] = -1;
while (l) {
result[i] = l % ob;
l /= ob;
i--;
}
return result + i + 1;
}
int main()
{
int *x = f("1234567890", 16, 10);
while (*x > -1) {
printf("%d ", *x);
x++;
}
return 0;
}
So I wrote a simple C program which displays the nth fibonacci number based on user specification. I started using ints and obviously that did not work out too well so I then went to floats, longs, and finally long longs. Even with long longs there is a cut off to where I get nonsensical or incorrect output such as negative numbers ( starts to happen a bit before n = 100 ). Is there anything I can do to expand the range of this program or is there no way to fix this?
#include <stdio.h>
#include <stdlib.h>
int main()
{
long long term = 0;
long long val = 0;
long long first = 0;
long long second = 1;
printf( "Which fibonacci term (starting at 0) would you like to see? ");
scanf("%lld", &term );
if( term == 0 )
{
printf("%lld\n", first );
return 1;
}
if( term == 1 )
{
printf( "%lld\n", second );
return 1;
}
if( term > 1 )
{
for( int i = 1; term > i; i++ )
{
val = first + second;
first = second;
second = val;
}
printf( "%lld\n", val );
return 1;
}
return 0;
}
As per ISO C99, a long long is 64bit at the minimum - the standard integer data type with the largest size. Your compiler may allow for larger types, and these are defined by intmax_t and uintmax_t in <stdint.h>.
However, I would strongly recommend using a Bigint library such as GMP.
Using GMP, the only limitation for long integers and floating points is the resources available on the system.
Change all of your types to unsigned long long which will perform unsigned arithmetic, nearly doubling the range. If you wish to extend this further you will need to create your own data type. A few examples of libraries that can do this for you:
GMP
OpenSSL BN
Edit per Michael Burr's comment:
Requires your platform to have unsigned long longs that are greater than 64 bits, such as 128 bits (or more). C99 only guarantees that long long is at least 64 bits. The range of your particular program won't change if long long is only 64 bits.
One can use uintmax_t but that is likely <= 128 bits.
GMP is an excellent library to consider.
But one only needs to write an extend precision add() as below. Not very efficient, but it gets the job done for fib(). Here I use a C string as the data type. Other types of your design could work far more better.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define max(a, b) ((a) > (b) ? (a) : (b))
// "Add" 2 strings
char *strsum(const char *a, const char *b) {
size_t alen = strlen(a);
size_t blen = strlen(b);
size_t clen = max(alen, blen) + 1;
size_t csize = clen + 1;
char *c = malloc(csize);
if (c == NULL) return NULL;
c[clen] = '\0';
int carry = 0;
while (alen > 0 && blen > 0) {
int sum = a[--alen] - '0' + b[--blen] - '0' + carry;
c[--clen] = sum%10 + '0';
carry = sum / 10;
}
while (alen > 0) {
int sum = a[--alen] - '0' + carry;
c[--clen] = sum%10 + '0';
carry = sum / 10;
}
while (blen > 0) {
int sum = b[--blen] - '0' + carry;
c[--clen] = sum%10 + '0';
carry = sum / 10;
}
if (carry) {
c[--clen] = carry + '0';
}
if (clen > 0) {
memmove(&c[0], &c[1], csize - 1);
}
return c;
}
void fib(unsigned n) {
char *a = NULL;
char *b = malloc(2); strcpy(b, "0");
char *c = malloc(2); strcpy(c, "1");
unsigned i;
for (i=1; i<n; i++) {
free(a);
a = b;
b = c;
c = strsum(a, b);
if (c == NULL) break;
printf("fib(%u) = %s\n", i+1, c);
}
free(a);
free(b);
free(c);
}
int main(int argc, char *argv[]) {
fib(1000);
return 0;
}
Sample
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13
fib(8) = 21
fib(9) = 34
fib(10) = 55
...
fib(93) = 12200160415121876738 /* max using 64-bit math */
...
fib(100) = 354224848179261915075
...
fib(1000) = 43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875
...
fib(10000) = 336...(2084 digits)...875
I want to print a number into log or to a terminal using write (or any async-safe function) inside a signal handler. I would prefer not to use buffered I/O.
Is there an easy and recommended way to do that ?
For example in place of printf, below I would prefer write (or any asyn safe function).
void signal_handler(int sig)
{
pid_t pid;
int stat;
int old_errno = errno;
while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
printf("child %d terminated\n", pid);
errno = old_errno;
return;
}
Printing strings is easy. In place of the printf above I can use (without printing pid):
write(STDOUT_FILENO, "child terminated", 16);
If you really insist on doing the printing from a signal handler, you basically have 2 options:
Block the signal except in a dedicated thread you create for handling the signal. This special thread can simply perform for (;;) pause(); and since pause is async-signal-safe, the signal handler is allowed to use any functions it wants; it's not restricted to only async-signal-safe functions. On the other hand, it does have to access shared resources in a thread-safe way, since you're now dealing with threads.
Write your own code for converting integers to decimal strings. It's just a simple loop of using %10 and /10 to peel off the last digit and storing them to a short array.
However, I would highly recommend getting this operation out of the signal handler, using the self-pipe trick or similar.
Implement your own async-signal-safe snprintf("%d and use write
It is not as bad as I thought, How to convert an int to string in C? has several implementations.
The POSIX program below counts to stdout the number of times it received SIGINT so far, which you can trigger with Ctrl + C.
You can exit the program with Ctrl + \ (SIGQUIT).
main.c:
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
/* Calculate the minimal buffer size for a given type.
*
* Here we overestimate and reserve 8 chars per byte.
*
* With this size we could even print a binary string.
*
* - +1 for NULL terminator
* - +1 for '-' sign
*
* A tight limit for base 10 can be found at:
* https://stackoverflow.com/questions/8257714/how-to-convert-an-int-to-string-in-c/32871108#32871108
*
* TODO: get tight limits for all bases, possibly by looking into
* glibc's atoi: https://stackoverflow.com/questions/190229/where-is-the-itoa-function-in-linux/52127877#52127877
*/
#define ITOA_SAFE_STRLEN(type) sizeof(type) * CHAR_BIT + 2
/* async-signal-safe implementation of integer to string conversion.
*
* Null terminates the output string.
*
* The input buffer size must be large enough to contain the output,
* the caller must calculate it properly.
*
* #param[out] value Input integer value to convert.
* #param[out] result Buffer to output to.
* #param[in] base Base to convert to.
* #return Pointer to the end of the written string.
*/
char *itoa_safe(intmax_t value, char *result, int base) {
intmax_t tmp_value;
char *ptr, *ptr2, tmp_char;
if (base < 2 || base > 36) {
return NULL;
}
ptr = result;
do {
tmp_value = value;
value /= base;
*ptr++ = "ZYXWVUTSRQPONMLKJIHGFEDCBA9876543210123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[35 + (tmp_value - value * base)];
} while (value);
if (tmp_value < 0)
*ptr++ = '-';
ptr2 = result;
result = ptr;
*ptr-- = '\0';
while (ptr2 < ptr) {
tmp_char = *ptr;
*ptr--= *ptr2;
*ptr2++ = tmp_char;
}
return result;
}
volatile sig_atomic_t global = 0;
void signal_handler(int sig) {
char buf[ITOA_SAFE_STRLEN(sig_atomic_t)];
enum { base = 10 };
char *end;
end = itoa_safe(global, buf, base);
*end = '\n';
write(STDOUT_FILENO, buf, end - buf + 1);
global += 1;
signal(sig, signal_handler);
}
int main(int argc, char **argv) {
/* Unit test itoa_safe. */
{
typedef struct {
intmax_t n;
int base;
char out[1024];
} InOut;
char result[1024];
size_t i;
InOut io;
InOut ios[] = {
/* Base 10. */
{0, 10, "0"},
{1, 10, "1"},
{9, 10, "9"},
{10, 10, "10"},
{100, 10, "100"},
{-1, 10, "-1"},
{-9, 10, "-9"},
{-10, 10, "-10"},
{-100, 10, "-100"},
/* Base 2. */
{0, 2, "0"},
{1, 2, "1"},
{10, 2, "1010"},
{100, 2, "1100100"},
{-1, 2, "-1"},
{-100, 2, "-1100100"},
/* Base 35. */
{0, 35, "0"},
{1, 35, "1"},
{34, 35, "Y"},
{35, 35, "10"},
{100, 35, "2U"},
{-1, 35, "-1"},
{-34, 35, "-Y"},
{-35, 35, "-10"},
{-100, 35, "-2U"},
};
for (i = 0; i < sizeof(ios)/sizeof(ios[0]); ++i) {
io = ios[i];
itoa_safe(io.n, result, io.base);
if (strcmp(result, io.out)) {
printf("%ju %d %s\n", io.n, io.base, io.out);
assert(0);
}
}
}
/* Handle the signals. */
if (argc > 1 && !strcmp(argv[1], "1")) {
signal(SIGINT, signal_handler);
while(1);
}
return EXIT_SUCCESS;
}
Compile and run:
gcc -std=c99 -Wall -Wextra -o main main.c
./main 1
After pressing Ctrl + C fifteen times, the terminal shows:
^C0
^C1
^C2
^C3
^C4
^C5
^C6
^C7
^C8
^C9
^C10
^C11
^C12
^C13
^C14
Here is a related program that creates a more complex format string: How to avoid using printf in a signal handler?
Tested on Ubuntu 18.04. GitHub upstream.
You can use string handling functions (e.g. strcat) to build the string and then write it in one go to the desired file descriptor (e.g. STDERR_FILENO for standard error).
To convert integers (up to 64-bit wide, signed or unsigned) to strings I use the following functions (C99), which support minimal formatting flags and common number bases (8, 10 and 16).
#include <stdbool.h>
#include <inttypes.h>
#define STRIMAX_LEN 21 // = ceil(log10(INTMAX_MAX)) + 2
#define STRUMAX_LEN 25 // = ceil(log8(UINTMAX_MAX)) + 3
static int strimax(intmax_t x,
char buf[static STRIMAX_LEN],
const char mode[restrict static 1]) {
/* safe absolute value */
uintmax_t ux = (x == INTMAX_MIN) ? (uintmax_t)INTMAX_MAX + 1
: (uintmax_t)imaxabs(x);
/* parse mode */
bool zero_pad = false;
bool space_sign = false;
bool force_sign = false;
for(const char *c = mode; '\0' != *c; ++c)
switch(*c) {
case '0': zero_pad = true; break;
case '+': force_sign = true; break;
case ' ': space_sign = true; break;
case 'd': break; // decimal (always)
}
int n = 0;
char sign = (x < 0) ? '-' : (force_sign ? '+' : ' ');
buf[STRIMAX_LEN - ++n] = '\0'; // NUL-terminate
do { buf[STRIMAX_LEN - ++n] = '0' + ux % 10; } while(ux /= 10);
if(zero_pad) while(n < STRIMAX_LEN - 1) buf[STRIMAX_LEN - ++n] = '0';
if(x < 0 || force_sign || space_sign) buf[STRIMAX_LEN - ++n] = sign;
return STRIMAX_LEN - n;
}
static int strumax(uintmax_t ux,
char buf[static STRUMAX_LEN],
const char mode[restrict static 1]) {
static const char lbytes[] = "0123456789abcdefx";
static const char ubytes[] = "0123456789ABCDEFX";
/* parse mode */
int base = 10; // default is decimal
int izero = 4;
bool zero_pad = false;
bool alternate = false;
const char *bytes = lbytes;
for(const char *c = mode; '\0' != *c; ++c)
switch(*c) {
case '#': alternate = true; if(base == 8) izero = 1; break;
case '0': zero_pad = true; break;
case 'd': base = 10; izero = 4; break;
case 'o': base = 8; izero = (alternate ? 1 : 2); break;
case 'x': base = 16; izero = 8; break;
case 'X': base = 16; izero = 8; bytes = ubytes; break;
}
int n = 0;
buf[STRUMAX_LEN - ++n] = '\0'; // NUL-terminate
do { buf[STRUMAX_LEN - ++n] = bytes[ux % base]; } while(ux /= base);
if(zero_pad) while(n < STRUMAX_LEN - izero) buf[STRUMAX_LEN - ++n] = '0';
if(alternate && base == 16) {
buf[STRUMAX_LEN - ++n] = bytes[base];
buf[STRUMAX_LEN - ++n] = '0';
} else if(alternate && base == 8 && '0' != buf[STRUMAX_LEN - n])
buf[STRUMAX_LEN - ++n] = '0';
return STRUMAX_LEN - n;
}
They can be used like this:
#include <unistd.h>
int main (void) {
char buf[STRIMAX_LEN]; int buf_off;
buf_off = strimax(12345,buf,"+");
write(STDERR_FILENO,buf + buf_off,STRIMAX_LEN - buf_off);
}
that outputs:
+12345
If you insist on using xprintf() inside a signal handler you can always roll your own version that does not rely on buffered I/O:
#include <stdarg.h> /* vsnprintf() */
void myprintf(FILE *fp, char *fmt, ...)
{
char buff[512];
int rc,fd;
va_list argh;
va_start (argh, fmt);
rc = vsnprintf(buff, sizeof buff, fmt, argh);
if (rc < 0 || rc >= sizeof buff) {
rc = sprintf(buff, "Argh!: %d:\n", rc);
}
if (!fp) fp = stderr;
fd = fileno(fp);
if (fd < 0) return;
if (rc > 0) write(fd, buff, rc);
return;
}