1 struct with 2 different definitions (or dynamic structs) - c

I'm implementing a little program in C which uses a shared library called "libhelper.so".
The "libhelper.so" defines a struct in it's h-file, but sadly depending on the target-system those definitions are different (libhelper.so is always provided by the system and not by myself):
System A:
struct theStruct {
int fd;
unsigned int flags;
struct config config; // only in System A
int foo; // in both systems
int bar; // only in System A
};
System B:
struct theStruct {
int fd;
unsigned int flags;
int foo; // in both systems
int foobar; // only in system B
};
In my program, I thought I just define that struct by myself like this:
struct theStruct {
int fd;
unsigned int flags;
struct config config; // only in System A
int foo; // in both systems
int foobar; // only in system B
int bar; // only in System A
};
As the result of a call to a function in "libhelper.so" I got an instance of "theStruct" and now I though I can just check if "theStructInstance->bar" or "theStructInstance->foobar" is filled with a valid value to detect which implementation has been used by the library.
But it seems that I get just values like 1...6 which looks like the position of the field in the struct.
Has anyone an idea how I can do this?

No, this won't work.
First of all, all the definitions of the structure must be the same, otherwise you get the infamous Undefined Behaviour.
Second, look at the memory layout. What should be the offset to bar from the beginning of the structure? The first and the third definitions don't agree on this (the fields are most likely to be located consecutively).
Maybe you can try a union?
struct theStruct {
int fd;
unsigned int flags;
struct config config;
int foo; // in both systems
union {
int bar; // only in System A
int foobar; // only in system B
};
};
If you choose this, you should use only bar on System A and only foobar on System B.
If the two systems are incompatible, and the actual type needed for bar is not available on System B (and vice versa), you can do with the following code:
struct theStruct {
int fd;
unsigned int flags;
struct config config;
int foo; // in both systems
#ifdef SYSTEM_A
int bar; // only in System A
#else
#ifdef SYSTEM_B
int foobar; // only in system B
#else
#pragma error(either SYSTEM_A or SYSTEM_B must be enabled)
#endif
#endif
};
This way you will be always working with either with code compiled for System A or for System B, so you'll need to have different executables (which seems to be unavoidable anyway if you are compiling for the systems so much different).
You'll need to wrap parts of your code accessing the fields into #ifdefs:
#ifdef SYSTEM_A
s.bar = 5;
#endif
-- otherwise you'll get compile errors on System B.

Another possible solution would be to write platform dependent code to handle each of the individual structs, and then load their data into a common struct. This would then allow you to deal with the same members of the struct, no matter the code path, rather than always referencing one of the two union members based on the system:
struct mystruct;
mystruct.member1 = theStruct.member1; //the common part of the struct
mystruct.member2 = theStruct.member2;
#ifdef platform1
mystruct.member3 = theStruct.p1member; //specific to platform1
mystruct.member4 = -1;
#else
mystruct.member3 = -1;
mystruct.member4 = theStruct.p2member; //specific to platform2
#endif

