I was writing a little source file function for my Pic32 and I got stucked on one thing.
It's basically an utility that should store incomming char data into buffer and then, if '\r' is recieved, it compares the buffer against list of commands (in array names), and if match is found, the index of the item is returned.
This part is from header:
#define NAMECNT 6
static const char names[NAMESCNT][10] = { // 6commands, max 10 char each
"korr", // 1
"adc", // 2
"fft", // 3
"data", // 4
"pr", // 5
"prsc"}; // 6
/* functions */
extern int comm(char cdata);
At the main file, there is one big switch:
switch( comm(recieved_ch) ){
case 1: foo1(); break;
case 2: foo2(); break;
...
}
Now, for the better clarity, I wanted to use instead of 1, 2, ... the original names (like case KORR: case ADC:) so I wrote deffinitions for each one of them
#define KORR 1
#define ADC 2
But I don't like that solution, because I want to use this source file in more projects and there is gonna be different list of commands for each. Is there any way how to do this?
Best thing would be to create the array names in preprocessor, but I doubt that's even possible. I was thinking about using enum type (which would have same items as list of commands names), but I am not sure how would that go.
You can use X-macros to build an enum and fill the array, then you can use the enum values in the switch:
#define VARS \
X(korr) \
X(adc) \
X(fft) \
X(data) \
X(pr) \
X(prsc)
static const char names[][10] = { // 6commands, max 10 char each
#define X(name) #name,
VARS
#undef X
};
enum evars {
#define X(name) name,
VARS
#undef X
};
extern int comm(char cdata);
int main(void)
{
char x = 1;
switch (comm(x)) {
case korr:
printf("korr");
break;
case adc:
printf("adc");
break;
/* ... and so on */
}
return 0;
}
The expansion of X is:
static const char names[][10] = {
"korr", "adc", "fft", "data", "pr", "prsc",
};
enum evars {
korr, adc, fft, data, pr, prsc,
};
Edit: As pointed out by #5gon12eder, you don't need to hardcode 6 in the first dimension of the array (you can leave it unspecified).
The preprocessor could make things clearer here, I think, using the concatentation operator ##, but it'll not yield a performance advantage. A switch statement could be optimized by the compiler, but that's implementation-dependent.
Instead of the "one big switch," use an array of function pointers. Something like
func_ptrs[comm(received_ch) - 1]();
will call the corresponding function, where foo1 is at index 0, foo2 at 1, etc. To add a command, simply append a command name to the command list and a function pointer func_ptrs.
After all, you kill two birds with one stone: you create an easy way to add commands and improve performance.
Besides, a linear search through an array of strings is pretty inefficient. A hash table would yield a performance advantage.
Related
I have various array content "templates" of type uint8_t that I'd like to define in a special header file. Those content templates also have different lengths:
#define CONTENT_VARIANT_A { 5, 3, 8, 1, 4, 23 }
#define CONTENT_VARIANT_B { 1, 10, 2 }
#define CONTENT_VARIANT_C { 4, 39, 2, 39 }
// '0' is not a valid element value (=> can be used for loop termination)
#define CONTENT_MAX_SIZE = 20;
In my code, I'd like to have a method to set the content of an array buffer to one of those pre-defined values. This is my code so far, using switch and memcpy:
Method to set the content:
void SetBuffer(uint8_t *my_buffer, uint8_t chosen_content) {
memset(my_buffer, 0, CONTENT_MAX_SIZE);
switch (chosen_content) {
case CHOICE_VARIANT_A: {
uint8_t new_content[] = CONTENT_VARIANT_A;
memcpy(my_buffer, new_content, sizeof(new_content));
break;
}
case CHOICE_VARIANT_B: {
uint8_t new_content[] = CONTENT_VARIANT_B;
memcpy(my_buffer, new_content, sizeof(new_content));
break;
}
case CHOICE_VARIANT_C: {
uint8_t new_content[] = CONTENT_VARIANT_C;
memcpy(my_buffer, new_content, sizeof(new_content));
break;
}
}
}
Usage:
// Buffer declaration (done once)
uint8_t my_buffer[CONTENT_MAX_SIZE] = { 0 };
// Buffer population + usage (executed multiple times, with varying values for 'chosen_content')
SetBuffer(my_buffer, chosen_content);
uint8_t i = 0;
while (i < CONTENT_MAX_SIZE && my_buffer[i] > 0) {
// ...
++i;
}
I'm a C# programmer, and new to C; the code in SetBuffer seems overly complicated to me, but is the only thing my mind could come up with that should work (with regards to what I think I know about C), and that also compiles. Is it the correct way of doing what I want, or is it pell-mell and should be done completely different?
In case the zero-out isn't necessary, you can shave the function down to something like this:
void SetBuffer(uint8_t *my_buffer, uint8_t chosen_content) {
switch (chosen_content) {
case CHOICE_VARIANT_A: memcpy(my_buffer, (uint8_t[])CONTENT_VARIANT_A, sizeof((uint8_t[])CONTENT_VARIANT_A)); break;
case CHOICE_VARIANT_B: memcpy(my_buffer, (uint8_t[])CONTENT_VARIANT_B, sizeof((uint8_t[])CONTENT_VARIANT_B)); break;
case CHOICE_VARIANT_C: memcpy(my_buffer, (uint8_t[])CONTENT_VARIANT_C, sizeof((uint8_t[])CONTENT_VARIANT_C)); break;
}
}
Where (uint8_t[])CONTENT_VARIANT_A is not a cast but together with the macro forms a compound literal. Essentially a local, anonymous temporary array. The sizeof expression is similar and calculated at compile-time.
If you must zero-out non-used cells, then replace (uint8_t[]) with (uint8_t[CONTENT_MAX_SIZE]). C guarantees that items not contained in the initializer list gets set to zero.
Yet another alternative for speed over readability is an evil macro:
#define SetBuffer(my_buffer, content) \
memcpy(my_buffer, \
(uint8_t[])CONTENT_VARIANT_##content, \
(uint8_t[])CONTENT_VARIANT_##content)
Call as SetBuffer(buf, A); etc. It's fairly type safe since unknown letter prefixes will result in compiler errors. You might also want to ask yourself why you aren't simply using memcpy on the caller side.
So you can dynamically define a method with its own name like this:
#define test(name) void name() { print("#name"); }
Then you can call it like:
test(foo);
foo();
I'm wondering though if you can make a "callback"-style form, like this:
#define test(name, body) void name() { print(#name); body(); }
Where it invokes a body that is defined as sort of a "block" like this:
test(dosomething, {
int a = add(1, 1);
assert(a == 2);
})
But more than that, I would like to pass a callback for async functions to say they are complete, like this:
test(dosomething, { (done)
int a = add(1, 1);
assert(a == 2);
done();
})
In addition, I am defining these outside of the main, so it would be defined in the same scope as a normal function. Because of that, the tests aren't going to automatically run. They need to be iterated over. As such, they probably need to be pushed into an array of some sort. So wondering how that could be done, if macros allow you to sort of capture stuff into an array, or to build up an enum one #define at a time.
#define test(name, body) void name() { \
print(#name); \
} \
\
TESTS[CURRENT_TEST++] = &name \ // push the test into a TESTS array.
So then in main you can iterate over them:
int
main() {
iterate over TESTS...
}
To summarize, I am wondering how to #define this at the file body level (i.e. not in main, but at the level of functions):
void afunction() { printf("Foo"); }
test(dosomething, { (done)
int a = add(1, 1);
assert(a == 2);
done();
})
void anotherfunction() { printf("Bar"); }
such that I can iterate over the tests in main.
This suggests blocks are possible in macros.
Looks like you're building some sort of mini test framework using the c preprocessor.
There's a caveat for the bodies; to the C preprocessor, curly brackets and square brackets are just tokens. Parenthesized expressions are recognized (i.e., parentheses are matched), and commas are recognized as delimiters. So for example, this macro invocation:
test(dosomething, { int a = add(1, 1); assert(a == 2); })
...has two arguments despite having two commas (because the second comma is "hugged" in a parenthesized set), but that's a bit misleading. This invocation:
test(dosomething, { enum { red, green, blue }; assert(red+1==green); })
...has four arguments: 1: dosomething, 2: { enum { red, 3: green, and 4: blue }; assert(red+1==green); }. If you're going to do this, you probably want to cover cases like this... there are basic strategies: (a) hug the body in parentheses (you can unwrap it in expansion), or (b) use variadic macros.
They need to be iterated over.
Sounds like a job for x-macros (below I'll be using the parameterized-macro flavor of x-macros).
But more than that, I would like to pass a callback for async functions to say they are complete, like this:
...you can't add an argument in the middle, but the braces don't have to be part of this (they don't help anyway, since the preprocessor ignores them). So for the above, we probably want to pick the hug option. That leaves your invocations looking like this:
test(dosomething, (int a=add(1,1); assert(a==2);), done)
However, since we're ripping the curly braces out, we can put them in arbitrary places in our expansion and do arbitrary things in between. Since I'm guessing you want the same kind of async thing going on, we could just put that thing in the expansion that generates the definition rather as an argument.
Here's roughly what it would look like, using a parameterized macro version of x-macros, and applying an async on expansion (using semaphores to demonstrate how arbitrary this could be):
#define APPLY_TEST_MACROS(macro) \
macro(test_add, (int a=add(1,1); assert(a==2); )) \
macro(test_sub, (int a=sub(5,2); assert(a==3); )) \
macro(test_mul, (int a=mul(3,4); assert(a==12); ))
#define UNWRAP(...) __VA_ARGS__
#define MAKE_ASYNC_SEM(NAME_, BODY_) \
void NAME_() { \
sem_wait(&test_sem_ctl); print(#NAME_); sem_post(&test_sem_ctl); \
UNWRAP BODY_ \
sem_wait(&test_sem_ctl); \
if (0==--tests_remaining) sem_post(&test_sem_done); \
sem_post(&test_sem_ctl); \
}
#define COUNT_TESTS(NAME_, BODY_) +1
sem_t test_sem_ctl;
sem_t test_sem_done;
void init_semaphores() {
sem_init(&test_sem_ctl, 0, 1);
sem_init(&test_sem_done, 0, 0);
}
// iterate over tests to count them
unsigned int tests_remaining = APPLY_TEST_MACROS(COUNT_TESTS);
// define the tests
APPLY_TEST_MACROS(MAKE_ASYNC_SEM)
...and so forth (I'm stopping here because the idea is to convey the idea, not code it for you). The x-macro layout allows you to iterate in the preprocessor, so you can do something like spawn a thread per test; you could also just use this same approach to build an array of test functions if, say, you want to feed your tests to a thread pool.
If I were to compare these enum values in a switch statement. What is the purpose of numbering the strings?
If I store the enum in a header file and then import the header file. I know that I can say events event = ls; (I can store ls into events)
But can I say something like event[0] to access ls?
#define NUM_EVENTS 11
typedef enum events {
ls = 0,
rs = 1,
ws = 2,
lo = 3,
ro = 4,
lc = 5,
rc = 6,
gru = 7,
grl = 8,
gll = 9,
glu = 10
} events;
There's no such thing as enum strings. What you're talking about are identifiers, only used in the source code. An enum in C is represented as an integer type, typically int, but compilers are free to chose e.g. short or another one. An enum is even allowed to hold a value that doesn't correspond to one of the named members. Only these numbers go in the compiled programs. C compilers do not (have to) store any type information in the compiled executable.
So, if you need the enum identifiers as strings at runtime, you have to store them yourself. This typically looks like e.g.
typedef enum events {
ls,
rs,
ws,
lo,
ro,
lc,
rc,
gru,
grl,
gll,
glu
} events;
static const char *event_names[] = {
"ls",
"rs",
"ws",
"lo",
"ro",
"lc",
"rc",
"gru",
"grl",
"gll",
"glu"
};
void example(event ev)
{
// might want to check for a valid value here ...
// print name of event
puts(event_names[ev]);
}
As a side note, there's no need to explicitly number the members in your case -- the default numbering starts at 0 and increases by one for each member.
Further hints:
enum member identifiers are global, so it's a good practice to make them unique, e.g. by prepending the type name like this:
typedef enum events {
event_ls,
event_rs,
// ...
} events;
The above code violates the DRY (don't repeat yourself) principle: when changing the enum, you have to touch two different locations in the code which is a source of potential bugs. This is sometimes solved using X macros. There are opposing opinions whether this is good or bad style, just presenting it here for completeness:
#define EVENTS_LIST \
X(ls) \
X(rs) \
X(ws) \
X(lo) \
X(ro) \
X(lc) \
X(rc) \
X(gru) \
X(grl) \
X(gll) \
X(glu)
#define X(name) event_##name ,
typedef enum events {
EVENTS_LIST
} events;
#undef X
#define X(name) #name ,
static const char *event_names[] = {
EVENTS_LIST
};
#undef X
after preprocessing, this produces
typedef enum events {
event_ls , event_rs , event_ws , event_lo , event_ro , event_lc , event_rc , event_gru , event_grl , event_gll , event_glu ,
} events;
static const char *event_names[] = {
"ls" , "rs" , "ws" , "lo" , "ro" , "lc" , "rc" , "gru" , "grl" , "gll" , "glu" ,
};
You can cast from an integer to an enum, which is equivalent to indexing if the enum is a dense sequence starting at zero. In general enums are just a way to introduce symbolic names for a list of related constants. It is syntactically better than doing so using #define but there is very little functionality beyond such. If the numbers are not explicitly assigned, the compiler will manage assigning unique values to each member. But even finding the max value is not something offered by the language, much less mapping to names as strings or similar.
I think you are asking how to get the name of the event as a string from the event so you can do something like printf("%s\n", SOMEFUNCTION(event)); to print the name of the event.
There's is no such function.
If you really want to use event[0] you can do this:
events event[11];
event[0] = ls;
event[1] = rs;
event[2] = ws;
event[3] = lo;
event[4] = ro;
event[5] = lc;
event[6] = rc;
event[7] = gru;
event[8] = grl;
event[9] = gll;
event[10] = glu;
I will say though, I am sure there is a way around doing this ( since it is not elegant at all ), if you give more context :)
EDIT: After re-reading your question, I get the impression you want the STRING "ls" from your enum?
If this is so, then no you are not able to do this. Enumerated types are great for switch statements, but if you are trying to turn a variable name into a string through code without hardcoding event[0] = "ls", it isn't going to happen.
I'm working in a condition like this:
typedef struct __type_x{
sub_type_a a;
sub_type_b b;
sub_type_c c;
}type_x_t;
uint32_t type_x_uids[] = {
1, //a's_uid
2, //b's_uid
3, //c's_uid
};
uint32_t type_x_uid_another[] = {
3, //a's uid in another API system
1, //b's uid in another API system
2, //c's uid in another API system
};
uint32_t type_x_uids_one_more[] = {
1, //a's_uid in yet another system
3, //b's_uid in yet another system
2, //c's_uid in yet another system
};
The problem is: If I need to add one more data(like d) into struct type_x_t, I need to add its uids into all other three arrays.
It is hard to maintain the code.
So I'm wondering if it is possible to maintain all these data from four places into one table?
I can put three uint32 arrays into one 2D array
uint32_t uid[system_id][value];
but I also have to maintain two pieces of code instead of one.
I'm wondering if there is anyway I can move forward? Therefore I can maintain one table to manage all these data. Such like
{#data_type, #data_name, #uid_1, #uid2, #uid 3},
Trying to use Macro to solve the problem, but I cannot access the data in certain position by __VA_ARGS__.
First of all, if you have placed the code adjacently in one single file, you are actually maintaining one place and not several. What you have is not necessarily bad. Anyway...
I will assume there's a reason why type_x_t is a struct and not an array. Then clearly the best design is to add the "uid" for "a" to the sub_type_a struct, or alternatively make a new struct containing both, since those data belong together.
If that's not an option, you could go with the array and then put that inside the type_x_t struct.
If that's not an option either, consider some completely different program design.
With all program design options exhausted, then - and only then - you could consider macros. What you are fishing for is so-called "X macros", which is the last resort and not really recommended, since they make the code much harder to read. The purpose of X macros is to centralize code maintenance of data to one place. It goes like this:
#include <stdint.h>
#define TYPE_X_LIST \
X(a,1,3,1) \
X(b,2,1,3) \
X(c,3,2,2)
typedef struct
{
#define X(name, dummmy1, dummy2, dummy3) sub_type_##name name;
TYPE_X_LIST
#undef X
} type_x_t;
uint32_t type_x_uids[] = {
#define X(dummy1, id, dummy2, dummy3) id,
TYPE_X_LIST
#undef X
};
uint32_t type_x_uid_another[] = {
#define X(dummy1, dummy2, id, dummy3) id,
TYPE_X_LIST
#undef X
};
uint32_t type_x_uids_one_more[] = {
#define X(dummy1, dummy2, dummy3, id) id,
TYPE_X_LIST
#undef X
};
Now you only have to change the "TYPE_X_LIST" when you need to change the data. If you want to keep track of how many data sets you have, you can add an enum to count them:
typedef enum
{
#define X(name, ...) something_##name,
TYPE_X_LIST
#undef X
TYPE_X_N // the number of data sets
} type_x_size_t;
Then TYPE_X_N could be used to set array sizes etc.
In a project I work on we have some utility functions which taking 2 arguments,one is address to write and second is value to write.
Lets use a simple example:
enum {
ADDR1 = 0x1000,
ADDR2 = 0x1500,
....
};
void Hardware_write_reg(ADDR,val)
{
switch ADDR {
case ADDR1 : abc.x = val; break;
case ADDR2 : abc.f = val; break;
.....
.....
}
This function "Hardware_write_reg" are called in many number of times in many files throughout the project with different ADDR and values as arguments.
Now,I need to remove the calls to "Hardware_write_reg" and write directly to struct members.
An example,Function1() below need to be changed
Before change:
Function1()
{
Hardware_write_reg(ADDR1,val1);
Hardware_write_reg(ADDR2,val4);
Hardware_write_reg(ADDR7,val6);
.....
}`
After change:
Function1()
{
abc.x = val1;
abc.f = val4;
abc.s = val6;
.....
}
I cannot do it manually,because many numerous calls to Hardware_write_reg() in multiple files.
I tried with C Macro,like this;but could not get expected result
#define Hardware_write_reg(ADDR1,A) do{\
abc.x = ((A));\
}while(0)
#define Hardware_write_reg(ADDR2,B) do{\
abc.f = ((B));\
}while(0)
Basically,I want to replace (at compile time)
1) Hardware_write_reg(ADDR1,val1); function call to abc.x = val1; with a macro
2) Hardware_write_reg(ADDR2,val2); function call to abc.f = val2; with a macro
3)....
Any help from C-Guru's would be appreciated!
You could define a bunch of macros which does
#define target_ADDR1 abc.x
#define target_ADDR2 abc.f
#define CONCAT2(a, b) a##b
#define Hardware_write_reg(ADDR,val) CONCAT2(target_,ADDR) = val
so that
Hardware_write_reg(ADDR1,42);
is replaced by
target_ADDR1 = 42;
and thus
abc.x = 42;
is what you get.
Disclaimer: I haven't tested it, but I am quite sure this should work.
Instead of programatically solving this you can try regular expression replacement which included in most of the text editors today. but here the problem is for each case in switch you have to modify regex for a bit. And you should not have any other Hardware_write_reg in your code.
search
Hardware_write_reg(ADDR1,val([0-9]+));
replace with
abc.x = val\1;
Also if you have many cases you can generate regex and replacement with a small script.
The only thing I could imagine that works for this case is you put the assignment operations into a switch statement
#define Hardware_write_reg(ADDR,A) do{\
switch((ADDR)) { \
case ADDR1: abc.x = ((A));\
case ADDR2: abc.f = ((A));\
...
} \
} while(0)
As the C Macros cannot guess the values of ADDR during compile time and hence cannot put appropriate structure member for each ADDR.
As you want replace the function call with the actual assignment statement. So it seems like the only way you can do it is to Find/Replace in the code. For this you can write a regular expression and find a replace using that. For instance for ADDR1:
Find the Regular Expression:
(Hardware_write_reg)[(](ADDR1)[,]([\w]*)[)];
And Replace it with:
abc.x = $3;
The above regular expressions are compatible in eclipse