I have a huge struct array and I created different function to get the struct pointer based on a member's value:
typedef struct {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} test_t;
test_t test[10]; // Initialized somewhere else
static test_t* __get_struct_by_a(uint_32_t a) {
for (uint32_t i = 0; i < 10; i++) {
if (test[i].a == a)
return &test[i];
}
return NULL;
}
static test_t* __get_struct_by_b(uint_32_t b) {
...
}
Is there an easy way to tackle this in C instead of create a lookup function for every member?
Following is one way to write a general function to get (find) your first matching struct member.
I tried to keep consistent with your sample code, adding a few #include directives that may be necessary, and a typical fixed-array size #define for TEST_SIZE so that you don't use hard-coded values for the loop indices.
#include <stddef.h>
#include <stdio.h>
#include <string.h>
typedef struct {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} test_t;
test_t test[10]; // Initialized somewhere else
#define TEST_SIZE (sizeof(test)/sizeof(test[0]))
static test_t* __get_struct_by_offset(const void* value_ptr, const size_t offset, const size_t field_size)
{
for (uint32_t i = 0; i < TEST_SIZE; i++)
{
if (0 == memcmp(value_ptr, ((unsigned char*)&test[i])+offset, field_size))
{
return &test[i];
}
}
return NULL;
}
You would use it like this:
uint32_t a_value_to_find = 5; /* example field "a" value to find */
uint32_t b_value_to_find = 10; /* example field "b" value to find */
test_t* test_ptr;
/* find specified value for field "a" */
test_ptr = __get_struct_by_offset(&a_value_to_find, offsetof(test_t, a), sizeof(a_value_to_find));
/* find specified value for field "b" */
test_ptr = __get_struct_by_offset(&b_value_to_find, offsetof(test_t, b), sizeof(b_value_to_find));
It's your responsibility to ensure that the data types for *value_ptr and the field at the specified offset are identical, and therefore of the same size (field_size).
To simplify the usage, you could write some macros as shorthand for these calls. For example:
#define GET_A(value) __get_struct_by_offset(&value, offsetof(test_t, a), sizeof(value))
#define GET_B(value) __get_struct_by_offset(&value, offsetof(test_t, b), sizeof(value))
The queries for "a" and "b" are then simplified to:
/* find specified value for field "a" */
test_ptr = GET_A(a_value_to_find);
/* find specified value for field "b" */
test_ptr = GET_B(b_value_to_find);
Related
I have several data structures which all contain a same data structure inside
struct numbers{
int num_a;
int num_b;
};
struct var_a{
numbers num;
//other variables
};
struct var_b{
numbers num;
//other variables
};
Now I wants to write a single function to access struct numbers in all these structures(use +1 in my example below for demonstration purpose).
One way is to use void pointer input and then cast to struct number inside the function:
void foo(void* p_input)
{
((numbers*)p_input)->num.num_a += 1;
}
int main()
{
var_a a;
var_b b;
foo(&a);
foo(&b);
return 0;
}
But this method requires struct numbers put at the top of structure var_a/var_b/var_c.
Another way is to use a macro instead of a function:
#define foo(type_t, input) \
do{ \
((type_t*)input)->num.num_a += 1; \
while(0)
int main()
{
var_a a;
var_b b;
foo(typeof(a), &a);
foo(typeof(a), &b);
return 0;
}
But since my actual operation is much more complex than num_a += 1, I prefer not to use a macro.
Is there any way I can use a function to achieve my requirement?
Below I have pseudo-code which uses typeof as the function input but I suppose it is not valid in C.
void foo(void* p_input, type_t input_type)
{
((input_type*)p_input)->num.num_a += 1;
}
int main()
{
var_a a;
var_b b;
foo(a, typeof(a));
foo(b, typeof(b));
return 0;
}
If when passing to functions we treat parameters uint32_t data[] and uint32_t* data as both being pointers to an array of data, why is the same not true when setting a member of a struct? Quick example:
#include <stdint.h> /* uint32_t */
#include <string.h> /* memcpy */
#include <stdlib.h> /* malloc */
typedef struct {
uint32_t data[1024];
} tData;
typedef struct {
uint32_t *data;
} pData;
int main()
{
// structs
pData pdata;
tData sdata;
// Test 1.
///////////////////////////////////
uint32_t mydata[1024];
// This is OK
pdata.data = mydata;
// This is not OK
sdata.data = mydata;
// But this is OK
memcpy(sdata.data, mydata, 1024);
// Test 2.
///////////////////////////////////
uint32_t *otherdata = malloc(sizeof(uint32_t)*1024);
// This is OK
pdata.data = otherdata;
// This is not OK
sdata.data = otherdata;
// But this is OK
memcpy(sdata.data, otherdata, 1024);
}
In order to satisfy the struct tData must I do a memcpy? Is this the only way?
It looks like an attempt to assign an array directly, something like this (without structs)
int A[10];
int B[10];
int *ptr;
ptr = A; // OK
A = B; // Not ok
assigning array to pointer will make the pointer to point to the first element of the array, while comparing 2 arrays directly is not allowed.
The example with memcpy is equal to copying elements(their memory) one by one. so you can also do something like this.
for(int i = 0; i < 1024; ++i)
sdata.data[i] = mydata[i];
I have many similar function calls dealing with one structure, but each call is using different field of structure.
Example:
typedef struct {
int i1;
int i2;
int i3;
} S;
functions to get structure fields (it would be better to avoid them):
int getFieldI1 (S *s){ return s->i1; }
int getFieldI2 (S *s){ return s->i2; }
int getFieldI3 (S *s){ return s->i3; }
function i have to call many times:
void doJob (int (*get_field_func)(S *)){
//some code
S s;
int v = get_field_func(&s);
//some code
}
i call doJob() this way:
doJob(&getFieldI1);
doJob(&getFieldI2);
doJob(&getFieldI3);
i would like to do like this:
doJob(i1);
doJob(i2);
doJob(i3);
is it possible in C?
option 1 - offsets
You can use memory offsets.
void doJob (int offset){
//some code
S s;
int v = *(&s+offset*sizeof(int));
//some code
}
You can call it like this:
doJob(0);//i1
doJob(1);//i2
doJob(2);//i3
As pointed out in the comments, the offsets are unsafe. You can create a check for this:
if(offset>2||offset<0){
//some kind of error
}
Also, this can only be used if the structure only contains integers(or elements of the same type, you would need to adjust it)(see comments).
If there are elements before s1, s2 and s3, you'll need to add the size of the elements(as padding, just add it);
option 2 - constants
Another option (that hasn't the mentioned problems) is to define constants/macros:
You'll just define them like this:
#define I1 &getFieldI1
#define I2 &getFieldI2
#define I3 &getFieldI3
and just call it using:
doJob(I1);
doJob(I2);
doJob(I3);
Just pass in a pointer to the field:
void doJob( int* fieldPointer )
{
assert( fieldPointer != NULL );
// Get the field value:
int v = *fieldPointer;
// Do something with the field value:
v += 10;
// Save the updated value back to the field:
*fieldPointer = v;
}
Usage:
S structInstance = ...
doJob( &structInstance.i1 );
doJob( &structInstance.i2 );
doJob( &structInstance.i3 );
How to pass structure field name to function?
In general, you cannot. A typical library coded in C does not show fields of internal struct to outside. In other words, a field name is only known to the compiler, and relevant to the current translation unit, and makes no sense at runtime.
Consider the following metaprogramming approach: write a metaprogram (in C or in some scripting language like Guile, awk, Python, etc...) generating your C code, and set up your build accordingly. That might mean to edit your Makefile, or configure your build automation tool.
This is usual practice since the previous century. Look into SWIG or RPCGEN as a famous example.
You might perhaps use preprocessor tricks, e.g. X-macros.
Unfortunately, C doesn't allow exactly what you need. But you can achieve a partial win with some code changes.
I have one and half solutions. For the first I propose a (simplified!) implementation, for the second I provide just an hint. Please, check if they can be acceptable for you.
Your example structure:
typedef struct {
int i1;
int i2;
int i3;
} S;
I would define an enum representing the specific field:
typedef enum
{
FIELD_ID_I1,
FIELD_ID_I2,
FIELD_ID_I3,
FIELD_ID_MAX
} FieldId_e;
Then I would add a field parameter in your general function, managing internally the correct field to be returned. Some smart error managing in case of wrong ID has to be done here. I just return -1 for brevity.
int getField (S *s, FieldId id)
{
int ret = -1;
switch(id)
{
case FIELD_ID_I1:
ret = s->i1;
break;
case FIELD_ID_I2:
ret = s->i2;
break;
case FIELD_ID_I3:
ret = s->i3;
break;
}
return ret;
}
Your doJob will become
void doJob (int (*get_field_func)(S *, FieldId), FieldId id){
//some code
S s;
int v = get_field_func(&s, id);
//some code
}
And final call will become this one. But probably (and it depends on your scenario) having a single general function will make possible to omit the function pointer, simplifying much the interface.
doJob(&getField, FIELD_ID_I1);
doJob(&getField, FIELD_ID_I2);
doJob(&getField, FIELD_ID_I3);
Just a short reference to another tricky solution that would require to play with pointers.
Do you know offsetof macro? (Wikipedia EN)
It evaluates to the offset (in bytes) of a given member within a
struct or union type, an expression of type size_t. The offsetof()
macro takes two parameters, the first being a structure name, and the
second being the name of a member within the structure.
In this case you could have something like
int getField (S *s, size_t offset);
doJob(&getField, offsetof(S, i1));
I failed to guess right types for i1/i2/i3, sorry. So I use auto keyword from c++:
#include <stdio.h>
typedef struct {
int i1;
int i2;
int i3;
} S;
int getFieldI1 (S *s){ return s->i1; }
int getFieldI2 (S *s){ return s->i2; }
int getFieldI3 (S *s){ return s->i3; }
void doJob (int (*get_field_func)(S *)){
//some code
S s = {1,2,3};
//S s;
int v = get_field_func(&s);
//some code
printf("got: %d\n", v);
}
int main() {
S s = {1,2,3};
auto i1 = getFieldI1;
auto i2 = getFieldI2;
auto i3 = getFieldI3;
doJob(i1);
doJob(i2);
doJob(i3);
}
Then
g++ 59503102.cxx -o 59503102 && ./59503102
as expected produces
got: 1
got: 2
got: 3
plain c version
#include <stdio.h>
typedef struct {
int i1;
int i2;
int i3;
} S;
int getFieldI1 (S *s){ return s->i1; }
int getFieldI2 (S *s){ return s->i2; }
int getFieldI3 (S *s){ return s->i3; }
void doJob (int (*get_field_func)(S *)){
//some code
S s = {1,2,3};
//S s;
int v = get_field_func(&s);
//some code
printf("got: %d\n", v);
}
int main() {
S s = {1,2,3};
int (*i1)(S *) = getFieldI1;
int (*i2)(S *) = getFieldI2;
int (*i3)(S *) = getFieldI3;
doJob(i1);
doJob(i2);
doJob(i3);
}
I'm getting this error:
list.c list.h types.h
list.c: In function 'List_push':
list.c:11:23: error: invalid initializer
--- void *values[len] = ls->values);
EDIT:
Now, with my current code (I've done undos/redos that removed somethings), I get this error instead:
Why?
Basically I've a List structure which declares an variable-length array, containing void pointers (what I want is pointers to any data type). You can see it below at the list.h file.
I've tried a mix of changes in list.c (i.e., *values[pos++] = ..., etc...), but doing these changes it only results in worse gcc errors.
wscom.c
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
#include "types.h"
int main() {
List ls;
// TEST: Put a value pointer at index 0
uint8 value = 0x41;
List_push(&ls, 1, &value);
printf("%c",
*(char*) List_getindex(&ls, 0)
);
return 0;
}
types.h
#ifndef hydroTrackerTypesH
#define hydroTrackerTypesH
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned long long uint32;
#endif
list.h (Declarations)
#ifndef hydroTrackerListH
#define hydroTrackerListH
#include "types.h"
typedef struct {
uint32 length;
void *values[];
} List;
void List_push(List *ls, uint8 count, ...);
void *List_getindex(List *ls, uint32 i);
void List_setindex(List *ls, uint32 i, void *v);
#endif
list.c (Defns.)
#include "list.h"
#include "types.h"
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
static size_t PointerSize =
sizeof(void*);
void List_push(List *ls, uint8 count, ...) {
uint32 len = ls->length;
void *values[len] = ls->values;
uint32 sum = len + count;
realloc(&values, sum * PointerSize);
ls->length = sum;
va_list newVals;
va_start(newVals, count);
uint8 pos = len;
while(count--)
values[pos++] = va_arg(newVals, void*);
va_end(newVals);
}
void *List_getindex(List *ls, uint32 i) {
return (void *)(ls->values[i]);
}
//void List_setindex(List *ls, uint32 i, void *v);
This is a little bit long for a comment.
Thus, I make it an answer.
I try to show you how pointers and arrays are related to each other:
#include <stdlib.h>
#include <stdio.h>
int main()
{
/* The compiler allocates space for "Hello" and '\0' (5 + 1 chars)
* and stores the address in aString1.
*/
const char *aString1 = "Hello";
/* The compiler allocates 10 chars and initializes
* it with "World" (and the '\0' for terminator).
*/
const char aString2[10] = "World";
/* The compiler determines length of initializer "I'm here."
* (9 + 1) and allocates the array of appropriate size.
*/
const char aString3[] = "I'm here.";
/* allocate storage for array (3 const char*) */
#if 0 /* the usual way */
const char **array = malloc(3 * sizeof (const char*));
#else /* how Matheus wants to do it */
const char **array = NULL;
array = realloc(array, 3 * sizeof (const char*));
#endif /* 0 */
/* assign contents (using it like an array) */
array[0] = aString1;
array[1] = aString2;
array[2] = aString3;
/* apply array to another variable array2 */
const char **array2 = array; /* assigns the address only */
/* use it: */
printf("array2[0]: '%s', array2[1]: '%s', array2[2]: '%s'\n",
array2[0], array2[1], array2[2]);
/* throw away storage of array (and array2) */
free(array);
/* Attention! array, array2 become wild pointers at this point
* and may not be accessed (except new, valid addresses are assigned).
* However, aString1, aString2, aString3 are still intact.
*/
printf("aString1: '%s', aString2: '%s', aString3: '%s'\n",
aString1, aString2, aString3);
/* done */
return 0;
}
The sample can be tested on ideone.com.
The sample output is:
array2[0]: 'Hello', array2[1]: 'World', array2[2]: 'I'm here.'
aString1: 'Hello', aString2: 'World', aString3: 'I'm here.'
Update:
So, I finally looked again on to the question & answer of Matheus and tried to fix it according to his intention (or how I understood it). I based it on Matheus' implementation and remarked modified codes by comments:
list.h:
#ifndef LIST_H
#define LIST_H
#if 0 /* not necessary to define these types */
#include "types.h"
#else /* they are already available in a (better) portable manner: */
#include <stdint.h>
/* Btw. I had to change:
* uint8 -> uint8_t
* uint32 -> uint32_t
*/
#endif /* 0 */
typedef struct {
uint32_t length;
#if 0 /* gcc ERROR: */
/* list.c:17:3: error: invalid use of flexible array member
* ls->values = NULL;
*/
void *values[];
#else /* (not) 0 */
void **values;
#endif /* 0 */
} List;
void List_init(List *ls);
void List_push(List *ls, uint8_t count, ...);
void* List_getindex(List *ls, uint32_t i);
void List_setindex(List *ls, uint32_t i, void *v);
#endif /* LIST_H */
list.c:
#include "list.h"
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#if 0 /* no need for a variable (with storage */
static size_t PointerSize = sizeof(void*);
#else /* use enum instead (constant) */
enum { PointerSize = sizeof(void*) };
#endif /* 0 */
void List_init(List *ls)
{
ls->length = 0;
/* This is important: */
ls->values = NULL;
/* or 1st realloc() in List_push() may have Undefined Behavior.) */
}
void List_push(List *ls, uint8_t count, ...)
{
uint32_t len = ls->length;
uint32_t sum = len + count;
void **values = realloc(ls->values, sum * PointerSize);
if (!values) {
/* realloc() failed! Bail out before destroying the existing data. */
return;
}
ls->length = sum;
ls->values = values;
/* assign new contents */
va_list newVals;
va_start(newVals, count);
#if 1 /* the readable way: */
int pos = len;
while (count--) values[pos++] = va_arg(newVals, void*);
#else /* the hackish C style way: */
values += len;
while (count--) *values++ = va_arg(newVals, void*);
#endif /* 1 */
va_end(newVals);
}
void* List_getindex(List *ls, uint32_t i)
{
return ls->values[i];
}
wscom.c:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "list.h"
int main()
{
List ls;
/* Put a value pointers at indices 0, 1, 2 */
uint8_t value1 = 0x41, value2 = 0x42;
uint8_t value3[3] = { 0x43, 0x44, 0x45 };
List_init(&ls);
List_push(&ls, 3, &value1, &value2, value3);
/* Check whether list contents can be retrieved again */
if ((*(uint8_t*)List_getindex(&ls, 0)) == 0x41) {
printf("List entry 0 is correct.\n");
}
if ((*(uint8_t*)List_getindex(&ls, 1)) == 0x42) {
printf("List entry 1 is correct.\n");
}
{ uint8_t *values = List_getindex(&ls, 2);
if (values[0] == 0x43
&& values[1] == 0x44
&& values[2] == 0x45) {
printf("List entry 2 is correct.\n");
}
}
/* Done. */
return 0;
}
In one of my comments, I stated that void *values[]; in struct List might be OK. Ahem, I was wrong. gcc remarks this as error when I tried to use it in list.c. So, actually, it is OK but not for what I intend it to use.
Finally, my sample session (using gcc in cygwin on Windows 10):
$ gcc -std=c11 -o wscom wscom.c list.c
$ ./wscom
List entry 0 is correct.
List entry 1 is correct.
List entry 2 is correct.
$
2nd Update:
(I believe) I realized the missing piece of Matheus (considering his Javascript background):
There are no dynamic arrays in C (in opposition to Javascript).
Instead, there are arrays with variable size which may be used only in specific situations:
In C:
Definition of arrays with variable size in global variables is prohibited. (The compiler needs to know how many bytes to allocate for storage.) This does not exclude something like e.g.
int array[] = { 1, 2, 3 };
because the compiler determines the size from the initializer (on the right hand side of =).
Declaration of global arrays without explicit size is possible. (The definition with proper size might/must be done somewhere else. The linker will fail if no proper storage definition can be found.)
A local variable (inside a function, storage class auto but not static or extern) might be declared as array with size determined at runtime (from a variable). This feature was introduced in C99 but not (yet) in C++ (at least not until C++11 incl.)
A function parameter might be declared as array with unknown (or any) size. (This is equal to declaring it as a pointer.)
I found a nice answer about this in SO: Dynamic array allocation on stack in C (which I used to prove my own statements above).
The only supported way to have "dynamic arrays" in C is the usage of the standard library functions malloc()/realloc()/free(). However this is better called "dynamic memory" allocation because this applies to any C type (not only arrays).
Disclaimer:
I apologize if I wrote something rubbish about Javascript. I'm the total newbie in Javascript with very less practical experience...
Suppose I have this struct (which incidentally contain bit-fields, but you shouldn't care):
struct Element {
unsigned int a1 : 1;
unsigned int a2 : 1;
...
unsigned int an : 1;
};
and I want to access the i'th member in a convenient way. Let's examine a retrieval solution.
I came up with this function:
int getval(struct Element *ep, int n)
{
int val;
switch(n) {
case 1: val = ep->a1; break;
case 2: val = ep->a2; break;
...
case n: val = ep->an; break;
}
return val;
}
But I suspect that there is a much simpler solution. Something like array accessing style, maybe.
I tried to do something like that:
#define getval(s,n) s.a##n
But expectedly it doesn't work.
Is there a nicer solution?
Unless you have specific knowledge of the underlying structure of the struct, there is no way to implement such a method in C. There are all sorts of problems that will get in the way including
Members of different sizes
Packing issues
Alignment issues
Tricks like bitfields will be problematic
You're best off implementing a method by hand for your struct which has a deep understanding of the internal members of the structure.
If every field in your struct is an int, then you should basically be able to say
int getval(struct Element *ep, int n)
{
return *(((int*)ep) + n);
}
This casts the pointer to your struct to a pointer to an array if integers, then accesses the nth element of that array. Since everything in your struct seems to be an integer, this is perfectly valid. Note that this will fail horribly if you ever have a non-int member.
A more general solution would be to maintain an array of field offsets:
int offsets[3];
void initOffsets()
{
struct Element e;
offsets[0] = (int)&e.x - (int)&e;
offsets[1] = (int)&e.y - (int)&e;
offsets[2] = (int)&e.z - (int)&e;
}
int getval(struct Element *ep, int n)
{
return *((int*)((int)ep+offsets[n]));
}
This will work in the sense that you'll be able to call getval for any of the int fields of your struct, even if you have other non-int fields in your struct, since the offsets will all be correct. However, if you tried to call getval on one of the non-int fields it would return a completely wrong value.
Of course, you could write a different function for each data type, e.g.
double getDoubleVal(struct Element *ep, int n)
{
return *((double*)((int)ep+offsets[n]));
}
and then just call the proper function for whichever datatype you'd want. Incidentally, if you were using C++ you could say something like
template<typename T>
T getval(struct Element *ep, int n)
{
return *((T*)((int)ep+offsets[n]));
}
and then it would work for whatever datatype you'd want.
If your struct was anything except bitfields, you could just use array access, if I'm right in remembering that C guarantees that a series of members of a struct all of the same type, has the same layout as an array. If you know which bits in what order your compiler stores bitfields into integer types, then you could use shift/mask ops, but that's then implementation-dependent.
If you want to access bits by variable index, then it's probably best to replace your bitfields with an integer containing flag bits. Access by variable really isn't what bitfields are for: a1 ... an are basically independent members, not an array of bits.
You could do something like this:
struct Element {
unsigned int a1 : 1;
unsigned int a2 : 1;
...
unsigned int an : 1;
};
typedef unsigned int (*get_fn)(const struct Element*);
#define DEFINE_GETTER(ARG) \
unsigned int getter_##ARG (const struct Element *ep) { \
return ep-> a##ARG ; \
}
DEFINE_GETTER(1);
DEFINE_GETTER(2);
...
DEFINE_GETTER(N);
get_fn jump_table[n] = { getter_1, getter_2, ... getter_n};
int getval(struct Element *ep, int n) {
return jump_table[n-1](ep);
}
And some of the repetition could be avoided by the trick where you include the same header multiple times, each time having defined a macro differently. The header expands that macro once for each 1 ... N.
But I'm not convinced it's worth it.
It does deal with JaredPar's point that you're in trouble if your struct mixes different types - here all the members accessed via a particular jump table must of course be of the same type, but they can have any old rubbish in between them. That still leaves the rest of JaredPar's points, though, and this is a lot of code bloat for really no benefit compared with the switch.
No, there is no simple way to do this easier. Especially for bitfields, that are hard to access indirectly through pointers (you cannot take the address of a bitfield).
You can of course simplify that function to something like this:
int getval(const struct Element *ep, int n)
{
switch(n)
{
case 1: return ep->a1;
case 2: return ep->a2;
/* And so on ... */
}
return -1; /* Indicates illegal field index. */
}
And it seems obvious how the implementation can be further simplified by using a preprocessor macro that expands to the case-line, but that's just sugar.
If the structure really is as simple as described, you might use a union with an array (or a cast to an array) and some bit-access magic (as in How do you set, clear and toggle a single bit in C?).
As Jared says, the general case is hard.
I think your real solution is to not use bitfields in your struct, but instead define either a set type or a bit array.
I suggest code generation. If your structures don't contain huge amount of fields you can auto generate routines for each field or for a range of fields
and use them like:
val = getfield_aN( myobject, n );
or
val = getfield_foo( myobject );
If you have
Only bitfields, or all the bitfields first in your struct
less than 32 (or 64) bitfields
then this solution is for you.
#include <stdio.h>
#include <stdint.h>
struct Element {
unsigned int a1 : 1;
unsigned int a2 : 1;
unsigned int a3 : 1;
unsigned int a4 : 1;
};
#define ELEMENT_COUNT 4 /* the number of bit fields in the struct */
/* returns the bit at position N, or -1 on error (n out of bounds) */
int getval(struct Element* ep, int n)
{
if(n > ELEMENT_COUNT || n < 1)
return -1;
/* this union makes it possible to access bit fields at the beginning of
the struct Element as if they were a number.
*/
union {
struct Element el;
uint32_t bits;
} comb;
comb.el = *ep;
/* check if nth bit is set */
if(comb.bits & (1<<(n-1))) {
return 1;
} else {
return 0;
}
}
int main(int argc, char** argv)
{
int i;
struct Element el;
el.a1 = 0;
el.a2 = 1;
el.a3 = 1;
el.a4 = 0;
for(i = 1; i <= ELEMENT_COUNT; ++i) {
printf("el.a%d = %d\n", i, getval(&el, i));
}
printf("el.a%d = %d\n", 8, getval(&el, 8));
return 0;
}
Based on eli-courtwright solution but without using array of field offsets
......
if you have a structure containing pointer field like this, maybe you could write:
struct int_pointers
{
int *ptr1;
int *ptr2;
long *ptr3;
double *ptr4;
std::string * strDescrPtr;
};
Then you know that every pointer has a 4 bytes offset from a pointer to the structure, so you can write:
struct int_pointers ptrs;
int i1 = 154;
int i2 = -97;
long i3 = 100000;
double i4 = (double)i1/i2;
std::string strDescr = "sample-string";
ptrs.ptr1 = &i1;
ptrs.ptr2 = &i2;
ptrs.ptr3 = &i3;
ptrs.ptr4 = &i4;
ptrs.strDescrPtr = &strDescr;
then, for example, for a int value you can write:
int GetIntVal (struct int_pointers *ep, int intByteOffset)
{
int * intValuePtr = (int *)(*(int*)((int)ep + intByteOffset));
return *intValuePtr;
}
Calling it by:
int intResult = GetIntVal(&ptrs,0) //to retrieve the first int value in ptrs structure variable
int intResult = GetIntVal(&ptrs,4) //to retrieve the second int value in ptrs structure variable
and so on for the others structure fields values (writing other specific functions and using correct bytes offset value (multiple of 4)).
Although the OP specifies that we shouldn't care about the contents of the struct, since they are just bitfields would it be possible to use a char or int (or whatever data type has the size required) to create an n-bit "array" in this case?
void writebit(char *array, int n)
{
char mask = (1 << n);
*array = *array & mask;
}
with the char types replaced with a larger type if a longer "array" was needed. Not sure this is a definitive solution in other structs but it should work here, with a similar readbit funcition.
If you want to access your structure using both element index:
int getval(struct Element *ep, int n)
and by name:
ep->a1
then you are stuck with some hard to maintain switch like method that everyone has suggested.
If, however, all you want to do is access by index and never by name, then you can be a bit more creative.
First off, define a field type:
typedef struct _FieldType
{
int size_in_bits;
} FieldType;
and then create a structure definition:
FieldType structure_def [] = { {1}, {1}, {1}, {4}, {1}, {0} };
The above defines a structure with five elements of size 1, 1, 1, 4 and 1 bits. The final {0} marks the end of the definition.
Now create an element type:
typedef struct _Element
{
FieldType *fields;
} Element;
To create an instance of an Element:
Element *CreateElement (FieldType *field_defs)
{
/* calculate number of bits defined by field_defs */
int size = ?;
/* allocate memory */
Element *element = malloc (sizeof (Element) + (size + 7) / 8); /* replace 7 and 8 with bits per char */
element->fields = field_defs;
return element;
}
And then to access an element:
int GetValue (Element *element, int field)
{
/* get number of bits in fields 0..(field - 1) */
int bit_offset = ?;
/* get char offset */
int byte_offset = sizeof (Element) + bit_offset / 8;
/* get pointer to byte containing start of data */
char *ptr = ((char *) element) + byte_offset;
/* extract bits of interest */
int value = ?;
return value;
}
Setting values is similar to getting values, only the final part needs changing.
You can enhance the above by extending the FieldType structure to include information about the type of value stored: char, int, float, etc, and then write accessors for each type which checks the required type against the defined type.
Why not build getval() in to the struct?
struct Whang {
int a1;
int a2;
int getIth(int i) {
int rval;
switch (i) {
case 1: rval = a1; break;
case 2: rval = a2; break;
default : rval = -1; break;
}
return rval;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Whang w;
w.a1 = 1;
w.a2 = 200;
int r = w.getIth(1);
r = w.getIth(2);
return 0;
}
getIth() would have knowledge of the internals of Whang, and could deal with whatever it contained.