Here is an approach to think about.
A bit of background about the assumptions I am making first.
It sounds like you have some function libraryFunction () that returns a pointer to a struct theStruct. However the actual layout of the struct theStruct depends on the particular system on which your application is running. In this struct is some information that you need to access. You do not specify the calling arguments or signature of the library function and if the pointer to the struct theStruct is returned as a function value or a pointer to a pointer is part of the argument list. I will assume it is a function return value.
Create a struct you define for the information that you want. Create two files each of which has a function that takes a void pointer and a pointer to your new struct and then fills in your struct with the data you want from the library supplied struct. Each of these two files will be compiled with a particular system target specified (SystemA or SystemB) so that your conversion function will interpret the struct provided by the library function depending on the target system and fill in your struct with the data you want.
file 1 for System A
// copy of the struct used in System A which is in the library header file
// put here for reference only as should be in the header file
struct theStruct {
int fd;
unsigned int flags;
struct config config; // only in System A
int foo; // in both systems
int bar; // only in System A
};
// my struct that contains the data from struct theStruct that I want
// would be in a header file included into each of these files but here for reference
struct myConvertStruct {
int foo;
};
void convert2SystemA (void *structPtr, struct *myStruct)
{
myStruct->foo = ((struct theStruct *)structPtr)->foo;
}
file 2 for System B
// copy of the struct used in System B which is in the library header file
// put here for reference only as should be in the header file
struct theStruct {
int fd;
unsigned int flags;
int foo; // in both systems
int foobar; // only in system B
};
// my struct that contains the data from struct theStruct that I want
// would be in a header file included into each of these files but here for reference
struct myConvertStruct {
int foo;
};
void convert2SystemB (void *structPtr, struct *myStruct)
{
myStruct->foo = ((struct theStruct *)structPtr)->foo;
}
file 3 using the conversion functions
// my struct that contains the data from struct theStruct that I want
// would be in a header file included into each of these files but here for reference
struct myConvertStruct {
int foo;
};
{
struct myConvertStruct myStruct;
// some function body and now we come to the library call
if (mySystem == SystemA) {
void *pStruct = libraryFunction (......);
convert2SystemA (pStruct, &myStruct);
} else if (mySystem == SystemB) {
void *pStruct = libraryFunction (......);
convert2SystemB (pStruct, &myStruct);
} else {
// some error conditions
}
// now use the data that you have pulled as you want to use it
}

The reason why your proposal won't work is that the offset to the foo member is different for System A and System B. You say that you can only figure out what System you are using at runtime. So, when System B sets foo, it will likely end up setting something inside of config instead.
enum system { SystemUnknown, SystemA, SystemB };
struct theStructSystemA {
int fd;
unsigned int flags;
struct config config; // only in System A
int foo; // in both systems
int bar; // only in System A
};
struct theStructSystemB {
int fd;
unsigned int flags;
int foo;
int foobar;
};
struct myStruct {
union {
struct theStructSystemA a;
struct theStructSystemB b;
} u;
enum system sys;
};
struct myStruct s = { 0 };
Now, you can set bar to some invalid value: s.u.a.bar = -1, for example. Now, when you call into your library, you can check:
s.u.a.bar = -1;
some_libhelper_call((void *)&s);
if (s.u.a.bar != -1) s.sys = SystemA;
else s.sys = SystemB;
So now, after s.sys is known, you can switch to a different code path that deals entirely with a known system version.

Related

What is the intended usage of DEVICE_INT_ATTR?

