Reputation: 473
I would like to force a functions parameters to accept only specific definitions. For example, consider #define OUTPUT 1
, #define INPUT 0
and void restrictedFunction(int parameter);
.
How would I force restrictedFunction(int parameter)
to accept only OUTPUT
or INPUT
?
I would also like to take into consideration that another definition may have the same value, for example, #define LEFT 1
and #define RIGHT 0
.
So in this case I would like restrictedFunction(int parameter)
to be able to accept only OUTPUT
and INPUT
specifically.
Upvotes: 2
Views: 171
Reputation: 9972
You can use a wrapper to validate the argument:
#define restrictedFunction(x) do { \
static_assert((x) == INPUT || (x) == OUTPUT); \
assert(!strcmp(#x, "INPUT") || !strcmp(#x, "OUTPUT")); \
restrictedFunction(x); \
} while(0)
Notes:
restrictedFunction()
returns a void.
If it returns a value which you actually use, you'll need something like gcc's compound statement http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html. Or--better--you can use BUILD_BUG_ON_ZERO
(see What is ":-!!" in C code?), which I keep forgetting about, because it doesn't seem to work with C++.do ... while(0)
is to "swallow the semi-colon"; not really relevant here.static_assert()
is a compile-time assert; there are many variants available. Here is a link to one, https://stackoverflow.com/a/9059896/318716, if you don't have your own handy.assert()
is the standard run-time assert.static_assert(),
you can replace the run-time assert()
with a compile-time assert when the two !strcmp()
's are replaced with ==
; see example below. I haven't tested this with other compilers.x
is only used once in the macro expansion, since the first four references are only used at compile-time.When your actually define your function, you'll have to add parentheses to disable the macro expansion, as in:
void (restrictedFunction)(int x){ ... }
Also, if your code has a special case (whose code doesn't?) where you need to call restrictedFunction()
with the argument foo,
you'll need to write:
(restrictedFunction)(foo);
Here is a complete example, which puts a wrapper around the standard library function exit()
:
#include <stdlib.h>
#define CONCAT_TOKENS(a, b) a ## b
#define EXPAND_THEN_CONCAT(a,b) CONCAT_TOKENS(a, b)
#define ASSERT(e) enum{EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__) = 1/!!(e)}
#define ASSERTM(e,m) enum{EXPAND_THEN_CONCAT(m##_ASSERT_line_,__LINE__)=1/!!(e)}
#define exit(x) do { \
ASSERTM((x) == EXIT_SUCCESS || (x) == EXIT_FAILURE, value); \
ASSERTM(#x == "EXIT_SUCCESS" || #x == "EXIT_FAILURE", symbol); \
exit(x); \
} while(0)
int main(void) {
exit(EXIT_SUCCESS); // good
exit(EXIT_FAILURE); // good
exit(0); // bad
exit(3); // doubly bad
}
If I try to compile it, I get:
gcc foo.c -o foo
foo.c: In function 'main':
foo.c:17: error: enumerator value for 'symbol_ASSERT_line_17' is not an integer constant
foo.c:18: warning: division by zero
foo.c:18: error: enumerator value for 'value_ASSERT_line_18' is not an integer constant
foo.c:18: error: enumerator value for 'symbol_ASSERT_line_18' is not an integer constant
Upvotes: 1
Reputation: 754410
typedef enum { INPUT = 0, OUTPUT = 1 } IO_Type;
void restrictedFunction(IO_Type parameter) { ... }
It doesn't absolutely force the use of the values (the compiler will let someone write restrictedFunction(4)
), but it is about as good as you'll get.
If you truly want to force the correct type, then:
typedef enum { INPUT = 0, OUTPUT = 1 } IO_Type;
typedef struct { IO_Type io_type } IO_Param;
void restrictedFunction(IO_Param parameter) { ... }
In C99 or later, you could call that with:
restrictedFunction((IO_Param){ INPUT });
This is a compound literal, creating a structure on the fly. It is not entirely clear that the structure type really buys you very much, but it will force the users to think a little and may improve the diagnostics from the compiler when they use it wrong (but they can probably use restrictedFunction((IO_Param){ 4 });
still).
What this means is that your restrictedFunction()
code should be ready to validate the argument:
void restrictedFunction(IO_Type io_type)
{
switch (io_type)
{
case INPUT:
...do input handling...
break;
case OUTPUT:
...do output handling...
break;
default:
assert(io_type != INPUT && io_type != OUTPUT);
...or other error handling...
break;
}
}
Upvotes: 3
Reputation: 212354
You don't get quite as much protection as you might like, but you can do:
enum func_type { INPUT, OUTPUT };
void restrictedFunction( enum func_type parameter );
Upvotes: 1
Reputation: 13196
You could use an enum.
typedef enum TrafficDirection { INPUT = 0, OUTPUT = 1 } TrafficDirection;
restrictedFunction(TrafficDirection direction);
of course, this isn't perfect. You can still pass any int to it as long as you use a cast.
restrictedFunction((TrafficDirection) 4);
Upvotes: 1