Problems printing a bitmap font - c

Lately I'm trying to print a bitmap font in C, using only a set_pixel function (that only sets the color of a pixel in a determinate coordinate of the screen).
The problem is that when I try my code, this code does not work at all.
I will write the code down, do you know why this is failing?
The only reason I could find is a warning that my compiler (clang) reports:
x86_64-uefi/tty.c:67:11: warning: incompatible pointer to integer conversion passing 'char [2]' to parameter of type 'char' [-Wint-conversion]
put_char("c", 1, 1, r | g | b);
That I don't know how to fix.
Files involved:
1: tty.c (The one that prints)
#include "font.h"
#include "tty.h"
KABI void put_char(char c, uint8_t x, uint8_t y, uint32_t rgb)
{
uint8_t i,j;
// Convert the character to an index
c = c & 0x7F;
if (c < ' ') {
c = 0;
} else {
c -= ' ';
}
// 'font' is a multidimensional array of [96][char_width]
// which is really just a 1D array of size 96*char_width.
const uint8_t* chr = font[c*CHAR_WIDTH];
// Draw pixels
for (j=0; j<CHAR_WIDTH; j++) {
for (i=0; i<CHAR_HEIGHT; i++) {
if (chr[j] & (1<<i)) {
set_pixel(x+j, y+i, rgb);
}
}
}
}
2: tty.h (the header of tty.c)
#pragma once
// Types (uint8_t, etc.)
#include "typedefs.h"
// tty_init: Cleans the screen and setup all.
KABI void tty_init(void);
KABI void put_char(char c, uint8_t x, uint8_t y, uint32_t rgb);
3: font.h (I made it a little bit shorter)
// Our types (uint8_t, etc.)
#include "typedefs.h"
#define CHAR_WIDTH 6
#define CHAR_HEIGHT 8
const unsigned char font[96][6] = {
{0x00,0x00,0x00,0x00,0x00,0x00}, //
{0x2e,0x00,0x00,0x00,0x00,0x00}, // !
{0x03,0x00,0x03,0x00,0x00,0x00}, // "
{0x0a,0x1f,0x0a,0x1f,0x0a,0x00}, // #
{0x2e,0x2a,0x6b,0x2a,0x3a,0x00}, // $
{0x0e,0x2a,0x1e,0x08,0x3c,0x2a}, // %
{0x3e,0x2a,0x2a,0x22,0x38,0x08}, // &
{0x03,0x00,0x00,0x00,0x00,0x00}, // '
...
{0x3e,0x24,0x24,0x24,0x3c,0x00}, // b
{0x3c,0x24,0x24,0x24,0x24,0x00}, // c
...
{0x00,0x00,0x00,0x00,0x00,0x00}
};
Calling the tty_init function output the following:
Apparently nothing, but if you take a closer look (the font is pretty small), it prints just a line of 6 pixels,
Thanks in advance!
I don't know if it has something to do, but...
Notes: KABI stands for __attribute__((sysv_abi))
and the pixel line is printed at the top of the "represented" string.

Related

Update char array with Int and float values, without sprintf?

