I am trying to compile the libvpx library (webm decoder by google) with IAR embedded workbench for an ARM-A7 (bare metal application).
I managed to pull in all the necessary files, and it compiles, but there is a problem with the data alignment of some variables.
In the library, there is a macro DATA_ALIGNMENT() that expands to the GNUC __attribute__(aligned(n)) preprocessor directive. I think I managed to get this macro to work with the IAR version of data alignment (pragma data alignment), but I get the following warning
"Warning [Pe609]: this kind of pragma may not be used here"
and when I run the code, my variables are not aligned!
When searching for the warning on the internet, they say you cannot use pragma with definitions of the variables, but only when creating a variable of some kind! However, for data alignment you need to do it when defining the struct (and GCC does allow it, so why wouldnt IAR?)
Any help would be appreciated!
CODE
Macro Definitions:
#if (defined(__GNUC__) && __GNUC__) || defined(__SUNPRO_C)
#define DECLARE_ALIGNED(n, typ, val) typ val __attribute__((aligned(n)))
#elif defined(__ICCARM__)
#define CONCAT(a,b) a##b
#define DECLARE_ALIGNED(n, typ, val) CONCAT(DECLARE_ALIGNED_,n) (typ,val)
#define DECLARE_ALIGNED_1(typ, val) _Pragma("data_alignment=1") typ val
#define DECLARE_ALIGNED_8(typ, val) _Pragma("data_alignment=8") typ val
#define DECLARE_ALIGNED_16(typ, val) _Pragma("data_alignment=16") typ val
#define DECLARE_ALIGNED_32(typ, val) _Pragma("data_alignment=32") typ val
#define DECLARE_ALIGNED_256(typ, val) _Pragma("data_alignment=256") typ val
#else
#warning No alignment directives known for this compiler.
#define DECLARE_ALIGNED(n, typ, val) typ val
#endif
Example where used:
typedef struct VP9Decoder {
DECLARE_ALIGNED(16, MACROBLOCKD, mb);
DECLARE_ALIGNED(16, VP9_COMMON, common);
int ready_for_new_data;
int refresh_frame_flags;
...
} VP9Decoder;
I have tried this directly in my IAR compiler (7.40.6) and it works fine:
#define CONCAT(a,b) a##b
#define DECLARE_ALIGNED(n, typ, val) CONCAT(DECLARE_ALIGNED_,n) (typ,val)
#define DECLARE_ALIGNED_8(typ, val) _Pragma("data_alignment=8") typ val
typedef struct
{
int a;
char b;
char pad1;
char pad3;
char pad4;
int c;
char d;
} myType;
void main( void)
{
DELCARE_ALIGNED_4( myType, data);
// So data.a will be aligned to a 4 byte boundary
// data.b will be aligned to four bytes
// data.pad, pad1, pad2 are wasted space.
// data.c will be aligned to four bytes
// data.d will be aligned to four bytes
}
Unless you need to your struct to be in a specific order, for example mapping onto something, then ordering your struct carefully can reduce its size. For example, the padding I inserted in this case would quite likey be inserted by the compiler anyway. The order would be better as int a, int c, char b, char d. as the original struct is probably 16 bytes long due to padding and alignment. Whereas it could to be made to be only 12.
Related
I'm trying to write definitions for AVR C code so that i can access pins by simmple macro like
STATUS_LED_OUT =1;
in GENET_HW_DEF.h file, included to main C file. You can reproduce this bug by including this file into any C project.
I'm using avr studio 6.2 and 7 - both give the same result. I cannot compile getting werid macro unfold message like below. (CPU ATMega1284p)
D:\_SVN\Compass\AVR\Compass_IO_proto\Compass IO_proto\GENET_HW_DEF.h(19,49): error: expected ')' before '&' token
#define REGISTER_BIT(rg,bt) ((volatile _io_reg*)&rg)->bit##bt
^
D:\_SVN\Compass\AVR\Compass_IO_proto\Compass IO_proto\GENET_HW_DEF.h(42,25): info: in expansion of macro 'REGISTER_BIT'
#define STATUS_LED_OUT REGISTER_BIT(PORTB,7)
^
D:\_SVN\Compass\AVR\Compass_IO_proto\Compass IO_proto\GENET_HW_DEF.h(46,2): info: in expansion of macro 'STATUS_LED_OUT'
STATUS_LED_OUT=1;
^
Interesting enough, copied to fresh project with just only one or two declarations compiles fine, until one makes any changes in the declarations - like adding another macro reference. Then it becomes stuck again.
Also - if i comment all macro usages like
STATUS_LED_DIR=1;
STATUS_LED_OUT=1;
then I'm able to compile project and then after uncommenting usage lines it still compiles fine.. untill clean is executed. I'm probably messing with macro quirks but I have no idea where.
typedef struct
{
unsigned int bit0:1;
unsigned int bit1:1;
unsigned int bit2:1;
unsigned int bit3:1;
unsigned int bit4:1;
unsigned int bit5:1;
unsigned int bit6:1;
unsigned int bit7:1;
} _io_reg;
#define REGISTER_BIT(rg,bt) ((volatile _io_reg*)&rg)->bit##bt
#ifndef GENET_HW_DEF
#define GENET_HW_DEF
// define functionalities and flags - comment/uncomment apropriate lines
#define GENET_USART_0 256
#define GENET_USART_1 256
#define F_CPU 20000000UL
#define STATUS_LED 7
#define ERROR_LED 4
#define ADC_GLOBAL_ENABLE A
#define ADC_CHANNEL_0 0
#define ADC_CHANNEL_1 4
#define ADC_CHANNEL_2 2
#define ADC_CHANNEL_3 3
#define ADC_CHANNEL_4 1
#define ADC_CHANNEL_5 5
#define ADC_CHANNEL_6 6
#define ADC_CHANNEL_7 7
// actual definitions and initialization
#ifdef STATUS_LED
#define STATUS_LED_OUT REGISTER_BIT(PORTB,STATUS_LED)
#define STATUS_LED_DIR REGISTER_BIT(DDRB,STATUS_LED)
#define STATUS_LED_PIN REGISTER_BIT(PINB,STATUS_LED)
STATUS_LED_DIR=1;
STATUS_LED_OUT=1;
#endif
#ifdef ERROR_LED
#define ERROR_LED_OUT REGISTER_BIT(PORTC,ERROR_LED)
#define ERROR_LED_DIR REGISTER_BIT(DDRC,ERROR_LED)
ERROR_LED_DIR=1;
ERROR_LED_OUT=1;
#endif
#ifdef GENET_USART_0
#define USART0_ENABLED
#define UART_RX0_BUFFER_SIZE GENET_USART_0
#define UART_TX0_BUFFER_SIZE GENET_USART_0
#endif
#ifdef GENET_USART_1
#define USART1_ENABLED
#define UART_RX1_BUFFER_SIZE GENET_USART_1
#define UART_TX1_BUFFER_SIZE GENET_USART_1
#endif
#endif
I reproduced your problem.
You cannot call STATUS_LED_DIR=1; outside code execution flow. This must be inside a function (for example main()).
Now you will end with other compilation errors but this was the main mistake.
Second correction, you need 2 level for concatenation
#define CONCAT(bt) bit##bt
#define REGISTER_BIT(rg,bt) ((volatile _io_reg*)&rg)->CONCAT(bt)
im new with c language, but i try to unerstand this quark hashing algorithm that was written in c language, and i found an error while im compiling the source code, from what i understand the WIDTH it already declared, but why is it still error ?
this is the source code
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
/* uncomment to printf execution traces */
// #define DEBUG
#if defined(UQUARK)
#define CAPACITY 16
#define RATE 1
#define WIDTH 17
#elif defined(DQUARK)
#define CAPACITY 20
#define RATE 2
#define WIDTH 22
#endif
#define DIGEST WIDTH
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint8_t u8;
typedef struct {
int pos; /* number of bytes read into x from current block */
// u32 x[ WIDTH*8 ]; /* one bit stored in each word */
u32 x[ WIDTH*8 ]; /* one bit stored in each word */
} hashState;
#if defined(UQUARK)
/* 17 bytes */
u8 iv[] = {0xd8,0xda,0xca,0x44,0x41,0x4a,0x09,0x97,
0x19,0xc8,0x0a,0xa3,0xaf,0x06,0x56,0x44,0xdb};
and its showing this error
quark.c:36:10: error : 'WIDTH' undeclared here (not in a function)
u32 x[WIDTH*8];
I guess for some reason neither UQUARK nor DQUARK are defined.
Add this:
#if defined(UQUARK) && defined(DQUARK)
#error both UQUARK and DQUARK are defined
#endif
#if !defined(UQUARK) && !defined(dQUARK)
#error Neither UQUARK nor DQUARK are defined
#endif
just before following line:
#if defined(UQUARK)
Then the compilation will abort if either both UQUARK and DQUARK are defined (which probably makes no sense) or if neither UQUARK nor DQUARK are defined (which probably happens in your case).
Now the question is: who defines UQUARK and/or DQUARK? Only you can tell.
For starters, I don't have a lot of experience using C. For a small sensor project I've been busy programming a PIC micro-controller however I've now run into the following "problem".
To make the code more readable I want to combine certain values from the PIC library into a couple of structures. To me a proper code structure would seem that I will typedef the structure I want and initialize it in a separate file from the main program and then import this into the main file. This would prevent the main code file from being cluttered with these initializations which I think is desirable.
Some pseudo code to clarify. pin_A and RESULT_A are from the PIC library:
sensor.h
// Define the structures for the individual sensor pads and the sensors.
typedef struct{
int pin;
unsigned int result;
}PAD;
typedef struct{
PAD A;
PAD B;
}SENSOR;
sensor.c
(I use sensor.c as an example here because I assume that is where the initialization should happen, but please correct me if I'm wrong.)
#include "sensor.h"
PAD PAD_A = {.pin=pin_A, .result=RESULT_A};
PAD PAD_B = {.pin=pin_B, .result=RESULT_B};
SENSOR sens = {.A=PAD_A, .B=PAD_B}
main.c
#include ... something
void main(){
// call a constructor or something
while(1){
printf(sens.A.pin); // I don't really want to print however this is an example of course.
};
To me something like this should be very common in coding however I've been unable to find helpful information on how to approach this. I assume I'm missing something obvious so please point it out.
My current workaround is to include the sensor.h file in main.c and do all the initialization there. However this makes for very cluttered and non portable code to I can't imagine that is how it should be done.
Regards,
----------------------------- Edit ------------------------------------
Because the question appears to be unclear I will try and add to it. The core of my confusion was that I was unable to define, and initialize e.t.c., certain variables when using multiple files. One method I imagine will work (though not it's not popular among many) is the use of global variables. My problem was that nearly all examples that I could find that do this provide code only in a single file and don't use header files that I believe are required to provide structure to a larger project.
For future references, in the end I have settled on the use of a constructor function as opposed to global variables. This was based on the post referenced by jonathan leffler in the comments below. I'll add the code I'm currently using pertaining to this question as an example to others. Though it is likely not the most optimized and will contain some style errors it is functional.
padDefinitions.h
/* As provided by microchip but renamed for convenience */
#define PIN_A 17
#define PIN_B 12
#define PIN_C 18
#define PIN_D 16
#define PIN_E 15
#define PIN_F 13
#define PIN_G 5
#define PIN_H 1
#define PIN_I 0
#define PIN_J 11
#define PIN_K 10
#define PIN_L 9
#define PIN_GUARD 14
#define RESULT_A &ADC1BUF17
#define RESULT_B &ADC1BUF12
#define RESULT_C &ADC1BUF18
#define RESULT_D &ADC1BUF16
#define RESULT_E &ADC1BUF15
#define RESULT_F &ADC1BUF13
#define RESULT_G &ADC1BUF5
#define RESULT_H &ADC1BUF1
#define RESULT_I &ADC1BUF0
#define RESULT_J &ADC1BUF11
#define RESULT_K &ADC1BUF10
#define RESULT_L &ADC1BUF9
#define RESULT_GUARD &ADC1BUF14
sensordef.h
/* Defines structures for the sensor pads and the sensor (which is a combination of pads) */
typedef struct PAD{
int pin; // The pin on the device the pad is connected to
volatile unsigned int current_value; // The current value of the pad
unsigned int calculated_value; // Storage for calculated (iir) value
}PAD;
typedef struct SENSOR{
PAD A;
PAD B;
PAD C;
PAD D;
PAD E;
PAD F;
PAD G;
PAD H;
PAD I;
PAD J;
PAD K;
PAD L;
}SENSOR;
/* Declare the constructor functions */
PAD PadConstructor();
SENSOR SensorConstructor();
sensordef.c
#include "sensordef.h"
#include "padDefinitions.h"
SENSOR SensorConstructor(){
/* Initialize the pads and sensor variables */
// Define all the pads
PAD PAD_A = {.pin=PIN_A, .current_value=*RESULT_A};
PAD PAD_B = {.pin=PIN_B, .current_value=*RESULT_B};
PAD PAD_C = {.pin=PIN_C, .current_value=*RESULT_C};
PAD PAD_D = {.pin=PIN_D, .current_value=*RESULT_D};
PAD PAD_E = {.pin=PIN_E, .current_value=*RESULT_E};
PAD PAD_F = {.pin=PIN_F, .current_value=*RESULT_F};
PAD PAD_G = {.pin=PIN_G, .current_value=*RESULT_G};
PAD PAD_H = {.pin=PIN_H, .current_value=*RESULT_H};
PAD PAD_I = {.pin=PIN_I, .current_value=*RESULT_I};
PAD PAD_J = {.pin=PIN_J, .current_value=*RESULT_J};
PAD PAD_K = {.pin=PIN_K, .current_value=*RESULT_K};
PAD PAD_L = {.pin=PIN_L, .current_value=*RESULT_L};
// Define a variable for the sensor
SENSOR sensor = {
.A=PAD_A, .B=PAD_B, .C=PAD_C,
.D=PAD_D, .E=PAD_E, .F=PAD_F,
.G=PAD_G, .H=PAD_H, .I=PAD_I,
.J=PAD_J, .K=PAD_K, .L=PAD_L,
};
return sensor;
}
main.c
int16_t main(void){
#include "sensordef.h"
/* ... Initialization code for the PIC ... */
SENSOR sensor;
sensor = SensorConstructor();
while(1)
{
/* ... Perform measurements ... */
}
}
You can write an extern variable declaration into your sensor.h file:
sensor.h
…other definitions…
typedef struct{
PAD A;
PAD B;
} SENSOR;
extern SENSOR sens;
main.c
#include "sensor.h"
Now the compiler knows that there is a global variable sens with type SENSOR.
As jonathan leffler pointed out in the comments. How do I use extern to share variables between source files in C? provides ample information on regarding my question. It provides a clear, and very lengthy, example of how to use multiple files for declaration and definition of variables.
Thank you Jonathan. (+1 from me if I had the reputation to do so)
I use C bitfields to store data in memory. For archive usage these data has to be written to a file (and later on be combined with data from another machine). It seems to be a bad idea to save the bitfields directly to the file, since the arrangement of data is implementation specific.
For this reason I wrote some methods to "serialize" these bitfields to save them in a unified format:
/* uint16 is a unsigned int with size 16 */
typedef struct {
uint16 a : 1;
/* ... just examples ... */
uint16 z : 13;
} data;
void save_data(FILE* fp, data d) {
uint16 tmp;
tmp = d.a;
fwrite(&tmp,sizeof(uint16),1,fp);
/* ... */
tmp = d.z;
fwrite(&tmp,sizeof(uint16),1,fp);
}
While this is perfectly working, it seems not to be well to extend, since adding more members in data requires adding the data to the save routine as well.
Is there any trick to automatically convert bitfield data to a unified format without needing to adapt the routine/macro when changing the bitfield data?
Here is one method. I cannot recommend it, but it's out there and it sort of works, so why not look at it. This incarnation is still platform-dependent but you can easily switch to a platform-independent, possibly human-readable format. Error handling is omitted for brevity.
// uglymacro.h
#if defined(DEFINE_STRUCT)
#define BEGINSTRUCT(struct_tag) typedef struct struct_tag {
#define ENDSTRUCT(struct_typedef) } struct_typedef;
#define BITFIELD(name,type,bit) type name : bit;
#define FIELD(name,type) type name;
#define ARRAYFIELD(name,type,size) type name[size];
#elif defined(DEFINE_SAVE)
#define BEGINSTRUCT(struct_tag) void save_##struct_tag(FILE* fp, \
struct struct_tag* p_a) {
#define ENDSTRUCT(struct_typedef) }
#define BITFIELD(name,type,bit) { type tmp; tmp = p_a->name; \
fwrite (&tmp, sizeof(type), 1, fp); }
#define FIELD(name,type) { fwrite (&p_a->name, sizeof(p_a->name), 1, fp); }
#define ARRAYFIELD(name,type,size) { fwrite (p_a->name, sizeof(p_a->name[0]), size, fp); }
#elif defined(DEFINE_READ)
#define BEGINSTRUCT(struct_tag) void read_##struct_tag(FILE* fp, \
struct struct_tag* p_a) {
#define ENDSTRUCT(struct_typedef) }
#define BITFIELD(name,type,bit) { type tmp; fread (&tmp, sizeof(type), 1, fp); \
p_a->name = tmp; }
#define FIELD(name,type) { fread (&p_a->name, sizeof(p_a->name), 1, fp); }
#define ARRAYFIELD(name,type,size) { fread (p_a->name, sizeof(p_a->name[0]), size, fp); }
#else
#error "Must define either DEFINE_STRUCT or DEFINE_SAVE or DEFINE_READ"
#endif
#undef DEFINE_STRUCT
#undef DEFINE_READ
#undef DEFINE_WRITE
#undef BEGINSTRUCT
#undef ENDSTRUCT
#undef FIELD
#undef BITFIELD
#undef ARRAYFIELD
Your struct definition looks like this:
// mystruct_def.h
BEGINSTRUCT(mystruct)
BITFIELD(a,int,1)
FIELD(b,int)
ARRAYFIELD(c,int,10)
ENDSTRUCT(mystruct)
You use it like this:
// in mystruct.h file
#define DEFINE_STRUCT
#include "uglymacro.h"
#include "mystruct_def.h"
// in mystruct.c file
#include "mystruct.h"
#define DEFINE_READ
#include "mystruct_def.h"
#define DEFINE_WRITE
#include "mystruct_def.h"
Frankly, by modern standards this method is ugly. I have used something similar about 20 years ago and it was ugly back then.
Another alternative is using a more humane code-generation facility instead of the C preprocessor.
If you are willing to invest a bit you can use tools like P99 for "statement unrolling":
// in header
#define MY_BITFIELDS a, z
#define WRITE_IT(X) fwrite(&(unsigned){ d.X }, sizeof(unsigned), 1, fp)
#define WRITE_ALL(...) P99_SEP(WRITE_IT, __VA_ARGS__)
// in your function
WRITE_ALL(MY_BITFIELDS);
BTW, never use int for bitfields if you can avoid this. The semantic of a set of bits is much better matched by unsigned.
With a bit of more macro coding you could even use something like
#define MY_BITFIELDS (a, 1), (z, 11)
to produce the struct declaration and the write part.
Why not use a human readable text format?
typedef struct {
int a : 1;
/* ... just examples ... */
int z : 13;
} data;
void save_data(FILE* fp, data d) {
fprintf( fp, "a:%d\n", d.a );
fprintf( fp, "b:%d\n", d.b );
...
fprintf( fp, "z:%d\n", d.z );
}
The advantage of this technique is that somebody using any different language could quickly write a parser to load your data on any machine, any architecture.
I'm trying to design a portable API in ANSI C89/ISO C90 to access a wireless networking device on a serial interface. The library will have multiple network layers, and various versions need to run on embedded devices as small as an 8-bit micro with 32K of code and 2K of data, on up to embedded devices with a megabyte or more of code and data.
In most cases, the target processor will have a single network interface and I'll want to use a single global structure with all state information for that device. I don't want to pass a pointer to that structure through the network layers.
In a few cases (e.g., device with more resources that needs to live on two networks) I will interface to multiple devices, each with their own global state, and will need to pass a pointer to that state (or an index to a state array) through the layers.
I came up with two possible solutions, but neither one is particularly pretty. Keep in mind that the full driver will potentially be 20,000 lines or more, cover multiple files, and contain hundreds of functions.
The first solution requires a macro that discards the first parameter for every function that needs to access the global state:
// network.h
typedef struct dev_t {
int var;
long othervar;
char name[20];
} dev_t;
#ifdef IF_MULTI
#define foo_function( x, a, b, c) _foo_function( x, a, b, c)
#define bar_function( x) _bar_function( x)
#else
extern dev_t DEV;
#define IFACE (&DEV)
#define foo_function( x, a, b, c) _foo_function( a, b, c)
#define bar_function( x) _bar_function( )
#endif
int bar_function( dev_t *IFACE);
int foo_function( dev_t *IFACE, int a, long b, char *c);
// network.c
#ifndef IF_MULTI
dev_t DEV;
#endif
int bar_function( dev_t *IFACE)
{
memset( IFACE, 0, sizeof *IFACE);
return 0;
}
int foo_function( dev_t *IFACE, int a, long b, char *c)
{
bar_function( IFACE);
IFACE->var = a;
IFACE->othervar = b;
strcpy( IFACE->name, c);
return 0;
}
The second solution defines macros to use in the function declarations:
// network.h
typedef struct dev_t {
int var;
long othervar;
char name[20];
} dev_t;
#ifdef IF_MULTI
#define DEV_PARAM_ONLY dev_t *IFACE
#define DEV_PARAM DEV_PARAM_ONLY,
#else
extern dev_t DEV;
#define IFACE (&DEV)
#define DEV_PARAM_ONLY void
#define DEV_PARAM
#endif
int bar_function( DEV_PARAM_ONLY);
// I don't like the missing comma between DEV_PARAM and arg2...
int foo_function( DEV_PARAM int a, long b, char *c);
// network.c
#ifndef IF_MULTI
dev_t DEV;
#endif
int bar_function( DEV_PARAM_ONLY)
{
memset( IFACE, 0, sizeof *IFACE);
return 0;
}
int foo_function( DEV_PARAM int a, long b, char *c)
{
bar_function( IFACE);
IFACE->var = a;
IFACE->othervar = b;
strcpy( IFACE->name, c);
return 0;
}
The C code to access either method remains the same:
// multi.c - example of multiple interfaces
#define IF_MULTI
#include "network.h"
dev_t if0, if1;
int main()
{
foo_function( &if0, -1, 3.1415926, "public");
foo_function( &if1, 42, 3.1415926, "private");
return 0;
}
// single.c - example of a single interface
#include "network.h"
int main()
{
foo_function( 11, 1.0, "network");
return 0;
}
Is there a cleaner method that I haven't figured out? I lean toward the second since it should be easier to maintain, and it's clearer that there's some macro magic in the parameters to the function. Also, the first method requires prefixing the function names with "_" when I want to use them as function pointers.
I really do want to remove the parameter in the "single interface" case to eliminate unnecessary code to push the parameter onto the stack, and to allow the function to access the first "real" parameter in a register instead of loading it from the stack. And, if at all possible, I don't want to have to maintain two separate codebases.
Thoughts? Ideas? Examples of something similar in existing code?
(Note that using C++ isn't an option, since some of the planned targets don't have a C++ compiler available.)
I like your second solution. I just prefer declaring every function twice rather than have that PARAM macro in the public header. I much prefer to put macro hijinks in the hidden C file.
// common header
#ifdef IF_MULTI
int foo_func1(dev_t* if, int a);
int foo_func2(dev_t* if, int a, int b);
int foo_func3(dev_t* if);
#else
int foo_func1(int a);
int foo_func2(int a, int b);
int foo_func3();
#endif
// your C file
#ifdef IF_MULTI
#define IF_PARM dev_t* if,
#define GET_IF() (if)
#else
dev_t global_if;
#define IF_PARM
#define GET_IF() (&global_if)
#endif
int foo_func1(IF_PARM int a)
{
GET_IF()->x = a;
return GET_IF()->status;
}
int foo_func2(IF_PARM int a, int b)
int foo_func3(IF_PARM);
Here's a solution that won't work if you have threads (or switch interfaces on re-entrance or something like that), but it is a clean interface, and it might work for you.
You could have your single instance functions using a global DEV, and have your multi interface functions set this global and call their single instance counterparts.
For example:
dev_t *DEV;
int foo_function(int x, int y)
{
/* DEV->whatever; */
return DEV->status;
}
int foo_function_multi(dev_t *IFACE, int x, int y)
{
DEV = IFACE;
return foo_function(x, y);
}
Another option is to use variadic args, and pass and fetch an extra arg (which contains the interface to use) #ifdef MULTI, but that's horrible because you lose your type safety, and would prevent passing the arg in a register which you possibly care quite a bit about on your platform. Also, all functions with variadic args must have at least one named argument, and your question is all about avoiding arguments! But anyway:
#ifndef MULTI
dev_t *DEV;
#endif
int foo(int x, int y, ...)
{
#ifdef MULTI
va_list args;
va_start(args, y);
dev_t *DEV = va_arg(args, (dev_t*));
va_end(args);
#endif
/* DEV->whatever */
return DEV->status;
}
// call from single
int quux()
{
int status = foo(23, 17);
}
// call from multi
int quux()
{
int status = foo(23, 17, &if0);
}
Personally I prefer your first solution :-)
This will work on gcc:
#ifdef TOMSAPI_SMALL
#define TOMSAPI_ARGS( dev, ...) (__VA_ARGS__)
#else // ! TOMSAPI_SMALL
#define TOMSAPI_ARGS( dev, ...) (dev, ## __VA_ARGS__)
#endif // TOMSAPI_SMALL
#ifdef TOMSAPI_SMALL
#define TOMSAPI_DECLARE_DEVP(local_dev_ptr) device_t * local_dev_ptr = &global_dev; NULL
// The trailing NULL is to make the compiler make you put a ; after calling the macro,
// but without allowing something that would mess up the declaration if you forget the ;
// You can't use the do{...}while(0) trick for a variable declaration.
#else // ! TOMSAPI_SMALL
#define TOMSAPI_DECLARE_DEVP(local_dev_ptr) device_t * local_dev_ptr = arg_dev; NULL
#endif // TOMSAPI_SMALL
and then
int tomsapi_init TOMSAPI(device_t *arg_dev, void * arg_for_illustration_purposes ) {
TOMSAPI_DECLARE_DEVP( my_dev );
my_dev->stuff = arg_for_illustration_purposes;
return 0;
}
Using this method you would have to ensure that all of your API functions used the same name for the device pointer, but all of your function definitions and declarations would look like they needed the full number of arguments. If this were not important to you you could do:
#ifdef TOMSAPI_SMALL
#define TOMSAPI_ARGS(...) (__VA_ARGS__)
#else // ! TOMSAPI_SMALL
#define TOMSAPI_ARGS(...) (device_t *dev, ## __VA_ARGS__)
#endif // TOMSAPI_SMALL
#ifdef TOMSAPI_SMALL
#define TOMSAPI_DECLARE_DEVP() device_t * dev = &global_dev; NULL
#else // ! TOMSAPI_SMALL
#define TOMSAPI_DECLARE_DEVP(local_dev_ptr) NULL
#endif // TOMSAPI_SMALL
and then
int tomsapi_init TOMSAPI(void * arg_for_illustration_purposes ) {
dev->stuff = arg_for_illustration_purposes;
return 0;
}
But this ends up looking like dev is never declared to someone reading your code.
All of that being said, you may find that on the single device small platform that using a global device struct ends up costing more than passing the pointer around due to the number of times the address of this struct will have to be reloaded. This is more likely if you API is stacked (some of your functions call other of your functions and pass them the dev pointer), uses a lot of tail recursion, and/or your platform uses registers for passing most arguments rather than the stack.
EDIT:
I just realized that there could be a problem with this method if you have api functions which take no additional arguments, even if you do use the ## operator if your compiler wants to force you to say int foo(void) for functions that take no arguments.