I would like to expose some settings of my device via sysfs. If I understand it right, a driver can have multiple devices, so there should be one instance of the settings variable per device. This should be easy enough using DEVICE_ATTR macro.
Checking the sources I noticed there is also DEVICE_INT_ATTR and other with different type. I wonder what is the intended usage, as they use device_show_int functions that get pointer to device, but don't actually use it:
ssize_t device_store_int(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct dev_ext_attribute *ea = to_ext_attr(attr);
int ret;
long new;
ret = kstrtol(buf, 0, &new);
if (ret)
return ret;
if (new > INT_MAX || new < INT_MIN)
return -EINVAL;
*(int *)(ea->var) = new;
/* Always return full write size even if we didn't consume all */
return size;
}
EXPORT_SYMBOL_GPL(device_store_int);
I searched kernel sources for those macros, and it seems that they work with a global variable. For example DEVICE_INT_ATTR is used in drivers/base/core.c for mca_cfg.tolerant:
static DEVICE_INT_ATTR(tolerant, 0644, mca_cfg.tolerant);
but the mca_cfg varaible is actually global, not tied to a device:
struct mca_config mca_cfg __read_mostly = {
.bootlog = -1,
/* ... */
.tolerant = 1,
.monarch_timeout = -1
};
which makes it look like a driver (not device) attribute.
I also checked commit that adds these macros but it did not help me much.
You correctly take that DEVICE_INT_ATTR and other macros from that family are for "global" attributes, which store and show methods doesn't use dev parameter.
If you want to define attribute, which can be bound to several devices, then you could write your own store and show methods which gets information about the value from dev.
E.g. by having device
struct my_device
{
struct device base;
int repetition;
};
you could expose its repetition field in the attribute using following show method:
// Shows value of 'repetition' field of my_device.
static ssize_t repetition_show(struct device *dev, struct device_attribute *attr, char *buf)
{
// Obtain pointer to the real device structure.
struct my_device* my_dev = container_of(dev, struct my_device, base);
return sprintf(buf, "%d\n", my_dev->repetition);
}
Structure of such attribute could be initialized using __ATTR macro:
static struct device_attribute repetition_attr =
__ATTR(repetition, S_IRUGO, repetition_show, NULL);
Making "generic" attributes
Assume your device struct contains many int fields, which you want to expose via attributes:
struct my_device
{
struct device base;
int repetition;
int counter;
int value;
};
In that case you could generalize attribute definition, so you don't need to create many show (and store) functions.
E.g. you could store offset of the exposed field in your attribute structure:
struct device_bounded_attr
{
struct device_attribute base_attr;
size_t field_offset;
};
// Initializer for struct device_bounded_attr
//
// - real_device_type - type of the actual device structure
// - device_member - member of type 'struct device' in the actual device structure
// - field_member - member in actual device structure which you want to expose as attribute.
#define BOUNDED_ATTR(name, mode, show, store, real_device_type, device_member, field_member) { \
.base_attr = __ATTR(name, mode, show, store), \
.field_offset = offsetof(real_device_type, field_member) - offsetof(real_device_type, device_member)
}
Using this field, you could rewrite show method as follows:
// Shows value of integer field, stored in device.
static ssize_t bounded_attr_show(struct device *dev, struct device_attribute *attr, char *buf)
{
// Obtain pointer to the real attribute structure.
struct device_bounded_attr* bounded_attr = container_of(attr, struct device_bounded_attr, base_attr);
// Having offset of the field, calculate pointer to it
int field_ptr* = (int*)(((char*)dev) + bounded_attr->field_offset);
return sprintf(buf, "%d\n", *field_ptr);
}
So attributes can be declared as follows:
static struct device_bounded_attr repetition_attr =
BOUNDED_ATTR(repetition, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, repetition);
static struct device_bounded_attr counter_attr =
BOUNDED_ATTR(counter, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, counter);
static struct device_bounded_attr value_attr =
BOUNDED_ATTR(counter, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, value);

Trouble Including Externally Declared Enumeration - C Code

Update: The issue is resolved. Here is code that compiles properly.
---instruction.h---
#ifndef INSTRUCTION_H
#define INSTRUCTION_H
typedef enum OPCODE {ADD = 0x20,ADDI = 0x8,SUB = 0x22,MULT = 0x18,BEQ = 0x4,LW = 0x23,SW = 0x2B} opcode;
/*opcode is OPCODEs alias*/
typedef struct INSTRUCTION {
opcode op;
int rs;
int rt;
int rd;
int Imm;
} inst;
/*inst is INSTRUCTIONs alias*/
#endif // INSTRUCTION_H
---parser.c---
#include <stdio.h>
#include "instruction.h"
void parser(char *instruction)
{
/*Parse character string into instruction components*/
inst set1 = {LW,0,1,2,0};
printf("parsing");
};
int main()
{
char *instruction;
instruction = NULL;
parser(instruction);
};
/*pass in pointer for instruction being passed in*/
/*pointing to address of instruction being passed in*/
/*Parser return type is struct inst*/
I cannot seem to get my enumeration type "opcode" to be recognized in my main c file. I included the header file. I am fairly new to C, so haven't made much ground on the issue for some time now and wanted to see if anyone knew why I was getting the error messages below. My guess is the linking the header file is not working properly. Any help is much appreciated.
---instruction.h----
#ifndef INSTRUCTION_H
#define INSTRUCTION_H
typedef enum {add = 32,addi = 8,sub = 34,mult = 24,beq = 4,lw = 35,sw = 43}opcode;
extern opcode oper;
typedef struct {
opcode op;
int rs;
int rt;
int rd;
int Imm;
}inst;
#endif // INSTRUCTION_H
---Parser.c---
#include <stdio.h>
#include "instruction.h"
void parser(char *inst)
{
/*Parse character string into instruction components*/
struct inst{lw,0,1,2,0};
};
int main()
{
char *instruction;
instruction = NULL;
parser(instruction);
};
struct inst{lw,0,1,2,0};
This looks like it's supposed to be a variable declaration, but I don't see a name for the variable. Try:
struct inst name_of_the_variable = {lw,0,1,2,0};
As a side note, enum values are global constants, so it's probably not a good idea to give them names like lw that can be confused for variables. Standard practice would be to use all-caps for the names and give them a prefix… say, OPCODE_ADD, OPCODE_LW, etc.
This is not a valid variable definition:
struct inst{lw,0,1,2,0};
There's no struct inst defined, only inst, there's no variable name, and you need = to use an initializer. To create a variable of this type an initialize it, you need:
inst myinst = {lw,0,1,2,0};
Also, your function has a parameter named inst which masks the type inst. You need to give it a different name:
void parser(char *instruction)