I am using C to program a microcontroller (NHS3152) to calculate resistance using this formula:
RES = (V1-V2)/I
I want to write a program that:
Updates the values of 3 Floats and 1 integer (V1, V2, I, RES)
Update uint8_t text[] = "Char to store values of V_1, V_2, I, Res"; with a string with the values of these 3 floats and 1 integer
Commits text to memory using the function: "Example1_Creating_an_NDEF..." (provided below)
The issue I am having is with Updating text with the values of the 3 floats and 1 integer. I am able to do it with sprintf, however I cannot use this function with my microcontroller (I dont know why it stops me from flashing it to memory)
The code bellow is an example of what i want to achieve in from point 2 above, in the real code the float and integer values are updated by getting values from sensors on the mircocontroller:
#include <stdio.h>
#include <stdint.h>
static volatile float V_1 = 5.0; // store value of Voltage 1
static volatile float V_2 = 2.0; // store value of voltage 2
static volatile int I = 5; // store current value
static volatile float RES; // calcualte resitance
int i=1;
uint8_t text[] = "Char to store values of V_1, V_2, I, Res";
int main(void)
{
printf( text);
printf("\n");
//updates values for ADC_1, ADC_2, I, Res,
while(i<=2){ // real while loop is infinite
V_1 ++ ; // Usually value updates from microcontroller sensor
V_2 ++ ;
I ++;
RES = (V_1-V_2)/I; // calculating resistance
sprintf((char *)text, "Updated Text V1: %6.2f V2: %6.2f I: %8.d resistance: %e", V_1, V_2,I, RES ); // what i want to do but without sprintf
printf( text);
printf("\n");
i++;
}
printf("END");
}
OUT:
Char to store values of V_1, V_2, I, Res
Updated Text V1: 6.00 V2: 3.00 I: 6 resistance: 5.000000e-01
Updated Text V1: 7.00 V2: 4.00 I: 7 resistance: 4.285714e-01
END
Here is the Code including the function Example1_Creating_an_NDEF.. from point 3. This working code manages to Commit the text to memory (it works). All i need is to be able to update the text without sprintf as i believe these functions aren't allowed when i'm not in debug mode.
#include "board.h"
// From ndeft2t
#include "ndeft2t/ndeft2t.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
uint8_t instance[NDEFT2T_INSTANCE_SIZE] __attribute__((aligned (4)));
uint8_t buffer[NFC_SHARED_MEM_BYTE_SIZE ] __attribute__((aligned (4)));
uint8_t locale[] = "en";
uint8_t text[] = "Char to store values of V_1, V_2, I, Res";
// I want to add these to the text message
static volatile float V_1 = 5.0;
static volatile float V_2 = 2.0;
static volatile int I = 5;
static volatile float Res = ;
static void Example1_Creating_an_NDEF_Message_with_a_single_record_of_type_TEXT (void)
{
Chip_NFC_Init(NSS_NFC); /* Is normally already called during board initialization. */
NDEFT2T_Init();
NDEFT2T_CreateMessage(instance, buffer, NFC_SHARED_MEM_BYTE_SIZE, true);
NDEFT2T_CREATE_RECORD_INFO_T recordInfo = {.shortRecord = true, .pString = locale};
if (NDEFT2T_CreateTextRecord(instance, &recordInfo)) {
/* The payload length to pass excludes the NUL terminator. */
if (NDEFT2T_WriteRecordPayload(instance, text, sizeof(text) - 1)) {
NDEFT2T_CommitRecord(instance);
}
}
NDEFT2T_CommitMessage(instance);
/* The return value of the commit function is ignored here. */
}
int main(void)
{
Board_Init();
//NDEFT2T_Init();
/* Optional feature: send the ARM clock to PIO0_1 */
Chip_IOCON_SetPinConfig(NSS_IOCON, IOCON_PIO0_1, IOCON_FUNC_1);
Chip_Clock_Clkout_SetClockSource(CLOCK_CLKOUTSOURCE_SYSTEM);
/* Blink & attach a message to Memory */
while (1) {
//updates values for V_1, V_2, I, Res,
V_1++;
V_2++;
I++;
RES = (V_1 - V_2)/RES;
// Update the text message to contain "(V_1, V_2, I, RES)"
LED_Toggle(LED_RED);
Example1_Creating_an_NDEF_Message_with_a_single_record_of_type_TEXT();
Chip_Clock_System_BusyWait_ms(500);
}
return 0;
}
EDIT: responding to KamilCuk's comment : Do you really need float? Does your mcu has floating point support? Consider using integers only.
The fucntions i use to get V1,V2 & I, all return an Integer (eg: V1=2931 12 bit converter so between 0 and 4096 ) however to convert the integer value to the real value i need to use the following conversion:
V1_real= (V1* 1.2) / 2825 + 0.09; // CONVERSION added line
Without conversion i Cannot calculate Res.
An acceptable compromise is to commit to memory the values V1, V2, I without converstion to real values. I can then calculate RES in a second momemnt, once i retrieve the data from the MCU ( i am using my phone as an NFC reader, the chip is an NFC tag)
So the question is:
How do i convert the message in text to a message containing the integers (V_1,V_2,I)? :
Something like:
uint8_t text[] = "Char to store values of V_1, V_2, I";
text = "Message with V_1: V_1value, V_2: V_2value, I: IValue
Below is the code i use to extract the value of V_1 & V_2:
void adc (void)
{
Chip_IOCON_SetPinConfig(NSS_IOCON, IOCON_ANA0_1, IOCON_FUNC_1);
Chip_ADCDAC_SetMuxADC(NSS_ADCDAC0, ADCDAC_IO_ANA0_1);
Chip_ADCDAC_SetInputRangeADC(NSS_ADCDAC0, ADCDAC_INPUTRANGE_WIDE);
Chip_ADCDAC_SetModeADC(NSS_ADCDAC0, ADCDAC_SINGLE_SHOT);
Chip_ADCDAC_StartADC(NSS_ADCDAC0);
//Getting the data
while (!(Chip_ADCDAC_ReadStatus(NSS_ADCDAC0) & ADCDAC_STATUS_ADC_DONE)) {
; /* Wait until measurement completes. For single-shot mode only! */
}
//Data V1 stored here
adcInput_1 = Chip_ADCDAC_GetValueADC(NSS_ADCDAC0);
// Usually i use this line to then conver it to the real value of V1
//adcInput_1= (adcInput_1 * 1.2) / 2825 + 0.09; // CONVERSION added line
//! [adcdac_nss_example_3]
}
Maybe someone has a better solution that allows me to actually arrive at having also RES calculated, knowing that:
RES= V1_real-V1_real/I_real
V1_real= (V1* 1.2) / 2825 + 0.09 and 0< V1 < 4000 (12 bit converter for V1)
V2_real= (V2* 1.2) / 2825 + 0.09 and 0< V2 < 4000
I_real = I*1e-12 0<I<4000

