views:

349

answers:

7

My basic problem is that I want to use some structs and functions defined in a header file by not including that header file in my code.

The header file is generated by a tool. Since I don't have access to the header file, I can't include it in my program.

Here's a simple example of my scenario:

first.h

#ifndef FIRST_H_GUARD
#define FIRST_H_GUARD
typedef struct ComplexS {
   float real;
   float imag;
} Complex;

Complex add(Complex a, Complex b);

// Other structs and functions
#endif

first.c

#include "first.h"

Complex add(Complex a, Complex b) {
   Complex res;
   res.real = a.real + b.real;
   res.imag = a.imag + b.imag;
   return res;
}

my_program.c

// I cannot/do not want to include the first.h header file here
// but I want to use the structs and functions from the first.h
#include <stdio.h>

int main() {
   Complex a; a.real = 3; a.imag = 4;
   Complex b; b.real = 6; b.imag = 2;

   Complex c = add(a, b);
   printf("Result (%4.2f, %4.2f)\n", c.real, c.imag);

   return 0;
}

My intention is to build an object file for my_program and then use the linker to link up the object files into an executable. Is what I want to achieve possible in C?

+8  A: 

In order to use the struct in my_program.c, the struct has to be defined in my_program.c. There's no way around it.

In order to define it, you have to either include first.h or provide a definition of Complex in my_program.c in some other way (like copy-paste the definition of Complex into my_program.c).

If your first.h looks as you posted, then there's no point in doing any copy-pasting, of course, since it is going to be the same thing anyway. Just include your first.h.

If you don't want to include first.h because of something else in that header (which you don't show here), you can move the definition of Complex into a separate small header, and include it instead in both places.

AndreyT
The problem is the struct I want to use include has other structs as fields defined in the same header file (and possibly elsewhere) and this chain continues a long way.My main goal is to be able to invoke functions (also defined in the same header file) on those structs.
shams
@shams: You *need* the definition of those structs and functions. The compiler has no clue what to do otherwise. For functions, the compiler can guess, but generally it guesses badly and leads to broken code.
Yann Ramin
If your functions take pointers to structures, you can split them into a separate header and forward-declare the structures there (typedef struct ComplexS Complex;) . You won't be able to look at the contents of the structures in your main(), but at least you'll be able to call the functions.
Arkadiy
@Arkadiy I'll give this a try
shams
+1  A: 

if you know how data are put (and you know it since you showed it to us), you can replicate them in your file! If you want not to #include, copy-paste! If it is the result of reverse-engineering on data or something, you need anyway a struct made with your guesses to let the compiler access the data properly.

On the other hand, if you do not know at all how data are stored in the structure, there's no way the compiler can know it for you.

ShinTakezou
A: 

If you want to use the structure, you have to define it somewhere. Otherwise, the compiler doesn't have enough information to build the program (it doesn't even know how much memory a Complex object requires). It can't figure out what the data type looks like merely by your usage of the struct.

However, if you know the size of the structure you can still use it, although in a limited and potentially hazardous way. For example, you can include the following definition in your C file:

typedef char[2*sizeof(float)] Complex;

This would allow you to use the Complex data type in a basic way (essentially, treating it as a chunk of raw data). You would not be able to access the structure members normally, but you can pass Complex objects (or Complex* pointers) between functions, read/write them to a file, and compare them to each other. If you are brave, you can access the data inside the structure by using char pointers and referencing individual bytes. Be careful doing this, because it requires intimate knowledge of how your particular compiler will lay out the structure in memory, including any alignment/padding bytes. Here's an example of accessing internal members by byte offset, assuming the structure is "packed" (no padding):

typedef char[2*sizeof(float)] Complex;

Complex add(Complex a, Complex b) {
  Complex res;
  float real, imag;
  real = ((float*)&a)[0] + ((float*)&b)[0];
  imag = ((float*)&a)[1] + ((float*)&b)[1];
  ((float*)&res)[0] = real;
  ((float*)&res)[1] = imag;
  return res;
}

This code should give you the same result as the code you posted, as long as the definition of Complex does not change from your sample code. This is incredibly risky to do, and if you decide to do this then don't mention my name please.

This technique only works if you know the exact size of the "real" Complex structure at compile time, and only allows crude access to the structure. Any code that tries to use it with dot-notation (as in your function in first.c) will throw a compiler error. To access the struct's internals with dot-notation, you need a full definition of the structure.

If first.c is going to be compiled into a library that your code will link against, then you can make the following modifications to allow you to use the add function without having to use first.h:

/* In start.c */
Complex add(Complex* a, Complex* b) {
  Complex res;
  res.real = a->real + b->real;
  res.imag = a->imag + b->imag;
  return res;
}

/* In your code */
typedef char[2*sizeof(float)] Complex;
Complex add(Complex* a, Complex* b);

Now, you should be able to create objects of type Complexand pass them back and forth to the add function without having to know anything about the internals of the structure (other than the size).