undefined symbol of a struct pointer

I'm working on xinu, and I need to change some *.c files.
I have this struct in file ready.c:
struct newtimer{
struct newtimer* tnext;
struct newtimer* tprev;
int tkey;
int tprio;
int tcount;
};
and then I declared:
struct newtimer *timer = NULL;
I did some use with the timer variable in this file and I need to use it in another file as well (clkint.c). So in clkint I did this:
extern struct newtimer *timer;
(which compiles alright)
but when i try to access timer's fields I get these errors:
What am I doing wrong?
Thank you
edit:
As requested, here is some of the clkint.c:
struct newtimer *t;
extern struct newtimer *timer;
...
t = timer;
while(t!= NULL)
{
++(t->tcount);
if(t->tcount >= 18){
t->tcount = 0;
newprior = proctab[t->tkey]->pprio + 10;
t->tcount = newprior;
chprio(t->tkey, newprior);
}
t = t->tnext;
resched();
}
edit:
Replacing all the ts with timers does not solve the problem.
Your struct newtimer type is not defined. You probaly forgot to include the header file that defines struct newtimer.
When you use an unknown struct name in struct something, C compiler treats that as a forward declaration of a completely new struct type. The type is, of course, incomplete, which is why you are not allowed to access any innards of that type. The compiler simply knows nothing about those innards.
Where is your struct newtimer defined? If it is defined in a header file, you have to include it into your clkint.c.

Create static instance of struct in C without knowing struct implementation