Can you choose #define based on parameter?

To simplify a commonly occurring bitmap lookup, I decided to write a macro for it. This macro would take in parameters array, position, and number of bits per entry. The latter is always 1, 2, or 3, to be determined at compile time.
The macros for one and two bits per entry are quite simple:
#define 1BITLOOKUP(array, index) (array[index / CHAR_BIT] >> (index % CHAR_BIT)) & 1
#define 2BITLOOKUP(array, index) (array[2 * index / CHAR_BIT] >> (2 * index % CHAR_BIT)) & (0x11)
However, three-bit bitmap will be more complex, since it will cross byte boundaries. For the sake of efficiency, I do not wish to include that code when I am only using one- and two- bit lookups. So I would like to use a macro of the sort
#define BITMAPLOOKUP(array, index, k) kBITLOOKUP(array, index)
However, this does not work. Is there a workaround?
NOTE: This has been updated to a non-minimal working example, as per request of multiple commenters.
You can paste the macro argument into other words without separator, using the ## construct. So here this will work:
#define FUNCTION(k) FUNCTION##k
Try it online!
#MadPhysicist suggests something like:
#include <stdio.h>
static void func0(void) { printf("0\n"); }
static void func1(void) { printf("1\n"); }
static void func2(void) { printf("2\n"); }
static void func3(void) { printf("3\n"); }
static void (*func[])(void) = {func0, func1, func2, func3};
int main(void)
{
func[1]();
return 0;
}
This has an advantage: you are not limited to compile time literals:
for (int i = 0; i < 3; i++) {
FUNCTION(i); /* invalid */
func[i](); /* valid */
}

I am running gbdk a gameboy game making program and I am getting parse errors on the following code

