Value lookup table in C by strings? - c

If I have a set of small string values, and I want to fetch a numeric value to represent them, what's the best way to do this via a lookup table?
If I were only needing to do a straight look up, I know the optimal solution would just be a series of if statements:
if (strcmp(str, "foo") == 0)
tmp = FOO;
else if (strcmp(str, "bar") == 0)
tmp = BAR;
But, I ask this because these small string values represent an attribute in a small project I'm writing in C, and the attributes can be read-only or read-write (no write-only for now, maybe never).
So what I currently do just to make sure things work is have a lookup function comprised of an if-then clause like above to look up which values are read-only, and a second functions that looks up which values are read-write. But this is large and ugly to me.
I'm thinking, have three functions instead. One function is the lookup function, and it returns an int value that is the numeric form of the string. But this lookup function can also take a flag that determines whether it fetches a read-only value, or a read-write value. If a write operation is done on a value that is really read-only, the function will return -EINVAL (or something equivalent).
The other two functions, now still the read and write, just call this lookup function, passing in a string of the value, and the flag that determines whether they're for reading or writing.
Thing is, I don't know how this is modeled in C (if it can be modeled), and searching Google is tiresome with all the content farms ripping this place off (and giving me C++/C# answers instead).
So this is how I think it'll look:
int lookup_func(const char *name, const char *flag) {
int tmpval = 0;
/* code to do the lookup. */
if (tmpval == 0)
return -EINVAL;
else
return tmpval;
}
int get_readonly_bit(const char *name) {
return lookup_func(name, "ro");
}
int get_readwrite_bit(const char *name) {
return lookup_func(name, "rw")
}
Thoughts? The idea is to reduce code size by not repeating the if-then branches for these two functions, which differ slightly in overall design, and simply let some kind of a lookup function figure out what function this value serves.

Do you not consider just putting a table in? A hash table is also fine if there are lots of properties.
int lookup(const char *name)
{
typedef struct item_t { const char *name; int writable; int value; } item_t;
item_t table[] = {
{ "foo", 0, FOO },
{ "bar", 1, BAR },
{ NULL, 0, 0 }
};
for (item_t *p = table; p->name != NULL; ++p) {
if (strcmp(p->name, prop_name) == 0) {
return p->value;
}
}
return -EINVAL;
}

Related

Comparing two instances of a struct

I have my own data type since C89 doesn't allow bool type. I can't use C99 sadly.
I have tested the below code and my program is not even recognizing that the names are different. The names are working just fine. I have those loaded in an instance of the struct.
struct ROOM roomList [MAX_ROOMS_COUNT];
I used memset and strcpy to copy in a list of rooms from a room array. Testing this gives me the results I want like so...
printf("%s", roomList[1].name)
printf("%s", roomList[2].name)
...
printf("%s", roomList[7].name)
The rooms array has 7 values.
#define MAX_ROOMS_COUNT 7
typedef enum {
true = 1,
false = 0
} boolean;
struct ROOM {
char name[8];
char* type;
int numConnections;
char* connections [MAX_CONNECTIONS_COUNT];
};
boolean isSameRoom(struct ROOM x, struct ROOM y) {
//printf("Is Same Room, 1:%s, 2:%s\n", x.name, y.name);
if (x.name == y.name) {
//printf("ROOM IS SAME!");
return true;
}
else {
return false;
}
}
struct ROOM getRandomRoom() {
int random = rand() % MAX_ROOMS_COUNT;
return roomList[random];
}
void addRandomConnection() {
struct ROOM A;
struct ROOM B;
A = getRandomRoom();
do {
B = getRandomRoom();
}while(isSameRoom(A, B) == true);
//printf("Add Random Connection, 1:%s, 2:%s\n", A.name, B.name); //for testing purposes
}
Using the print statements I can see that the isSameRoom function isn't working. It's generating random rooms just fine, but there are instances when I run the program that I'm getting the same room for A and B. Any idea why? Thanks! :)
First of all, you're using pass-by-value for these structures, which is pretty inefficient. You should do pass by reference.
Second, as others mentioned, you cannot compare fixed-size strings with == in C; you have to use strncmp or some other structure.
Perhaps a better idea would be to do the whole comparison of the whole structure with memcmp?
return memcmp(&A, &B, sizeof(struct ROOM)) == 0;

How to preserve pointers between multiple method calls in C