I have the following header file:
typedef struct my_data my_data_t;
my_data_t* new_my_data(void);
void free_my_data(my_data_t* my_data);
And the corresponding c file:
typedef struct my_data
{
int val;
} my_data_t;
my_data_t* new_my_data()
{
my_data_t* ptr = (my_data_t*)malloc(sizeof(my_data_t));
return ptr;
}
void free_my_data(my_data_t* my_data)
{
free(my_data);
}
I would like to create in instance of my_data_t in static memory without using malloc. I'd like to do this by adding the following to the header file:
int sizeof_my_data(void);
...and this to the c file:
int sizeof_my_data()
{
return sizeof(my_data_t);
}
...and doing something like this:
static my_data_t* my_data_instance = static_alloc(sizeof_my_data());
Is this possible? I'd like to do this because the target machine for this code is a microprocessor that doesn't handle malloc (in our case, it's best that all structures be held in static memory), and I'd like to keep the actual data structure of my_data hidden from the code that implements it. Any other suggestions on how to accomplish the goal are welcome. Thanks.
The usual way of doing this is to create an opaque struct in your header file with the correct size. For example:
#define MY_DATA_SIZE 32
struct my_data_t
{
char opaque_data[MY_DATA_SIZE];
};
This is how the various pthreads data types (such as pthread_mutex_t) are defined.
How about adding to your header file
my_data_t *get_static_data();
and to your c file
my_data_t *get_static_data()
{
static my_data_t static_my_data;
return &static_my_data;
}

Trouble with Unions in C program

I am working on a C program that uses a Union. The union definition is in FILE_A header file and looks like this...
// FILE_A.h****************************************************
xdata union
{
long position;
char bytes[4];
}CurrentPosition;
If I set the value of CurrentPosition.position in FILE_A.c and then call a function in FILE_B.c that uses the union, the data in the union is back to Zero. This is demonstrated below.
// FILE_A.c****************************************************
int main.c(void)
{
CurrentPosition.position = 12345;
SomeFunctionInFileB();
}
// FILE_B.c****************************************************
void SomeFunctionInFileB(void)
{
// After the following lines execute I see all zeros in the flash memory.
WriteByteToFlash(CurrentPosition.bytes[0];
WriteByteToFlash(CurrentPosition.bytes[1];
WriteByteToFlash(CurrentPosition.bytes[2];
WriteByteToFlash(CurrentPosition.bytes[3];
}
Now, If I pass a long to SomeFunctionInFileB(long temp) and then store it into CurrentPosition.bytes within that function, and finally call WriteBytesToFlash(CurrentPosition.bytes[n]... it works just fine.
It appears as though the CurrentPosition Union is not global. So I tried changing the union definition in the header file to include the extern keyword like this...
extern xdata union
{
long position;
char bytes[4];
}CurrentPosition;
and then putting this in the source (.c) file...
xdata union
{
long position;
char bytes[4];
}CurrentPosition;
but this causes a compile error that says:
C:\SiLabs\Optec Programs\AgosRot\MotionControl.c:76: error 91: extern definition for 'CurrentPosition' mismatches with declaration.
C:\SiLabs\Optec Programs\AgosRot\/MotionControl.h:48: error 177: previously defined here
So what am I doing wrong? How do I make the union global?
Is FILE_A.h really MotionControl.h? If so I think the fix is to define a union type in the header:
typedef
union xdata
{
long position;
char bytes[4];
} xdata;
And declare a global variable of that type elsewhere in a header file (maybe the same one):
extern xdata CurrentPosition; // in a header file
Finally define the global variable in a C file exactly once. Maybe in file_a.c:
xdata CurrentPosition;
Of course a better fix might be to pass the xdata variable you want to write out to flash to SomeFunctionInFileB() so you don't have to depend on a global variable, which are well known to be problematic when not very, very carefully used. And there seems to be no good reason to not pass the data as a parameter:
// in a header file
void SomeFunctionInFileB( xdata const* pPosition);
void SomeFunctionInFileB( xdata const* pPosition)
{
// After the following lines execute I see all zeros in the flash memory.
WriteByteToFlash(pPosition->bytes[0];
WriteByteToFlash(pPosition->bytes[1];
WriteByteToFlash(pPosition->bytes[2];
WriteByteToFlash(pPosition->bytes[3];
}
And call it like so:
int main.c(void)
{
CurrentPosition.position = 12345;
SomeFunctionInFileB( &CurrentPosition);
}
Ideally you need a typedef for the union and an extern declaration in FILE_A.h and the actual definition of the union in FILE_A.c.
-
// FILE_A.h
typedef union
{
long position;
char bytes[4];
} Position;
extern Position CurrentPosition; // declaration
-
// FILE_A.c
#include "FILE_A.h"
Position CurrentPosition; // definition
int main(void)
{
CurrentPosition.position = 12345;
SomeFunctionInFileB();
return 0;
}
-
// FILE_B.c
#include "FILE_A.h"
void SomeFunctionInFileB(void)
{
// now there will be valid data in the flash memory.
WriteByteToFlash(cp.bytes[0];
WriteByteToFlash(cp.bytes[1];
WriteByteToFlash(cp.bytes[2];
WriteByteToFlash(cp.bytes[3];
}
-
You haven't instantiated the union.
You need :
// FILE_A.c****************************************************
#include "File_a.h"
CurrentPosition cp;
int main(void)
{
cp.position = 12345;
SomeFunctionInFileB();
}
// FILE_B.c****************************************************
#include "File_a.h"
extern CurrentPosition cp;
void SomeFunctionInFileB(void)
{
// now there will be valid data in the flash memory.
WriteByteToFlash(cp.bytes[0];
WriteByteToFlash(cp.bytes[1];
WriteByteToFlash(cp.bytes[2];
WriteByteToFlash(cp.bytes[3];
}
If sizeof(long) is not 4, then endianess comes into play...
consider
union{
long position
char bytes[sizeof long];
}

Resources