bta
I don't want to re-implement add, I want to be able to invoke the existing implementation in first.c. Probably changing the signature for add() to use pointers instead is feasible.
shams
@shams- You want to use the implementation in first.c? Is this compiled into a library first? If so, you can change `add` to use pointers and be okay, but if you are planning on compiling first.c into your program it won't work (both because first.c includes first.h and because `add` accesses the members of `Complex` using dot-notation).
bta
+1  A: 

You can wrap all access to the struct members with functions in first.c, forward declare the struct (struct ComplexS) in first.c and my_program.c (or a common header), and access the struct only via pointer in my_program.c (all the functions in first.c would operate on struct pointers).

Then your program only has to know the forward declaration, not the structure members.

When you are finished, my_program.c might read:

struct ComplexS;
typedef struct ComplexS Complex;

int main() {
   Complex *a = new_complex(3,4);
   Complex *b = new_complex(6,2);
   Complex *c = add_complex(a, b);
   printf("Result (%4.2f, %4.2f)\n", get_real(c), get_imag(c));

   return 0;
}
Randy Proctor
thanks I'm going to try this and post what I get to soon.
shams
A: 

DISCLAIMER: i would never write code this way IRL.

Ok, here is the hack I was talking about. Just don't use it in any code that is going to be remotely important, as it's not guaranteed to always work, but you'll see the idea of how memory is organized after looking at it. I encourage compiling them to assembly with gcc -S and taking a look at that as well.

root@brian:~# cat struct-hack.c
#include <stdio.h>

struct hack {
        int a, b;
};
int main()
{
        struct hack myhack = { 0xDEAD, 0xBEEF };
        FILE *out = fopen("./out", "w");
        fwrite(&myhack, sizeof(struct hack), 1, out);
        fclose(out);
        return 0;
}
root@brian:~# cat struct-read.c
#include <stdio.h>
#include <stdlib.h>

// in bytes.
#define STRUCT_ADDRESS_MODE     int
#define STRUCT_SIZE     8


int main()
{
        /** THIS IS BAD CODE. DO NOT USE IN ANY REMOTELY SERIOUS PROGRAM. **/

        // Open file
        FILE *in = fopen("./out", "r");
        if(!in) exit(1);

        // We need a way of addressing the structure, an Int is convenient because we
        // know the structure contains a couple ints.
        STRUCT_ADDRESS_MODE *hacked_struct = malloc(STRUCT_SIZE);
        if(!hacked_struct) exit(1);

        fread(hacked_struct, STRUCT_SIZE, 1, in);

        printf ("%x, %x\n", hacked_struct[0], hacked_struct[1]);
        free(hacked_struct);
        fclose(in);
        return 0;
}

root@brian:~# ./struct-hack
root@brian:~# hexdump  -C out
00000000  ad de 00 00 ef be 00 00                           |........|
00000008
root@brian:~# ./struct-read
dead, beef
Tom Dignan
I'd like to add that this program is a complete abuse of C, only use it if you're feeling really vein.
Tom Dignan
Thanks a lot Tom, as you suggest I probably won't end up using this. It helped though understand how structs are translated into in memory.
shams
A: 

In first.h/first.c declare and define add based on Complex*. Also declare initialize and print routines based on Complex* in my_program.c -- the idea here is do not use Complex as a type directly in your code if you don't want to include the header or type declaration explicitly.

So here's your new main:

#include <stdio.h>

struct Complex; // forward declaration
Complex* initialize(int, int); 
Complex* add(Complex*, Complex*); 
void print_complex(Complex*);

int main() {
   Complex* a = initialize(3, 4); // create new complex no 3 + 4i
   Complex* b = initialize(6, 2);

   Complex* c = add(a, b); // redefined add
   print_complex(c);

   return 0;
}
Fanatic23
thanks, I'll try this and post where I get to soon
shams
+1  A: 

I modified the files to use pointers and forward references and got it to work.

I'm now going to inspect the generated header file to see if I need to use any function that does not accept pointers as arguments.

Here's the code I ended up trying:

first.h

#ifndef FIRST_H_GUARD
#define FIRST_H_GUARD
typedef struct ComplexS {
   float real;
   float imag;
} Complex;

Complex* new_complex(float a, float b);
Complex* add(Complex* a, Complex* b);
void print_complex(Complex* a);
#endif

first.c

#include <stdio.h>
#include <stdlib.h>
#include "first.h"

Complex* new_complex(float a, float b) {
   Complex* temp = (Complex*)malloc(sizeof(Complex));
   temp->real = a;
   temp->imag = b;
   return temp;
}

Complex* add(Complex* a, Complex* b) {
   Complex *res = new_complex(a->real + b->real, a->imag + b->imag);
   return res;
}

void print_complex(Complex* a) {
   printf("Complex(%4.2f, %4.2f)\n", a->real, a->imag);
}

second.c

#include <stdio.h>

struct ComplexS; // forward declaration
typedef struct ComplexS Complex; 

Complex* new_complex(float a, float b); 
Complex* add(Complex* a, Complex* b); 
void print_complex(Complex* a);

int main() {
   Complex* a = new_complex(3, 4);
   Complex* b = new_complex(6, 2);

   Complex* c = add(a, b);
   print_complex(c);

   return 0;
}

output:

Complex(9.00, 6.00)
shams