I'm writing a parser for propositional logic (doesn't matter what that is, main point is I'm parsing a simple language) and initially started out with functions of the following form:
int formula() {
int store = step;
if(compound())
return TRUE;
else {
if(atom())
return TRUE;
else if(negation() && formula())
return TRUE;
else {
step = store;
return FALSE;
}
}
}
int compound() {
int store = step;
if(open() && formula() && binary_operator() && formula() && close())
return TRUE;
else {
step = store;
return FALSE;
}
}
The functions above not mentioned are base cases - these are the important parts. Formulas can have sub-formulas, and these sub-formulas in turn can be compound formulas, which contain sub-formulas, and so on.
Instead of ints though, I'm trying to return char sequences of 1s and 0s (true and false). If you return a sequence, it means that the input can generate a sequence (it must be valid). Otherwise, return null.
The issue is that every time I've tried the pointers keep getting lost - I understand this is to do with the stack(?) and the pointer sort of 'dies' when the function returns whatever. I've not tried arrays because I have been told that arrays work best statically, whereas the size of these arrays would be dynamic (size is determined by number of variables, which is only found at runtime).
Is there any way this approach can be done? I can't malloc anything because I won't be able to free it - the sequence of 1s and 0s needs to be returned before I'd be able to free it. Maybe pass structs with a sequence field, although I'm not sure if that suffers from the same issue.
Any help much appreciated. This is a program using C99. Any advice on clarifications welcome!
I'm not entirely following what you want to do, but there is not a clear reason why you couldn't use malloc. The pointer returned by malloc can be freed by another function later. Consider the following valid code:
char* foo(size_t* length)
{
*length = 3;
char* seq = malloc(*length);
seq[0] = 1;
seq[1] = 0;
seq[2] = 1;
return seq;
}
int main()
{
size_t length;
char* seq = foo(&length);
/* use seq */
free(seq);
}
You can also do it without malloc if you know an upper bound for your sequence. By passing a pointer to space you allocated on the stack from main(), you won't lose the data when the function exits:
void foo(char* seq, size_t total_size, size_t* used_size)
{
*used_size = 3;
seq[0] = 1;
seq[1] = 0;
seq[2] = 1;
}
int main()
{
size_t used_size;
char seq[100];
foo(seq, sizeof(seq), &used_size);
/* use seq */
}

Quick check to see if structure of function pointers is NULL in C99

In C99 is there an easier way of check if a structure of function pointers is NULL, other than checking each individual pointer?
What I currently have is similar to the following:
typedef struct {
void* (*foo)(int a);
int (*bar)(int a, int b);
} funcs;
void *funcs_dll;
funcs_dll = dlopen("my_funcs_dll.so", RTLD_GLOBAL);
if (funcs_dll == NULL) {
THROW_ERROR;
}
funs.foo = dlsym(funcs_dll, "foo");
funcs.bar = dlsym(funcs_dll, "bar");
if (!funcs.foo || !funcs.bar) {
THROW_ERROR;
}
What I am looking to do is reduce the second if check, so that I do not need to check each individual function. Any suggestions would be helpful.
Not directly, no.
You can't use memcmp() to compare to some constant buffer, since there might be padding inside the structure which will have "random" values. If you can make sure that the size of the structure is exactly the sum of the function pointer fields, you can perhaps go that way.
You can also use a proxy, by i.e. declaring an initial uint32_t member that is a bitset representing which function pointer(s) are valid. Then you can check up to 32 (or 64 with uint64_t) proxy bits in parallel.
If you only want to do this once, my suggestion would be a data-driven approach. Define a table of function names to look for, and process that in a loop, exiting as soon as a dlsym() call fails.
Something like:
const struct {
const char *name;
size_t offset;
} functions[] = {
{ "foo", offsetof(funcs, foo) },
{ "bar", offsetof(funcs, bar) },
};
Data-driven code like this is very powerful, and often very fast.
Make wrapper function for dlsym which will set error flag, if return value is NULL.

How can I return if error - C