#include <gb/gb.h>
#include <stdio.h>
#include "racecars.h"
/*
RACECARS.C
Tile Source File.
Info:
Form : All tiles as one unit.
Format : Gameboy 4 color.
Compression : None.
Counter : None.
Tile size : 8 x 8
Tiles : 0 to 16
Palette colors : None.
SGB Palette : None.
CGB Palette : None.
Convert to metatiles : No.
This file was generated by GBTD v2.2
*/
/* Start of tile array. */
unsigned char Racecars[] =
{
0x7E,0x7E,0x42,0x5E,0xC3,0xD7,0xC3,0xDF,
0x42,0x52,0x7E,0x7E,0xC3,0xC3,0xC3,0xC3,
0x1C,0x1C,0x36,0x3E,0x62,0x7A,0x43,0x4B,
0x41,0x5D,0x7F,0x7F,0x55,0x55,0x77,0x77,
0x38,0x38,0x44,0xC6,0xAA,0xAA,0xAA,0xAA,
0xAA,0xAA,0x82,0x82,0x44,0xC6,0x38,0x38,
0x18,0x18,0x18,0x18,0x24,0x24,0x42,0x42,
0xFF,0xFF,0x00,0xC6,0x00,0xC6,0x00,0x00,
0x3C,0x3C,0x24,0xE7,0x24,0xE7,0x24,0x24,
0x24,0xE7,0x3C,0xFF,0x24,0x24,0x3C,0x3C,
0x18,0x18,0x24,0xE7,0x42,0xC3,0x42,0x42,
0x42,0x42,0x24,0x66,0x18,0x7E,0x00,0x00,
0xF2,0xF2,0x96,0x96,0xF2,0xF2,0x82,0x82,
0x8F,0x8F,0x00,0x00,0x00,0x00,0x00,0x00,
0xEE,0xEE,0x82,0x82,0x8E,0x8E,0x8B,0x8B,
0xEE,0xEE,0x00,0x00,0x00,0x00,0x00,0x00,
0xEE,0xEE,0x82,0x82,0x8E,0x8E,0x82,0x82,
0xEE,0xEE,0x00,0x00,0x00,0x00,0x00,0x00,
0xE2,0xE2,0x86,0x86,0x82,0x82,0x82,0x82,
0xE7,0xE7,0x00,0x00,0x00,0x00,0x00,0x00,
0xEA,0xEA,0x8A,0x8A,0x8E,0x8E,0x82,0x82,
0xE2,0xE2,0x00,0x00,0x00,0x00,0x00,0x00,
0xE7,0xE7,0x84,0x84,0x87,0x87,0x81,0x81,
0xE7,0xE7,0x00,0x00,0x00,0x00,0x00,0x00,
0xF7,0xF7,0x85,0x85,0x85,0x85,0xB5,0xB5,
0x95,0x95,0xF7,0xF7,0x00,0x00,0x00,0x00,
0x82,0x82,0x82,0x82,0x92,0x92,0x92,0x92,
0x92,0x92,0x92,0x92,0xFE,0xFE,0x00,0x00,
0xFF,0xFF,0x10,0x10,0x10,0x10,0x10,0x10,
0x10,0x10,0xFF,0xFF,0x00,0x00,0x00,0x00,
0x82,0x82,0xC2,0xC2,0xA2,0xA2,0x92,0x92,
0x8A,0x8A,0x86,0x86,0x82,0x82,0x00,0x00,
0x20,0x20,0x30,0x30,0x38,0x38,0x3C,0x3C,
0x20,0x20,0x20,0x20,0x20,0x20,0xFF,0xFF
};
/* End of RACECARS.C */
int main(void){ //starts the main function
int x,y,st,key,z; //define the integers x and y wich will be used to define the hero position, key will be used later
int x1,y1,st1;
int x2,y2,st2;
int x3,y3,st3;
x=20; //set x as 20
y=20; //set y as 20
st=0; //sprite type(1=closed 0=open)
z=0; //set z=0, z will be used for testing if its time to change sprite or not
set_sprite_data(0,76,Racecars); //set our sprite data
wait_vbl_done(); //wait for the visual blank to be done
set_sprite_tile(0,0); //set our tile 0 as the tile 0 of our tile data-pacman open
set_sprite_tile(1,1); //set our tile 1 as the 1st tile of our tile data-pacman closed
set_sprite_tile(2,2);
set_sprite_tile(3,3);
set_sprite_tile(4,4);
set_sprite_tile(5,5);
set_sprite_tile(6,6);
set_sprite_tile(7,7);
set_sprite_tile(8,8);
set_sprite_tile(9,9);
set_sprite_tile(10,10);
set_sprite_tile(11,11);
set_sprite_tile(12,12);
set_sprite_tile(13,13);
set_sprite_tile(14,14);
set_sprite_tile(15,15);
set_sprite_tile(16,16);
move_sprite(st,x,y);
#define MapSizeX 256;
#define MapSizeY 256;
UBYTE ScrollXCnt;
UBYTE ScrollYCnt;
BYTE SCXCnt;
BYTE SCYCnt;
UBYTE tempa, tempb;
UWORD Cnt;
Cnt = 0;
wait_vbl_done();
for(tempa = 0; tempa != 32; tempa++) {
set_bkg_tiles(0,tempa,32,tempa+1,TileMap[Cnt]);
Cnt = Cnt + MapSizeX;
}
if(joypad() & J_UP) ScrollUp();
if(joypad() & J_DOWN) ScrollDown();
if(joypad() & J_LEFT) ScrollLeft();
if(joypad() & J_RIGHT) ScrollRight();
wait_vbl_done();
scroll_bkg(x,y);
ScrollX++; or ScrollX--;
ScrollY++; or ScrollY--;
SCXCnt++; or SCXCnt++;
SCYCnt++; or SCYCnt--;
if(SCXCnt == 8) {
SCXCnt = 0;
Cnt = ScrollX / 8;
Cnt = Cnt + 20;
for(tempa = 0; tempa != 224; tempa++) {
set_bkg_tiles(0,tempa,1,tempa+1,TileMap[Cnt]);
Cnt = Cnt + MapSizeX;
}
}
if(SCXCnt == -8) {
SCXCnt = 0;
SCXCnt = 0;
Cnt = ScrollX / 8;
Cnt--;
for(tempa = 0; tempa != 224; tempa++) {
set_bkg_tiles(0,tempa,1,tempa+1,TileMap[Cnt]);
Cnt = Cnt + MapSizeX;
}
}
.
.
.
That is the c code
I ran the prompt and the following errors popped up.
Parse error before ubyte line 108
Parse error before scroll x
Parse error before scroll y
Parse error before scxCnt
Parse error before ScyCnt
Parse error before 32
Your issue is that you are tying to define new variables in the middle of the function. The GBDK compiler does not allow that, for whatever reason. (This is apparently part of the ANSI C / C89 standard)
You can fix this by changing the top of your main() from
int main(void){ //starts the main function
int x,y,st,key,z; //define the integers x and y wich will be used to define the hero position, key will be used later
int x1,y1,st1;
int x2,y2,st2;
int x3,y3,st3;
to this:
int main(void){ //starts the main function
int x,y,st,key,z; //define the integers x and y wich will be used to define the hero position, key will be used later
int x1,y1,st1;
int x2,y2,st2;
int x3,y3,st3;
//These variables are used later on.
UBYTE ScrollXCnt;
UBYTE ScrollYCnt;
BYTE SCXCnt;
BYTE SCYCnt;
UBYTE tempa, tempb;
UWORD Cnt;
Alternatively, you could move all of the code starting at those variables to another function, and then those variables will be declared with the function and all will be fine.
You might be missing an include file which defines UBYTE.
That UBYTE error shouldn't occur with current sdcc 4.1.0 and therefore gbdk-2020 4.0.3
GBDK's last update was 2001 or so...
Though the other errors are coming from your code being invalid.
That's no valid C code:
ScrollX++; or ScrollX--;
ScrollY++; or ScrollY--;
SCXCnt++; or SCXCnt++;
SCYCnt++; or SCYCnt--;
And I don't think this is doing what you want:
#define MapSizeX 256;
#define MapSizeY 256;
Defines are always unscoped since they are handled by the preprocessor and not the compiler. And you rather want #define MapSizeX 256U
What you wrote will work with your code.
Cnt = Cnt + MapSizeX; turning into Cnt = Cnt + 256;; isn't a big deal, but it would likely break later.

Subtracting two images using NEON

I'm trying to subtract two images(grayscaled) by using Neon intrinsics as an exercise, I don't know what is the best way to subtract two vectors using the C intrinsics.
void subtractTwoImagesNeonOnePass( uint8_t *src, uint8_t*dest, uint8_t*result, int srcWidth)
{
for (int i = 0; i<srcWidth; i++)
{
// load 8 pixels
uint8x8x3_t srcPixels = vld3_u8 (src);
uint8x8x3_t dstPixels = vld3_u8 (src);
// subtract them
uint8x8x3_t subPixels = vsub_u8(srcPixels, dstPixels);
// store the result
vst1_u8 (result, subPixels);
// move 8 pixels
src+=8;
dest+=8;
result+=8;
}
}
It looks like you're using the wrong kind of loads and stores. Did you copy this from a three channel example? I think this is what you need:
#include <stdint.h>
#include <arm_neon.h>
void subtractTwoImagesNeon( uint8_t *src, uint8_t*dst, uint8_t*result, int srcWidth, int srcHeight)
{
for (int i = 0; i<(srcWidth/8); i++)
{
// load 8 pixels
uint8x8_t srcPixels = vld1_u8(src);
uint8x8_t dstPixels = vld1_u8(dst);
// subtract them
uint8x8_t subPixels = vsub_u8(srcPixels, dstPixels);
// store the result
vst1_u8 (result, subPixels);
// move 8 pixels
src+=8;
dst+=8;
result+=8;
}
}
You should also check that srcWidth is a multiple of 8. Also, you'd need to include all the lines of the image, as it appears that your code only handles the first line (maybe you know this and just cut down the example for simplicity).

c macro for setting bits

I have a program that compares variables from two structs and sets a bit accordingly for a bitmap variable. I have to compare each variables of the struct. No. of variables in reality are more for each struct but for simplicity I took 3. I wanted to know if i can create a macro for comparing the variables and setting the bit in the bitmap accordingly.
#include<stdio.h>
struct num
{
int a;
int b;
int c;
};
struct num1
{
int d;
int e;
int f;
};
enum type
{
val1 = 0,
val2 = 1,
val3 = 2,
};
int main()
{
struct num obj1;
struct num1 obj2;
int bitmap = 0;
if( obj1.a != obj2.d)
{
bitmap = bitmap | val1;
}
if (obj1.b != obj2.e)
bitmap = bitmap | val2;
printf("bitmap - %d",bitmap);
return 1;
}
can i declare a macro like...
#define CHECK(cond)
if (!(cond))
printf(" failed check at %x: %s",__LINE__, #cond);
//set the bit accordingly
#undef CHECK
With a modicum of care, you can do it fairly easily. You just need to identify what you're comparing and setting carefully, and pass them as macro parameters. Example usage:
CHECK(obj1.a, obj2.d, bitmap, val1);
CHECK(obj1.b, obj2.e, bitmap, val2);
This assumes that CHECK is defined something like:
#define STRINGIFY(expr) #expr
#define CHECK(v1, v2, bitmap, bit) do \
{ if ((v1) != (v2)) \
{ printf("failed check at %d: %s\n", __LINE__, STRINGIFY(v1 != v2)); \
(bitmap) |= (1 << (bit)); \
} \
} while (0)
You can lay the macro out however you like, of course; I'm not entirely happy with that, but it isn't too awful.
Demo Code
Compilation and test run:
$ gcc -Wall -Wextra -g -O3 -std=c99 xx.c -o xx && ./xx
failed check at 40: obj1.a != obj2.d
failed check at 42: obj1.c != obj2.f
bitmap - 5
$
Actual code:
#include <stdio.h>
struct num
{
int a;
int b;
int c;
};
struct num1
{
int d;
int e;
int f;
};
enum type
{
val1 = 0,
val2 = 1,
val3 = 2,
};
#define STRINGIFY(expr) #expr
#define CHECK(v1, v2, bitmap, bit) do \
{ if ((v1) != (v2)) \
{ printf("failed check at %d: %s\n", __LINE__, STRINGIFY(v1 != v2)); \
(bitmap) |= (1 << (bit)); \
} \
} while (0)
int main(void)
{
struct num obj1 = { 1, 2, 3 };
struct num1 obj2 = { 2, 2, 4 };
int bitmap = 0;
CHECK(obj1.a, obj2.d, bitmap, val1);
CHECK(obj1.b, obj2.e, bitmap, val2);
CHECK(obj1.c, obj2.f, bitmap, val3);
printf("bitmap - %X\n", bitmap);
return 0;
}
Clearly, this code relies on you matching the right elements and bit numbers in the invocations of the CHECK macro.
It's possible to devise more complex schemes using offsetof() etc and initialized arrays describing the data structures, etc, but you'd end up with a more complex system and little benefit. In particular, the invocations can't reduce the parameter count much. You could assume 'bitmap' is the variable. You need to identify the two objects, so you'll specify 'obj1' and 'obj2'. Somewhere along the line, you need to identify which fields are being compared and the bit to set. That could be some single value (maybe the bit number), but you've still got 3 arguments (CHECK(obj1, obj2, valN) and the assumption about bitmap) or 4 arguments (CHECK(obj1, obj2, bitmap, valN) without the assumption about bitmap), but a lot of background complexity and probably a greater chance of getting it wrong. If you can tinker with the code so that you have a single type instead of two types, etc, then you can make life easier with the hypothetical system, but it is still simpler to handle things the way shown in the working code, I think.
I concur with gbulmer that I probably wouldn't do things this way, but you did state that you had reduced the sizes of the structures dramatically (for which, thanks!) and it would become more enticing as the number of fields increases (but I'd only write out the comparisons for one pair of structure types once, in a single function).
You could also revise the macro to:
#define CHECK(cond, bitmap, bit) do \
{ if (cond) \
{ printf("failed check at %d: %s\n", __LINE__, STRINGIFY(cond)); \
(bitmap) |= (1 << (bit)); \
} \
} while (0)
CHECK(obj1.a != obj2.d, bitmap, val1);
...
CHECK((strcmp(obj3.str1, obj4.str) != 0), bitmap, val6);
where the last line shows that this would allow you to choose arbitrary comparisons, even if they contain commas. Note the extra set of parentheses surrounding the call to strcmp()!
You should be able to do that except you need to use backslash for multi-line macros
#ifndef CHECK
#define CHECK(cond) \
if (!(cond)) { \
printf(" failed check at %x: %s",__LINE__, #cond); \
//set the bit accordingly
}
#endif /* CHECK */
If you want to get really fancy (and terse), you can use the concatenation operator. I also recommend changing your structures around a little bit to have different naming conventions, though without knowing what you're trying to do with it, it's hard to say. I also noticed in your bit field that you have one value that's 0; that won't tell you much when you try to look at that bit value. If you OR 0 into anything, it remains unchanged. Anyway, here's your program slightly re-written:
struct num {
int x1; // formerly a/d
int x2; // formerly b/e
int x3; // formerly c/f
};
enum type {
val1 = 1, // formerly 0
val2 = 2, // formerly 1
val3 = 4, // formerly 2
};
// CHECK uses the catenation operator (##) to construct obj1.x1, obj1.x2, etc.
#define CHECK(__num) {\
if( obj1.x##__num != obj2.x##__num )\
bitmap |= val##__num;\
}
void main( int argc, char** argv ) {
struct num obj1;
struct num obj2;
int bitmap = 0;
CHECK(1);
CHECK(2);
CHECK(3);
}
As a reasonable rule of thumb, when trying to do bit-arrays is C, there needs to be a number that can be used to index the bit.
You can either pass that bit number into the macro, or try to derive it.
Pretty much the only thing available at compile time or run time is the address of a field.
So you could use that.
There are a few questions to understand if it might work.
For your structs:
Are all the fields in the same order? I.e. you can compare c with f, and not c with e?
Do all of the corresponding fields have the same type
Is the condition just equality? Each macro will have the condition wired in, so each condition needs a new macro.
If the answer to all is yes, then you could use the address:
#define CHECK(s1, f1, s2, f2) do \
{ if ((&s1.f1-&s1 != &s2.f2-&s2) || (sizeof(s1.f1)!=sizeof(s2.f2)) \
|| (s1.f1) != (s2.f2) \
{ printf("failed check at %d: ", #s1 "." #f1 "!=" #s1 "." #f1 "\n", \
__LINE__); \
(shared_bitmap) |= (1 << (&s1.f1-&s1)); // test failed \
} \
} while (0)
I'm not too clear on whether it is a bitmap for all comparisons, or one per struct pair. I've assumed it is a bit map for all.
There is quite a lot of checking to ensure you haven't broken 'the two rules':
(&s1.f1-&s1 != &s2.f2-&s2) || (sizeof(s1.f1)!=sizeof(s2.f2))
If you are confident that the tests will be correct, without those constraints, just throw that part of the test away.
WARNING I have not compiled that code.
This becomes much simpler if the values are an array.
I probably wouldn't use it. It seems a bit too tricky to me :-)

Resources