Reputation:
I am creating a C99 program and I'm needing some help to get out of a little redundancy of my code. I want to transform the following code into two calls of a function, but I don't know how to do it.
I want to read two files (each one has 15 entrances) and put their contents into two arrays. The problem is that these arrays are of a different data type.
Here is what I have:
typedef char string[30];
int
_vInt[15];
string
_vString[15];
FILE
*_fInt,
*_fString;
int main(){
...
for(int i = 0; !feof(_fInt) && i < 15; ++i){
fscanf(_fInt, "%d", &_vInt[i]);
...
}
for(int i = 0; !feof(_fString) && i < 15; ++i){
fscanf(_fString, "%s", _vString[i]);
...
}
...
}
So, I don't want to use this for
twice. I'd prefer to call a function twice instead:
readFile(_fInt, "%d", _vInt);
readFile(_fString, "%s", _vString);
The problem is that I don't know how the prototype of the function should be and neither how I should use it.
Yes, It's fine for me to use ugly void *
solutions...
Upvotes: 3
Views: 143
Reputation: 1371
Another solution would be to use enumerated type and table in your readFile() function.
#include <stdlib.h>
#include <stdio.h>
// Put in Header file
typedef char string[30];
typedef enum {
ITInt = 0,
ITString = 1,
ITMax
} InputTypes;
// Should be in module with readFile()
struct inputTypeEntry_s{
const char *format;
size_t sz;
};
struct inputTypeEntry_s inputTypeLUT[ITMax] = {
{ "%d", sizeof(int) }, // ITInt
{ "%s", sizeof(string) }, // ITString
};
int readFile(FILE *fp, InputTypes type, void *data) {
char *ptr = (char *)data;
for(int i = 0; !feof(fp) && i < 15; ++i){
fscanf(fp, inputTypeLUT[type].format, ptr + (i * inputTypeLUT[type].sz) );
}
return 0;
}
Upvotes: 1
Reputation: 224944
Since you mentioned you can use C11 features, here's an example that uses C11 type-generic expressions. I used it to remove the requirement for passing the format string, too:
#include <stdio.h>
typedef char string[30];
#define generic_arg(X) _Generic(X, string: X, \
default: &X)
#define generic_format(X) _Generic(X, string: "%s", \
int: "%d")
#define readFile(file, variable) for (int i = 0; i < 15; i++) \
{ \
fscanf(file, \
generic_format(variable[i]), \
generic_arg(variable[i])); \
}
int main(void)
{
int _vInt[15];
string _vString[15];
FILE *_fInt = fopen("ints", "r");
FILE *_fString = fopen("strings", "r");
readFile(_fInt, _vInt);
readFile(_fString, _vString);
for (int i = 0; i < 15; i++)
{
printf("%d %s\n", _vInt[i], _vString[i]);
}
fclose(_fInt);
fclose(_fString);
return 0;
}
I'm not sure if this is a really great example - it's certainly more complicated than your two-for-loop solution, but I think it meets your requirements.
Upvotes: 0
Reputation: 1277
You can extract out the type to a (void *)
, but it's not going to be pretty.
void foo(FILE *file, const char *format, void *arr, size_t size) {
for(int i = 0; !feof(file) && i < 15; ++i){
fscanf(file, format, ((char *)arr) + (size * i));
...
}
}
The function call would look like
foo(_fInt, "%d", (void *)&_vInt, sizeof(int))
Upvotes: 0
Reputation: 19153
You can use a macro.
#define readFile(file, fmt, arr) \
for (int i = 0; !feof(file) && i < 15; ++i) { \
fscanf(file, fmt, arr[i]); \
}
readFile(_fInt, "%d", &_vInt);
readFile(_fString, "%s", _vString);
Upvotes: 1