Basically I have this data structure:
typedef struct Data {
char *str;
int n;
} TData, *AData;
Where I want to allocate space for str using malloc (in that variable I am putting a string that I read from a file which, honestly, it's not that important considering the topic of my question). Therefore, I created a function TData InitData(const char *file_name):
TData InitData(const char *file_name) {
TData data;
data.str = malloc(1024);
if (!data.str) {
printf("Allocation error!\n");
// I have no idea what to return here in order to exit the function.
return (...);
}
return data;
}
Now, in my 'main()' function, I would make the call something like this:
int main(int argc, const char * argv[]) {
TData data;
if(!(data = ReadExpression(argv[1]))) {
return 1;
}
// I do something with my data and the program ends.
return 0;
}
How can I make the return statement valid in the InitData function?
I know that the other way, and probably the recommended way is either to send TData data from main to InitData as a parameter of type AData and change the type of the function to int (would result into something like: int InitData(const char *file_name, AData data)) or to simply use AData.
As a rule of thumb, always pass structs as pointer parameters and not by value.
As a rule of thumb, when writing complex functions, reserve the return value for errors.
So in your case, rewrite the function as:
bool InitData (TData* data, const char *file_name);
where the bool can be replaced by an enum for more detailed error codes. Right now the function result is only "ok" or "error".
C does not have an inbuilt multiple value return, so usually what you do is
int InitData(const char *file_name, TData *returnedData) { ... }
The return value of InitData is then the error/success flag and the TData structure is returned via the pointer.
i did not do much in C but many functions are like this:
int foo(intargs, outargs);
the thing behind this is that you allocate the object and pass it as a reference to be filled inside the function.
the return value of the function will be an enum or something like this to be compared to determine what happened.
I love the way MicroVirus suggested. Accepting the data as a parameter also be used for multithreaded code safety. Moreover, you can return the result of operation in order to check in the calling code. Your TData is not a pointer so you can not set it to NULL, which is a one way of telling an invalid case I think. However, there is another problem here! TData data in InitData is a local variable and should be copied when the function returns. It is not a good practice for performance issues I think. I suggest, accept the object as a parameter (as a pointer for example) return the result of the function to the calling code and check that for the validity of the data. You can set data NULL or not, but when you return a result value this makes everything cleae without checking the data object itself.
Just return data as normal, but change your code in main() to read:
data = ReadExpression(argv[1]);
if ( !data.str ) {
return 1;
}
You can't return a scalar value in place of a struct, and your question suggests you already know you can use a pointer but are choosing not to, so this is the simplest alternative way.
You could have a flag in the Data-struct set depending on the if (!data.str) result, and always return data.
Then check the flag whenever you want to know what happened.
Just to clarify:
You could write something like this, or similar:
`typedef struct Data
{
char *str;
int n;
char myflag;
const char *file_name;
} TData, *AData;`
`void InitData(TData *data)
{
data.str = malloc(1024);
if (!data.str)
{
printf("Allocation error!\n");
myflag=0;
}
else
{
myflag=1;
}
return data;
}`
But see the comments also. Just wanted to give another perspective.

idiomatic C for an int to const string map

How can I express in C a map like this one?
{
{1, "One"},
{1000, "One thousand"},
{1000000, "One million"}
}
The key is an int and can be a big int, the value is a constant string and it is known at compile time.
The map will contain some 20 or 30 elements.
I would write this function:
const char* numbers( const int i )
{
switch( i ) {
case 1: return "One";
case 1000: return "One thousand";
case 1000000: return "One million";
default: return "";
}
}
is there any better (more idiomatic) way of doing it?
Using a switch is entirely idiomatic C, there's a separate style consideration (that would apply in pretty much any language) whether you want to separate the key/value data out of the program logic.
You could use an array of const struct { int key; const char *value; };, but then you'll start worrying about whether you should use a linear search, binary search, perfect hash, etc. With a switch, the compiler takes it out of your hands.
If you have some kind of associative container (tree or hashmap) that you use for other things in this project then you could use that, but it's not really worth writing one for a 30-item collection.
Just like you did:
typedef struct {
long long key;
char *val;
} Item;
const Item mydict[] = {
{1, "One"},
{1000, "One thousand"},
{1000000, "One million"}
};
I leave as an exercise the body of numbers() function.
The alternate solution suggested by Steve Jessop is perfectly valid as well.
If all is statically known, I would go with something like this:
char strings[][] = {"One", "One thousand", "One million", /* whatever */};
int map[] = {1, 1000, 1000000};
const char *numbers(const int i)
{
position = search(map, i);// if the array is too small, just linear search
// if it is big, you can binary search
// if there is a relation use a formula
// although a formula is not extendable.
// In this case, it is log(i) in base 1000.
return strings[position];
}
This method can be extended for non-static data. You just have to make sure you correctly fill the strings array and keep the map sorted.
Note: Obviously, you should add sufficient error-checking etc. For example in the case where search couldn't find i.
This could be one solution.
Basically create sorted array of key-value pairs and then just use bsearch-function (performs binary search) to quickly find the correct value. It might make sense to implement your own binary search to make searching more convinient.
#include <stdlib.h>
#include <stdio.h>
typedef struct mapping
{
int key;
char* value;
} Mapping;
int mappingCompare(const void* a, const void* b)
{
const Mapping* first = (const Mapping*)a;
const Mapping* second = (const Mapping*)b;
return second->key - first->key;
}
int main()
{
Mapping map[3];
Mapping search;
Mapping* result;
map[0].key = 1;
map[0].value = "One";
map[1].key = 1000;
map[1].value = "One thousand";
map[2].key = 1000000;
map[2].value = "One million";
//qsort is only needed if the map is not already in order
qsort(map, 3, sizeof(Mapping), mappingCompare);
search.key = 1000;
result = (Mapping*)bsearch(&search, map, 3, sizeof(Mapping), mappingCompare);
printf("value for key 1000 is %s\n", result->value);
}

Resources